Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
60de2bd
refactor: apply code review nitpick improvements
Nov 25, 2025
516a2a0
fix: remove ConfigurableTestAuthenticationHandler race condition
Nov 25, 2025
0e48824
docs: remove obsolete documentation files
Nov 25, 2025
e50f728
feat: add IDocumentsModuleApi for cross-module communication
Nov 25, 2025
e046895
wip: add comprehensive IBGE API stubs to WireMock + refactor IbgeApiI…
Nov 25, 2025
efdd1ef
feat: add IServiceCatalogsModuleApi for cross-module service validation
Nov 25, 2025
6302d16
fix: refactor IbgeApiIntegrationTests to use WireMock + fix stubs
Nov 25, 2025
916e4dd
test: remove Skip from 2 ServiceCatalogs integration tests after AUTH…
Nov 25, 2025
4940bc2
fix: enable IBGE fail-open with fallback to simple validation
Nov 25, 2025
97dc7ac
feat(providers): integrate IDocumentsModuleApi in provider activation
Nov 25, 2025
62e46fa
feat(providers): prepare ISearchModuleApi integration for provider in…
Nov 25, 2025
a6ad166
feat(search): implement IndexProviderAsync and RemoveProviderAsync in…
Nov 25, 2025
c63ad65
test: remove duplicate GeographicRestrictionFeatureFlagTests
Nov 25, 2025
65897e9
docs: update Sprint 1 progress and skipped tests analysis
Nov 25, 2025
8ca60ea
docs: comprehensive Module APIs documentation in architecture.md
Nov 25, 2025
234a20f
docs: Sprint 1 final summary and completion report
Nov 25, 2025
b77eb08
feat(providers): implement full provider data sync for search indexing
Nov 25, 2025
2cf5816
feat(providers): add ProviderServices many-to-many table
Nov 25, 2025
b355776
test(providers): add comprehensive unit tests for ProviderServices
Nov 25, 2025
bff2381
refactor: rename infrastructure/database/modules folders and update S…
Nov 25, 2025
3d8b469
fix: make SearchProviders schema migration conditional for TestContai…
Nov 25, 2025
4aa726a
fix: corrigir schema hardcoded 'search' para 'search_providers' em qu…
Nov 25, 2025
938c55d
fix: atualizar schema 'search' para 'search_providers' em SearchProvi…
Nov 25, 2025
b317f38
feat: implement cross-module event handlers and fix SearchProviders s…
Nov 26, 2025
ca4b2f9
test: skip CreateAndUpdateUser_ShouldMaintainConsistency in CI/CD
Nov 26, 2025
17b6e55
refactor: code review fixes - remove duplicates, fix schemas, regener…
Nov 26, 2025
5dad19a
chore: final code review fixes - documentation, security, domain events
Nov 26, 2025
ac601cb
fix: correct schema naming to meajudaai_* pattern across all modules
Nov 26, 2025
b98c132
refactor: code quality improvements from review feedback
Nov 26, 2025
59e6265
refactor: minor code quality improvements
Nov 26, 2025
025aee0
test: improve test quality with explicit assertions and Theory pattern
Nov 26, 2025
ee4d1b2
refactor: use IReadOnlyList in ModuleServiceValidationResultDto
Nov 26, 2025
3d7b31b
fix: resolve race condition in ConfigurableTestAuthenticationHandler
Nov 26, 2025
78e47cb
fix: consolidate schema naming to meajudaai_* convention across all m…
Nov 26, 2025
dfa7f10
perf: reduce WireMock timeout simulation from 30s to 5s
Nov 26, 2025
5221f44
refactor: implement code review feedback - Result Match pattern, repo…
Nov 26, 2025
51f7e0d
refactor: rename Mock* to LocalDevelopment* services and move to dedi…
Nov 26, 2025
512cd66
fix: allowUnauthenticated should return anonymous principal, not admin
Nov 26, 2025
780cae9
fix: address CodeRabbit review feedback
Nov 26, 2025
9bfff7c
refactor: implement CodeRabbit Round 2 feedback - null guards, XML do…
Nov 26, 2025
0be5389
refactor: apply minor CodeRabbit polish - constants and comments
Nov 26, 2025
c69c404
feat: implement remaining CodeRabbit suggestions - UUID v7, migration…
Nov 27, 2025
93592d7
refactor: implement CodeRabbit final round - schema standardization, …
Nov 27, 2025
e0b4db3
refactor: complete CodeRabbit final polish - ProductVersion stable, h…
Nov 27, 2025
63a4a2d
fix: resolve E2E test failures - authentication and category deactiva…
Nov 27, 2025
274b378
refactor: apply CodeRabbit feedback - schema naming, async cleanup, t…
Nov 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
225 changes: 221 additions & 4 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -1079,12 +1079,229 @@ public sealed record UserRegisteredIntegrationEvent(

## 🚦 Status Atual da Implementação

Atualmente, a arquitetura do projeto **define os padrões** para comunicação síncrona (`IModuleApi`) e assíncrona (`IIntegrationEvent`), mas **eles ainda não foram implementados para comunicação entre os módulos existentes**.
**Status**: ✅ **PARCIALMENTE IMPLEMENTADO** (Sprint 1 Dias 3-6, Nov 2025)

- **`IModuleApi`**: As interfaces e implementações estão definidas dentro de seus respectivos módulos, mas nenhum módulo está injetando ou consumindo a API de outro módulo.
- **`IIntegrationEvent`**: Os eventos estão definidos no projeto `Shared`, mas não há `Handlers` nos módulos para consumir esses eventos. Os módulos atualmente lidam apenas com `Domain Events` internos.
### Module APIs Implementados:

A sua percepção de que os módulos não se comunicam está correta. O próximo passo no desenvolvimento é implementar esses padrões para criar um sistema coeso.
#### 1. **IDocumentsModuleApi** ✅ COMPLETO
**Localização**: `src/Shared/Contracts/Modules/Documents/IDocumentsModuleApi.cs`
**Implementação**: `src/Modules/Documents/Application/ModuleApi/DocumentsModuleApi.cs`

**Métodos (7)**:
```csharp
Task<Result<ModuleDocumentDto?>> GetDocumentByIdAsync(Guid documentId, CancellationToken ct);
Task<Result<IReadOnlyList<ModuleDocumentDto>>> GetProviderDocumentsAsync(Guid providerId, CancellationToken ct);
Task<Result<ModuleDocumentStatusDto?>> GetDocumentStatusAsync(Guid documentId, CancellationToken ct);
Task<Result<bool>> HasVerifiedDocumentsAsync(Guid providerId, CancellationToken ct);
Task<Result<bool>> HasRequiredDocumentsAsync(Guid providerId, CancellationToken ct);
Task<Result<bool>> HasPendingDocumentsAsync(Guid providerId, CancellationToken ct);
Task<Result<bool>> HasRejectedDocumentsAsync(Guid providerId, CancellationToken ct);
```

**Usado por**:
- ✅ `ActivateProviderCommandHandler` (Providers) - valida documentos antes de ativação

**Exemplo de Uso**:
```csharp
// src/Modules/Providers/Application/Handlers/Commands/ActivateProviderCommandHandler.cs
public sealed class ActivateProviderCommandHandler(
IProviderRepository providerRepository,
IDocumentsModuleApi documentsModuleApi, // ✅ Injetado
ILogger<ActivateProviderCommandHandler> logger
) : ICommandHandler<ActivateProviderCommand, Result>
{
public async Task<Result> HandleAsync(ActivateProviderCommand command, CancellationToken ct)
{
// Validar documentos via Documents module
var hasRequiredResult = await documentsModuleApi.HasRequiredDocumentsAsync(command.ProviderId, ct);
if (!hasRequiredResult.Value)
return Result.Failure("Provider must have all required documents before activation");

var hasVerifiedResult = await documentsModuleApi.HasVerifiedDocumentsAsync(command.ProviderId, ct);
if (!hasVerifiedResult.Value)
return Result.Failure("Provider must have verified documents before activation");

var hasPendingResult = await documentsModuleApi.HasPendingDocumentsAsync(command.ProviderId, ct);
if (hasPendingResult.Value)
return Result.Failure("Provider cannot be activated while documents are pending verification");

var hasRejectedResult = await documentsModuleApi.HasRejectedDocumentsAsync(command.ProviderId, ct);
if (hasRejectedResult.Value)
return Result.Failure("Provider cannot be activated with rejected documents");

// Ativar provider
provider.Activate(command.ActivatedBy);
await providerRepository.UpdateAsync(provider, ct);
return Result.Success();
}
}
```

---

#### 2. **IServiceCatalogsModuleApi** ⏳ STUB IMPLEMENTADO
**Localização**: `src/Shared/Contracts/Modules/ServiceCatalogs/IServiceCatalogsModuleApi.cs`
**Implementação**: `src/Modules/ServiceCatalogs/Application/ModuleApi/ServiceCatalogsModuleApi.cs`

**Métodos (3)**:
```csharp
Task<Result<ServiceValidationResult>> ValidateServicesAsync(IReadOnlyCollection<Guid> serviceIds, CancellationToken ct);
Task<Result<ServiceInfoDto?>> GetServiceByIdAsync(Guid serviceId, CancellationToken ct);
Task<Result<List<ServiceInfoDto>>> GetServicesByCategoryAsync(Guid categoryId, CancellationToken ct);
```

**Status**: Stub implementado, aguarda integração com Provider entity (ProviderServices many-to-many table)

**TODO**:
- Criar tabela `ProviderServices` no módulo Providers
- Implementar validação de serviços ao associar provider

---

#### 3. **ISearchProvidersModuleApi** ✅ COMPLETO
**Localização**: `src/Shared/Contracts/Modules/SearchProviders/ISearchProvidersModuleApi.cs`
**Implementação**: `src/Modules/SearchProviders/Application/ModuleApi/SearchProvidersModuleApi.cs`

**Métodos (3)**:
```csharp
Task<Result<ModulePagedSearchResultDto>> SearchProvidersAsync(
double latitude, double longitude, double radiusInKm, Guid[]? serviceIds,
decimal? minRating, ESubscriptionTier[]? subscriptionTiers,
int pageNumber, int pageSize, CancellationToken ct);

Task<Result> IndexProviderAsync(Guid providerId, CancellationToken ct); // ✅ NOVO (Sprint 1)
Task<Result> RemoveProviderAsync(Guid providerId, CancellationToken ct); // ✅ NOVO (Sprint 1)
```

**Usado por**:
- ✅ `ProviderVerificationStatusUpdatedDomainEventHandler` (Providers) - indexa/remove providers em busca

**Exemplo de Uso**:
```csharp
// src/Modules/Providers/Infrastructure/Events/Handlers/ProviderVerificationStatusUpdatedDomainEventHandler.cs
public sealed class ProviderVerificationStatusUpdatedDomainEventHandler(
IMessageBus messageBus,
ProvidersDbContext context,
ISearchModuleApi searchModuleApi, // ✅ Injetado
ILogger<ProviderVerificationStatusUpdatedDomainEventHandler> logger
) : IEventHandler<ProviderVerificationStatusUpdatedDomainEvent>
{
public async Task HandleAsync(ProviderVerificationStatusUpdatedDomainEvent domainEvent, CancellationToken ct)
{
var provider = await context.Providers.FirstOrDefaultAsync(p => p.Id == domainEvent.AggregateId, ct);

// Integração com SearchProviders: indexar quando verificado
if (domainEvent.NewStatus == EVerificationStatus.Verified)
{
var indexResult = await searchModuleApi.IndexProviderAsync(provider.Id.Value, ct);
if (indexResult.IsFailure)
logger.LogError("Failed to index provider {ProviderId}: {Error}",
domainEvent.AggregateId, indexResult.Error);
}
// Remover do índice quando rejeitado/suspenso
else if (domainEvent.NewStatus == EVerificationStatus.Rejected ||
domainEvent.NewStatus == EVerificationStatus.Suspended)
{
var removeResult = await searchModuleApi.RemoveProviderAsync(provider.Id.Value, ct);
if (removeResult.IsFailure)
logger.LogError("Failed to remove provider {ProviderId}: {Error}",
domainEvent.AggregateId, removeResult.Error);
}

// Publicar integration event
var integrationEvent = domainEvent.ToIntegrationEvent(provider.UserId, provider.Name);
await messageBus.PublishAsync(integrationEvent, cancellationToken: ct);
}
}
```

---

#### 4. **ILocationModuleApi** ✅ JÁ EXISTIA
**Localização**: `src/Shared/Contracts/Modules/Locations/ILocationModuleApi.cs`
**Implementação**: `src/Modules/Locations/Application/ModuleApi/LocationModuleApi.cs`

**Métodos**: GetAddressFromCepAsync, ValidateCepAsync, GeocodeAddressAsync

**Status**: Pronto para uso, não utilizado ainda (baixa prioridade)

---

### Integration Events Implementados:

#### ProviderVerificationStatusUpdated
- **Publicado por**: `ProviderVerificationStatusUpdatedDomainEventHandler` (Providers)
- **Consumido por**: Nenhum módulo ainda (preparado para futura expansão)
- **Payload**: ProviderId, UserId, Name, OldStatus, NewStatus, UpdatedAt

---

### Padrão de Implementação (Resumo):

**1. Definir Interface em Shared/Contracts/Modules/[ModuleName]**
```csharp
public interface IDocumentsModuleApi : IModuleApi
{
Task<Result<bool>> HasVerifiedDocumentsAsync(Guid providerId, CancellationToken ct);
}
```

##### 2. Implementar em Module/Application/ModuleApi

```csharp
[ModuleApi("Documents", "1.0")]
public sealed class DocumentsModuleApi(IQueryDispatcher queryDispatcher) : IDocumentsModuleApi
{
public async Task<Result<bool>> HasVerifiedDocumentsAsync(Guid providerId, CancellationToken ct)
{
var query = new GetProviderDocumentsQuery(providerId);
var result = await queryDispatcher.QueryAsync<GetProviderDocumentsQuery, Result<List<DocumentDto>>>(query, ct);
return Result.Success(result.Value?.Any(d => d.Status == EDocumentStatus.Verified) ?? false);
}
}
```

##### 3. Registrar em DI (Module/Application/Extensions.cs)

```csharp
services.AddScoped<IDocumentsModuleApi, DocumentsModuleApi>();
```

##### 4. Injetar e Usar em Outro Módulo

```csharp
public sealed class ActivateProviderCommandHandler(
IDocumentsModuleApi documentsApi) // ✅ Cross-module dependency
{
public async Task<Result> HandleAsync(...)
{
var hasVerified = await documentsApi.HasVerifiedDocumentsAsync(providerId, ct);
if (!hasVerified.Value)
return Result.Failure("Documents not verified");
}
}
```

---

### Benefícios Alcançados:

✅ **Type-Safe**: Contratos bem definidos em Shared/Contracts
✅ **Testável**: Fácil mockar IModuleApi em unit tests
✅ **Desacoplado**: Módulos não conhecem implementação interna de outros
✅ **Versionado**: Atributo [ModuleApi] permite versionamento
✅ **Observável**: Logging integrado em todas as operações
✅ **Resiliente**: Result pattern para error handling consistente

---

### Próximos Passos (Sprint 2):

- [ ] Implementar full provider data sync (IndexProviderAsync com dados completos)
- [ ] Criar IProvidersModuleApi para SearchProviders consumir
- [ ] Implementar ProviderServices many-to-many table
- [ ] Integrar IServiceCatalogsModuleApi em Provider lifecycle
- [ ] Adicionar integration event handlers entre módulos

---

Expand Down
Loading
Loading