Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
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
5 changes: 5 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>

<!-- Configurações de qualidade de código -->
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
Expand Down Expand Up @@ -61,6 +62,10 @@
<NoWarn>$(NoWarn);CA2213</NoWarn> <!-- Disposable fields in test infrastructure -->
<NoWarn>$(NoWarn);CA1816</NoWarn> <!-- Dispose methods should call GC.SuppressFinalize -->

<!-- Assembly version warnings - temporary -->
<NoWarn>$(NoWarn);CA1016</NoWarn> <!-- Assembly version warnings -->
<NoWarn>$(NoWarn);S3904</NoWarn> <!-- Assembly version warnings from SonarAnalyzer -->

<!-- Code Analysis - String and Culture -->
<NoWarn>$(NoWarn);CA1304</NoWarn> <!-- String culture specification -->
<NoWarn>$(NoWarn);CA1305</NoWarn> <!-- IFormatProvider specification -->
Expand Down
33 changes: 30 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,12 +170,18 @@ MeAjudaAi/
│ ├── Bootstrapper/ # API service bootstrapper
│ │ └── MeAjudaAi.ApiService/ # Ponto de entrada da API
│ ├── Modules/ # Módulos de domínio
│ │ └── Users/ # Módulo de usuários
│ │ ├── Users/ # Módulo de usuários
│ │ │ ├── API/ # Endpoints e controllers
│ │ │ ├── Application/ # Use cases e handlers CQRS
│ │ │ ├── Domain/ # Entidades, value objects, eventos
│ │ │ ├── Infrastructure/ # Persistência e serviços externos
│ │ │ └── Tests/ # Testes do módulo
│ │ └── Providers/ # Módulo de prestadores
│ │ ├── API/ # Endpoints e controllers
│ │ ├── Application/ # Use cases e handlers CQRS
│ │ ├── Domain/ # Entidades, value objects, eventos
│ │ ├── Infrastructure/ # Persistência e serviços externos
│ │ └── Tests/ # Testes do módulo
│ │ ├── Infrastructure/ # Persistência e event handlers
│ │ └── Tests/ # Testes unitários e integração
│ └── Shared/ # Componentes compartilhados
│ └── MeAjudaAi.Shared/ # Abstrações e utilities
├── tests/ # Testes de integração
Expand All @@ -193,6 +199,12 @@ MeAjudaAi/
- **Features**: Registro, login, perfis, papéis (cliente, prestador, admin)
- **Integração**: Keycloak para autenticação OAuth2/OIDC

### 🏢 Módulo Providers
- **Domain**: Gestão de prestadores de serviços e verificação
- **Features**: Cadastro, perfis empresariais, documentos, qualificações, status de verificação
- **Eventos**: Sistema completo de eventos de domínio e integração para comunicação inter-modular
- **Arquitetura**: Clean Architecture com CQRS, DDD e event-driven design

### 🔮 Módulos Futuros
- **Services**: Catálogo de serviços e categorias
- **Bookings**: Agendamentos e reservas
Expand Down Expand Up @@ -251,6 +263,21 @@ dotnet test src/Modules/Users/Tests/
- **Value Objects**: Para conceitos de domínio imutáveis
- **Aggregates**: Para consistência transacional

#### Implementação de Eventos - Módulo Providers

O módulo Providers implementa um sistema completo de eventos para comunicação inter-modular:

**Domain Events:**
- `ProviderRegisteredDomainEvent` - Novo prestador cadastrado
- `ProviderDeletedDomainEvent` - Prestador removido do sistema
- `ProviderVerificationStatusUpdatedDomainEvent` - Status de verificação alterado
- `ProviderProfileUpdatedDomainEvent` - Perfil do prestador atualizado

**Integration Events:**
- Conversão automática via Domain Event Handlers
- Publicação em message bus para outros módulos
- Suporte completo a event sourcing e auditoria

### Estrutura de Commits

```bash
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,18 @@ private sealed class Counter
}
public async Task InvokeAsync(HttpContext context)
{
var currentOptions = options.CurrentValue;

// Bypass rate limiting if explicitly disabled
if (!currentOptions.General.Enabled)
{
await next(context);
return;
}

var clientIp = GetClientIpAddress(context);
var isAuthenticated = context.User.Identity?.IsAuthenticated == true;

var currentOptions = options.CurrentValue;

// Check IP whitelist first - bypass rate limiting if IP is whitelisted
if (currentOptions.General.EnableIpWhitelist &&
currentOptions.General.WhitelistedIps.Contains(clientIp))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ public class RoleLimits

public class GeneralSettings
{
public bool Enabled { get; set; } = true;
[Range(1, 86400)] public int WindowInSeconds { get; set; } = 60;
public bool EnableIpWhitelist { get; set; } = false;
public List<string> WhitelistedIps { get; set; } = [];
Expand Down
8 changes: 8 additions & 0 deletions src/Modules/Providers/API/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace MeAjudaAi.Modules.Providers.API;
Expand Down Expand Up @@ -54,6 +55,13 @@ private static void EnsureDatabaseMigrations(WebApplication app)
var context = scope.ServiceProvider.GetService<Infrastructure.Persistence.ProvidersDbContext>();
if (context == null) return;

// Em ambiente de teste E2E, pular migrações automáticas - elas são gerenciadas pelo TestContainer
if (app.Environment.IsEnvironment("Test") || app.Environment.IsEnvironment("Testing"))
{
return;
}

// Em produção, usar migrações normais
context.Database.Migrate();
}
catch (Exception ex)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace MeAjudaAi.Modules.Providers.Application.Handlers.Commands;
/// </summary>
/// <param name="providerRepository">Repositório para acesso aos dados</param>
/// <param name="logger">Logger estruturado</param>
internal sealed class AddDocumentCommandHandler(
public sealed class AddDocumentCommandHandler(
IProviderRepository providerRepository,
ILogger<AddDocumentCommandHandler> logger
) : ICommandHandler<AddDocumentCommand, Result<ProviderDto>>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace MeAjudaAi.Modules.Providers.Application.Handlers.Commands;
/// </summary>
/// <param name="providerRepository">Repositório para acesso aos dados</param>
/// <param name="logger">Logger estruturado</param>
internal sealed class AddQualificationCommandHandler(
public sealed class AddQualificationCommandHandler(
IProviderRepository providerRepository,
ILogger<AddQualificationCommandHandler> logger
) : ICommandHandler<AddQualificationCommand, Result<ProviderDto>>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ namespace MeAjudaAi.Modules.Providers.Application.Handlers.Commands;
/// </remarks>
/// <param name="providerRepository">Repositório para persistência de prestadores de serviços</param>
/// <param name="logger">Logger estruturado para auditoria e debugging</param>
internal sealed class CreateProviderCommandHandler(
public sealed class CreateProviderCommandHandler(
IProviderRepository providerRepository,
ILogger<CreateProviderCommandHandler> logger
) : ICommandHandler<CreateProviderCommand, Result<ProviderDto>>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace MeAjudaAi.Modules.Providers.Application.Handlers.Commands;
/// <param name="providerRepository">Repositório para acesso aos dados</param>
/// <param name="dateTimeProvider">Provedor de data/hora para auditoria</param>
/// <param name="logger">Logger estruturado</param>
internal sealed class DeleteProviderCommandHandler(
public sealed class DeleteProviderCommandHandler(
IProviderRepository providerRepository,
IDateTimeProvider dateTimeProvider,
ILogger<DeleteProviderCommandHandler> logger
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace MeAjudaAi.Modules.Providers.Application.Handlers.Commands;
/// </summary>
/// <param name="providerRepository">Repositório para acesso aos dados</param>
/// <param name="logger">Logger estruturado</param>
internal sealed class RemoveDocumentCommandHandler(
public sealed class RemoveDocumentCommandHandler(
IProviderRepository providerRepository,
ILogger<RemoveDocumentCommandHandler> logger
) : ICommandHandler<RemoveDocumentCommand, Result<ProviderDto>>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace MeAjudaAi.Modules.Providers.Application.Handlers.Commands;
/// </summary>
/// <param name="providerRepository">Repositório para acesso aos dados</param>
/// <param name="logger">Logger estruturado</param>
internal sealed class RemoveQualificationCommandHandler(
public sealed class RemoveQualificationCommandHandler(
IProviderRepository providerRepository,
ILogger<RemoveQualificationCommandHandler> logger
) : ICommandHandler<RemoveQualificationCommand, Result<ProviderDto>>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace MeAjudaAi.Modules.Providers.Application.Handlers.Commands;
/// <summary>
/// Handler responsável por processar comandos de atualização de perfil do prestador de serviços.
/// </summary>
internal sealed class UpdateProviderProfileCommandHandler(
public sealed class UpdateProviderProfileCommandHandler(
IProviderRepository providerRepository,
ILogger<UpdateProviderProfileCommandHandler> logger
) : ICommandHandler<UpdateProviderProfileCommand, Result<ProviderDto>>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace MeAjudaAi.Modules.Providers.Application.Handlers.Commands;
/// </summary>
/// <param name="providerRepository">Repositório para acesso aos dados</param>
/// <param name="logger">Logger estruturado</param>
internal sealed class UpdateVerificationStatusCommandHandler(
public sealed class UpdateVerificationStatusCommandHandler(
IProviderRepository providerRepository,
ILogger<UpdateVerificationStatusCommandHandler> logger
) : ICommandHandler<UpdateVerificationStatusCommand, Result<ProviderDto>>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ namespace MeAjudaAi.Modules.Providers.Application.Handlers.Queries;
/// Implementa a lógica de negócio para buscar prestadores utilizando número de documento,
/// integrando com o repositório de dados e aplicando as regras de mapeamento necessárias.
/// </remarks>
internal sealed class GetProviderByDocumentQueryHandler(
public sealed class GetProviderByDocumentQueryHandler(
IProviderRepository providerRepository,
ILogger<GetProviderByDocumentQueryHandler> logger)
: IQueryHandler<GetProviderByDocumentQuery, Result<ProviderDto?>>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace MeAjudaAi.Modules.Providers.Application.Handlers.Queries;
/// </summary>
/// <param name="providerRepository">Repositório para acesso aos dados</param>
/// <param name="logger">Logger estruturado</param>
internal sealed class GetProviderByIdQueryHandler(
public sealed class GetProviderByIdQueryHandler(
IProviderRepository providerRepository,
ILogger<GetProviderByIdQueryHandler> logger
) : IQueryHandler<GetProviderByIdQuery, Result<ProviderDto?>>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace MeAjudaAi.Modules.Providers.Application.Handlers.Queries;
/// </summary>
/// <param name="providerRepository">Repositório para acesso aos dados</param>
/// <param name="logger">Logger estruturado</param>
internal sealed class GetProviderByUserIdQueryHandler(
public sealed class GetProviderByUserIdQueryHandler(
IProviderRepository providerRepository,
ILogger<GetProviderByUserIdQueryHandler> logger
) : IQueryHandler<GetProviderByUserIdQuery, Result<ProviderDto?>>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace MeAjudaAi.Modules.Providers.Application.Handlers.Queries;
/// </summary>
/// <param name="providerRepository">Repositório para acesso aos dados</param>
/// <param name="logger">Logger estruturado</param>
internal sealed class GetProvidersByCityQueryHandler(
public sealed class GetProvidersByCityQueryHandler(
IProviderRepository providerRepository,
ILogger<GetProvidersByCityQueryHandler> logger
) : IQueryHandler<GetProvidersByCityQuery, Result<IReadOnlyList<ProviderDto>>>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

namespace MeAjudaAi.Modules.Providers.Application.Handlers.Queries;

internal sealed class GetProvidersByIdsQueryHandler(
public sealed class GetProvidersByIdsQueryHandler(
IProviderRepository providerRepository,
ILogger<GetProvidersByIdsQueryHandler> logger
) : IQueryHandler<GetProvidersByIdsQuery, Result<IReadOnlyList<ProviderDto>>>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

namespace MeAjudaAi.Modules.Providers.Application.Handlers.Queries;

internal sealed class GetProvidersByStateQueryHandler(
public sealed class GetProvidersByStateQueryHandler(
IProviderRepository providerRepository,
ILogger<GetProvidersByStateQueryHandler> logger
) : IQueryHandler<GetProvidersByStateQuery, Result<IReadOnlyList<ProviderDto>>>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

namespace MeAjudaAi.Modules.Providers.Application.Handlers.Queries;

internal sealed class GetProvidersByTypeQueryHandler(
public sealed class GetProvidersByTypeQueryHandler(
IProviderRepository providerRepository,
ILogger<GetProvidersByTypeQueryHandler> logger
) : IQueryHandler<GetProvidersByTypeQuery, Result<IReadOnlyList<ProviderDto>>>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

namespace MeAjudaAi.Modules.Providers.Application.Handlers.Queries;

internal sealed class GetProvidersByVerificationStatusQueryHandler(
public sealed class GetProvidersByVerificationStatusQueryHandler(
IProviderRepository providerRepository,
ILogger<GetProvidersByVerificationStatusQueryHandler> logger
) : IQueryHandler<GetProvidersByVerificationStatusQuery, Result<IReadOnlyList<ProviderDto>>>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,7 @@ public async Task<Result<PagedResult<ProviderDto>>> HandleAsync(
"Erro ao buscar prestadores - Página: {Page}, Filtros: Nome='{Name}', Tipo={Type}, Status={Status}",
query.Page, query.Name, query.Type, query.VerificationStatus);

return Result<PagedResult<ProviderDto>>.Failure(Error.Internal(
"Erro interno ao buscar prestadores"));
return Result<PagedResult<ProviderDto>>.Failure(Error.Internal("Erro interno ao buscar prestadores"));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>MeAjudaAi.Modules.Providers.Tests</_Parameter1>
</AssemblyAttribute>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7</_Parameter1>
</AssemblyAttribute>
</ItemGroup>

<ItemGroup>
Expand Down
28 changes: 24 additions & 4 deletions src/Modules/Providers/Domain/Entities/Provider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,9 @@ public sealed class Provider : AggregateRoot<ProviderId>
private Provider() { }

/// <summary>
/// Construtor interno para testes que permite especificar o ID.
/// Construtor para testes que permite especificar o ID.
/// </summary>
internal Provider(
public Provider(
ProviderId id,
Guid userId,
string name,
Expand Down Expand Up @@ -158,7 +158,26 @@ public void UpdateProfile(string name, BusinessProfile businessProfile, string?
if (IsDeleted)
throw new ProviderDomainException("Cannot update deleted provider");

Name = name.Trim();
// Track which fields are being updated
var updatedFields = new List<string>();

var newName = name.Trim();
if (Name != newName)
updatedFields.Add("Name");

if (!BusinessProfile.ContactInfo.Email.Equals(businessProfile.ContactInfo.Email, StringComparison.OrdinalIgnoreCase))
updatedFields.Add("Email");

if (BusinessProfile.LegalName != businessProfile.LegalName)
updatedFields.Add("LegalName");

if (BusinessProfile.FantasyName != businessProfile.FantasyName)
updatedFields.Add("FantasyName");

if (BusinessProfile.Description != businessProfile.Description)
updatedFields.Add("Description");

Name = newName;
BusinessProfile = businessProfile;
MarkAsUpdated();

Expand All @@ -167,7 +186,8 @@ public void UpdateProfile(string name, BusinessProfile businessProfile, string?
1,
Name,
BusinessProfile.ContactInfo.Email,
updatedBy));
updatedBy,
updatedFields.ToArray()));
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@
/// <param name="Name">Novo nome do prestador de serviços</param>
/// <param name="Email">Novo email de contato</param>
/// <param name="UpdatedBy">Quem fez a atualização</param>
/// <param name="UpdatedFields">Lista dos campos que foram atualizados</param>
public record ProviderProfileUpdatedDomainEvent(
Guid AggregateId,
int Version,
string Name,
string Email,
string? UpdatedBy
string? UpdatedBy,
string[] UpdatedFields

Check warning on line 20 in src/Modules/Providers/Domain/Events/ProviderProfileUpdatedDomainEvent.cs

View workflow job for this annotation

GitHub Actions / Code Quality Checks

Properties should not return arrays (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1819)

Check warning on line 20 in src/Modules/Providers/Domain/Events/ProviderProfileUpdatedDomainEvent.cs

View workflow job for this annotation

GitHub Actions / Code Quality Checks

Properties should not return arrays (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1819)
) : DomainEvent(AggregateId, Version);
Loading