Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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