Skip to main content

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:

VarianteMecanismoOnde
A — Domain Event + MediatR NotificationBaseEntity.AddEventSaveChangesINotificationHandler<T>Core.DB (UserModel → Admin.API)
B — Event Handler explícitoInterface em Domain, impl em Infra, chamada manual no handlerIntegration, 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)

ComponentePadrãoObservação
PublishRecommendationHistoryHandlerHandler na chainCorreto para create-cart
CartService.SendEvent + local functionsInlineDuplicado, acoplado
MessageBusServiceCachedDecoratorDecorator cache URLAlinhado a CacheStoreRepositoryDecorator
CartRecommendationHistoryListenerServiceBaseTenantEventListenerServiceConsumer — não alterar
CartRecommendationHistoryServiceService persistênciaConsumer — não alterar

Proposta alinhada ao padrão B + Queue Service

Para histórico de recomendações, combinar o melhor dos dois:

AspectoSeguir de
Interface em DomainILogQueueService
Fire-and-forget com logLogQueueService.EnqueueAsync
Métodos por operaçãoIChangeCartDataEventHandler (explícito)
Cache de URL filaMessageBusServiceCachedDecorator (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 coloca LogQueueService nessa pasta apesar do nome "Service", ambas as pastas são aceitáveis. Preferir Infrastructure/Services/ se o Cart já usa essa pasta para serviços de domínio (CartRecommendationHistoryService está 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çãoCallerQuando publicar
Create cartPublishRecommendationHistoryHandlercommand.RecommendationsId != null e cart persistido
Add itemCartService.Handle(AddItemToCartCommand)cart.RecommendationsId != null
Remove itemCartService.Handle(RemoveCartItemCommand)cart.RecommendationsId != null e houve mudança de estado