Skip to main content

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

PapelResponsabilidade
Desenvolvedor Back-endImplementar 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:

  1. 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)
  2. WHEN o serviço estiver rodando THEN o sistema SHALL executar loop contínuo com while (!stoppingToken.IsCancellationRequested) chamando SubscribeAsync com o overload Func<ReceiveMessageResponse, string, CancellationToken, Task>
  3. WHEN a resposta SQS contiver 0 mensagens THEN o sistema SHALL aguardar Task.Delay(5s, cancellationToken) antes de nova iteração
  4. WHEN o serviço for registrado THEN o sistema SHALL ser adicionado em DependencyInjectionConfig.cs via services.AddHostedService<CartRecommendationsHistoryConsumerService>()

Localização: Cart.API/Background/CartRecommendationsHistoryConsumerService.cs

Injeção de dependências (primary constructor):

  • IMessageBusService messageBusService
  • IServiceScopeFactory 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:

  1. WHEN uma mensagem for recebida THEN o sistema SHALL deserializar o body como CartRecommendationsEvent via JsonSerializer.Deserialize<CartRecommendationsEvent>
  2. WHEN a deserialização resultar em null ou lançar exceção THEN o sistema SHALL não deletar a mensagem da fila (retry automático via VisibilityTimeout de 600s)
  3. WHEN a deserialização for bem-sucedida THEN o sistema SHALL criar um IServiceScope via IServiceScopeFactory.CreateScope()
  4. WHEN o scope for criado THEN o sistema SHALL obter SchemaNameScopedWrapper do scope e setar SchemaName = event.Schema
  5. WHEN o scope estiver configurado THEN o sistema SHALL obter IZZMediator do scope e despachar SaveRecommendationsHistoryCommand
  6. WHEN result.IsValid() for true THEN o sistema SHALL incluir o ReceiptHandle da mensagem na lista de deleção
  7. WHEN result.IsValid() for false THEN o sistema SHALL não incluir o ReceiptHandle na lista de deleção (retry automático via VisibilityTimeout)
  8. 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:

  1. WHEN o command for declarado THEN o sistema SHALL conter os campos:
    • SchemaName (string) — schema do tenant, para configuração do SchemaNameScopedWrapper no handler
    • RecommendationsId (Guid)
    • EventType (string)
    • CartId (int?)
    • CustomerId (int)
    • CodeStore (string)
    • Url (string?)
    • Request (string?)
    • Response (string?)
    • StatusCode (int?)
    • DurationMs (long?)
    • Message (string?)
    • Timestamp (DateTime) — mapeado de CartRecommendationsEvent.Timestamp
  2. WHEN o command for declarado THEN o sistema SHALL implementar ICommand<BaseResult>
  3. 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:

  1. WHEN o handler receber o command THEN o sistema SHALL mapear todos os campos do command para CartRecommendationsHistoryModel
  2. WHEN o mapping for concluído THEN o sistema SHALL chamar ICartRecommendationsHistoryRepository.SaveAsync(model)
  3. WHEN a persistência for bem-sucedida THEN o sistema SHALL retornar BaseResult válido
  4. WHEN a persistência lançar exceção THEN o sistema SHALL retornar BaseResult inválido com a mensagem de erro
  5. 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:

  1. WHEN a interface for declarada THEN o sistema SHALL expor Task SaveAsync(CartRecommendationsHistoryModel model)
  2. WHEN a implementação for criada THEN o sistema SHALL usar o CoreOrgDbContext (DbCoreOrg) com o schema resolvido via SchemaNameScopedWrapper
  3. WHEN SaveAsync for chamado THEN o sistema SHALL executar Add + SaveChangesAsync
  4. WHEN a interface for declarada THEN o sistema SHALL residir em Cart.Domain/Interfaces/Repositories/
  5. WHEN a implementação for declarada THEN o sistema SHALL residir em Cart.Infrastructure/Data/Repositories/
  6. WHEN o repositório for registrado THEN o sistema SHALL ser adicionado em DependencyInjectionConfig.cs com ciclo de vida Scoped

RF-06 — Registro de dependências

Critérios de Aceitação:

  1. WHEN a aplicação for iniciada THEN o sistema SHALL registrar CartRecommendationsHistoryConsumerService via services.AddHostedService<CartRecommendationsHistoryConsumerService>()
  2. WHEN a aplicação for iniciada THEN o sistema SHALL registrar ICartRecommendationsHistoryRepositoryCartRecommendationsHistoryRepository via services.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 é string livre (definido na task #196020)

Dependências

DependênciaDescriçãoStatus
Task #196018Tabela CartRecommendationsHistory e CartRecommendationsHistoryModelPendente
Task #196020Fila AWS_SQS_CART_RECOMMENDATION_HISTORY_FIFO e CartRecommendationsEventPendente

Arquivos alterados

ArquivoProjetoAção
Background/CartRecommendationsHistoryConsumerService.csCart.APINovo
Commands/CartRecommendationsHistory/SaveRecommendationsHistoryCommand.csCart.DomainNovo
Application/Messages/Commands/CartRecommendationsHistory/SaveRecommendationsHistoryCommandHandler.csCart.APINovo
Interfaces/Repositories/ICartRecommendationsHistoryRepository.csCart.DomainNovo
Data/Repositories/CartRecommendationsHistoryRepository.csCart.InfrastructureNovo
Configuration/DependencyInjectionConfig.csCart.APIAlterar — registrar hosted service e repositório

Referências

ArtefatoDescrição
ADR-011Estratégia de dedup/ordering da fila FIFO
Task #196018 — TabelaSchema da tabela e modelo
Task #196020 — FilaCartRecommendationsEvent, QueueTypes, StructureStack
LogQueueConsumerService.csReferência de padrão FIFO consumer na Checkout API
BaseTenantEventListenerService.csReferência de padrão consumer na Cart API