Pesquisa — Padrão Event Handlers na Coezzion
Data: 2026-07-01
Contexto: US 195746 — padrão de referência para side-effects de integração no Cart.
Dois padrões distintos no ecossistema
A skill dotnet-skill menciona "Event Handlers (Domínio)" — na prática existem duas variantes:
| Variante | Mecanismo | Onde |
|---|---|---|
| A — Domain Event + MediatR Notification | BaseEntity.AddEvent → SaveChanges → INotificationHandler<T> | Core.DB (UserModel → Admin.API) |
| B — Event Handler explícito | Interface em Domain, impl em Infra, chamada manual no handler | Integration, Checkout, Payments |
Para Cart + SQS, a variante B é a referência correta.
Variante B — Event Handler explícito
Estrutura de arquivos (convenção dotnet-skill)
<Serviço>.Domain/Interfaces/EventHandlers/I<Acao>EventHandler.cs
<Serviço>.Infrastructure/EventHandlers/<Acao>EventHandler.cs
DependencyInjectionConfig.cs → AddScoped<I..., ...>()
Exemplo — Integration: ChangeCartData
Interface (Integration.Domain):
public interface IChangeCartDataEventHandler
{
Task ChangeCartData(int cartId, string schema, string trackingURL = null, string ticket = null);
}
Implementação (Integration.Infrastructure):
public class ChangeCartDataEventHandler : IChangeCartDataEventHandler
{
public async Task ChangeCartData(...)
{
var queueUrl = await _messageBusService.GetQueueUrl(QueueTypes.AWS_SQS_CHANGE_CART_DATA);
var evt = new ChangeCartDataEvent(cartId, trackingURL, ticket, schemaName);
await _messageBusService.SendAsync(queueUrl, evt);
}
}
Uso: injetado no command handler, chamado após operação de negócio.
Exemplo — Payments Reports: PaymentCreated
Mesma estrutura — publica evento SQS para integração e-commerce após criação de pagamento.
Variante B estendida — Queue Service (AllLogs)
Checkout usa nomenclatura Service em vez de EventHandler, mas o conceito é o mesmo:
Checkout.Domain/Interfaces/Services/ILogQueueService.cs
Checkout.Infraestructure/EventHandlers/LogQueueService.cs
public interface ILogQueueService
{
Task EnqueueAsync(LogQueueMessageEvent message);
}
Diferença: encapsula fila + serialização + fire-and-forget. Vários handlers chamam ILogQueueService sem conhecer SQS.
Registro DI:
services.AddSingleton<LogQueueService>();
services.AddScoped<ILogQueueService, LogQueueServiceDecorator<LogQueueService>>();
Decorator injeta TransactionId do contexto — cross-cutting concern separado.
O que já existe no Cart (implementação atual)
| Componente | Padrão | Observação |
|---|---|---|
PublishRecommendationHistoryHandler | Handler na chain | Correto para create-cart |
CartService.SendEvent + local functions | Inline | Duplicado, acoplado |
MessageBusServiceCachedDecorator | Decorator cache URL | Alinhado a CacheStoreRepositoryDecorator |
CartRecommendationHistoryListenerService | BaseTenantEventListenerService | Consumer — não alterar |
CartRecommendationHistoryService | Service persistência | Consumer — não alterar |
Proposta alinhada ao padrão B + Queue Service
Para histórico de recomendações, combinar o melhor dos dois:
| Aspecto | Seguir de |
|---|---|
| Interface em Domain | ILogQueueService |
| Fire-and-forget com log | LogQueueService.EnqueueAsync |
| Métodos por operação | IChangeCartDataEventHandler (explícito) |
| Cache de URL fila | MessageBusServiceCachedDecorator (já existe) |
Nome sugerido: ICartRecommendationHistoryPublisher
Localização interface: Cart.Domain/Interfaces/Services/
Localização impl: Cart.Infrastructure/Services/ ou Cart.Infrastructure/EventHandlers/
A skill dotnet lista Event Handlers em
Infrastructure/EventHandlers/. Como o Checkout colocaLogQueueServicenessa pasta apesar do nome "Service", ambas as pastas são aceitáveis. PreferirInfrastructure/Services/se o Cart já usa essa pasta para serviços de domínio (CartRecommendationHistoryServiceestá em API — consumer é diferente).
Registro DI proposto
// DependencyInjectionConfig.cs
services.AddScoped<ICartRecommendationHistoryPublisher, CartRecommendationHistoryPublisher>();
// Manter:
services.AddSingleton<IMessageBusServiceCachedDecorator, MessageBusServiceCachedDecorator<IMessageBusService>>();
services.AddHostedService<CartRecommendationHistoryListenerService>();
services.AddScoped<ICartRecommendationHistoryRepository, CartRecommendationHistoryRepository>();
Call sites (quem chama o publisher)
| Operação | Caller | Quando publicar |
|---|---|---|
| Create cart | PublishRecommendationHistoryHandler | command.RecommendationsId != null e cart persistido |
| Add item | CartService.Handle(AddItemToCartCommand) | cart.RecommendationsId != null |
| Remove item | CartService.Handle(RemoveCartItemCommand) | cart.RecommendationsId != null e houve mudança de estado |