Documento de Requisitos — Serviço de Registro de Histórico
Tarefa: #196021 Contexto: Sequência de implementação #3
Visão Geral
Criar um BackgroundService standalone na API Cart que consuma mensagens da fila SQS FIFO AWS_SQS_CART_RECOMMENDATION_HISTORY_FIFO, deserialize eventos CartRecommendationsEvent e persista os registros na tabela CartRecommendationsHistory via IZZMediator.
Papéis Envolvidos
| Papel | Responsabilidade |
|---|---|
| Desenvolvedor Back-end | Implementar o consumer, command, handler e repositório |
Requisitos Funcionais
RF-01 — CartRecommendationsHistoryConsumerService
História de Usuário: Como sistema, quero um BackgroundService que consuma a fila FIFO de eventos de histórico de upsell, seguindo o padrão do LogQueueConsumerService da Checkout API.
Critérios de Aceitação:
- WHEN o serviço for iniciado THEN o sistema SHALL resolver a URL da fila via
IMessageBusService.GetQueueUrlFifo(QueueTypes.AWS_SQS_CART_RECOMMENDATION_HISTORY_FIFO) - WHEN o serviço estiver rodando THEN o sistema SHALL executar loop contínuo com
while (!stoppingToken.IsCancellationRequested)chamandoSubscribeAsynccom o overloadFunc<ReceiveMessageResponse, string, CancellationToken, Task> - WHEN a resposta SQS contiver 0 mensagens THEN o sistema SHALL aguardar
Task.Delay(5s, cancellationToken)antes de nova iteração - WHEN o serviço for registrado THEN o sistema SHALL ser adicionado em
DependencyInjectionConfig.csviaservices.AddHostedService<CartRecommendationsHistoryConsumerService>()
Localização: Cart.API/Background/CartRecommendationsHistoryConsumerService.cs
Injeção de dependências (primary constructor):
IMessageBusService messageBusServiceIServiceScopeFactory serviceScopeFactory
RF-02 — Processamento de mensagem
História de Usuário: Como sistema, quero que cada mensagem da fila seja deserializada, processada em scope DI isolado com multi-tenancy, e que apenas as mensagens bem-sucedidas sejam removidas da fila.
Critérios de Aceitação:
- WHEN uma mensagem for recebida THEN o sistema SHALL deserializar o body como
CartRecommendationsEventviaJsonSerializer.Deserialize<CartRecommendationsEvent> - WHEN a deserialização resultar em
nullou lançar exceção THEN o sistema SHALL não deletar a mensagem da fila (retry automático via VisibilityTimeout de 600s) - WHEN a deserialização for bem-sucedida THEN o sistema SHALL criar um
IServiceScopeviaIServiceScopeFactory.CreateScope() - WHEN o scope for criado THEN o sistema SHALL obter
SchemaNameScopedWrapperdo scope e setarSchemaName = event.Schema - WHEN o scope estiver configurado THEN o sistema SHALL obter
IZZMediatordo scope e despacharSaveRecommendationsHistoryCommand - WHEN
result.IsValid()fortrueTHEN o sistema SHALL incluir oReceiptHandleda mensagem na lista de deleção - WHEN
result.IsValid()forfalseTHEN o sistema SHALL não incluir oReceiptHandlena lista de deleção (retry automático via VisibilityTimeout) - WHEN todas as mensagens do batch forem processadas THEN o sistema SHALL chamar
DeleteBatchAsync(queueUrl, successfulReceiptHandles)com os handles bem-sucedidos
Observação: Sem DLQ configurada — mensagens que falham indefinidamente permanecem em loop até intervenção manual.
RF-03 — SaveRecommendationsHistoryCommand
História de Usuário: Como sistema, quero um command que transporte todos os dados do evento CartRecommendationsEvent para o handler de persistência, incluindo o schema para multi-tenancy.
Critérios de Aceitação:
- WHEN o command for declarado THEN o sistema SHALL conter os campos:
SchemaName(string) — schema do tenant, para configuração doSchemaNameScopedWrapperno handlerRecommendationsId(Guid)EventType(string)CartId(int?)CustomerId(int)CodeStore(string)Url(string?)Request(string?)Response(string?)StatusCode(int?)DurationMs(long?)Message(string?)Timestamp(DateTime) — mapeado deCartRecommendationsEvent.Timestamp
- WHEN o command for declarado THEN o sistema SHALL implementar
ICommand<BaseResult> - WHEN o command for declarado THEN o sistema SHALL residir em
Cart.Domain/Commands/CartRecommendationsHistory/
RF-04 — SaveRecommendationsHistoryCommandHandler
História de Usuário: Como sistema, quero um handler que mapeie o command para CartRecommendationsHistoryModel e persista via repositório.
Critérios de Aceitação:
- WHEN o handler receber o command THEN o sistema SHALL mapear todos os campos do command para
CartRecommendationsHistoryModel - WHEN o mapping for concluído THEN o sistema SHALL chamar
ICartRecommendationsHistoryRepository.SaveAsync(model) - WHEN a persistência for bem-sucedida THEN o sistema SHALL retornar
BaseResultválido - WHEN a persistência lançar exceção THEN o sistema SHALL retornar
BaseResultinválido com a mensagem de erro - WHEN o handler for declarado THEN o sistema SHALL residir em
Cart.API/Application/Messages/Commands/CartRecommendationsHistory/
RF-05 — ICartRecommendationsHistoryRepository e implementação
História de Usuário: Como sistema, quero um repositório para abstrair a persistência na tabela CartRecommendationsHistory.
Critérios de Aceitação:
- WHEN a interface for declarada THEN o sistema SHALL expor
Task SaveAsync(CartRecommendationsHistoryModel model) - WHEN a implementação for criada THEN o sistema SHALL usar o
CoreOrgDbContext(DbCoreOrg) com o schema resolvido viaSchemaNameScopedWrapper - WHEN
SaveAsyncfor chamado THEN o sistema SHALL executarAdd+SaveChangesAsync - WHEN a interface for declarada THEN o sistema SHALL residir em
Cart.Domain/Interfaces/Repositories/ - WHEN a implementação for declarada THEN o sistema SHALL residir em
Cart.Infrastructure/Data/Repositories/ - WHEN o repositório for registrado THEN o sistema SHALL ser adicionado em
DependencyInjectionConfig.cscom ciclo de vidaScoped
RF-06 — Registro de dependências
Critérios de Aceitação:
- WHEN a aplicação for iniciada THEN o sistema SHALL registrar
CartRecommendationsHistoryConsumerServiceviaservices.AddHostedService<CartRecommendationsHistoryConsumerService>() - WHEN a aplica ção for iniciada THEN o sistema SHALL registrar
ICartRecommendationsHistoryRepository→CartRecommendationsHistoryRepositoryviaservices.AddScoped
Fora de Escopo
- Endpoints REST para consulta do histórico
- DLQ (Dead Letter Queue) — task futura
- Monitoramento e alarmes
- Limite máximo de tentativas de retry (sem DLQ, sem contador)
- Enum tipado para
EventType— campo éstringlivre (definido na task #196020)
Dependências
| Dependência | Descrição | Status |
|---|---|---|
| Task #196018 | Tabela CartRecommendationsHistory e CartRecommendationsHistoryModel | Pendente |
| Task #196020 | Fila AWS_SQS_CART_RECOMMENDATION_HISTORY_FIFO e CartRecommendationsEvent | Pendente |
Arquivos alterados
| Arquivo | Projeto | Ação |
|---|---|---|
Background/CartRecommendationsHistoryConsumerService.cs | Cart.API | Novo |
Commands/CartRecommendationsHistory/SaveRecommendationsHistoryCommand.cs | Cart.Domain | Novo |
Application/Messages/Commands/CartRecommendationsHistory/SaveRecommendationsHistoryCommandHandler.cs | Cart.API | Novo |
Interfaces/Repositories/ICartRecommendationsHistoryRepository.cs | Cart.Domain | Novo |
Data/Repositories/CartRecommendationsHistoryRepository.cs | Cart.Infrastructure | Novo |
Configuration/DependencyInjectionConfig.cs | Cart.API | Alterar — registrar hosted service e repositório |
Referências
| Artefato | Descrição |
|---|---|
| ADR-011 | Estratégia de dedup/ordering da fila FIFO |
| Task #196018 — Tabela | Schema da tabela e modelo |
| Task #196020 — Fila | CartRecommendationsEvent, QueueTypes, StructureStack |
LogQueueConsumerService.cs | Referência de padrão FIFO consumer na Checkout API |
BaseTenantEventListenerService.cs | Referência de padrão consumer na Cart API |