Skip to main content

Documento de Requisitos — Consulta de Produtos Recomendados

Tarefa: #196023 Contexto: Sequência de implementação #5 Refinamento: Grill session 12/06/2026


Visão Geral

Modificar o endpoint POST bff/customer-product-recommendation/{codeStore}/{cpf} (BFF) para gerar um RecommendationsId único a cada consulta, alterar o formato da resposta e publicar evento na fila SQS FIFO para rastreamento do fluxo de upsell.

Mudança principal na resposta:

AntesDepois
[ { id, name, sku, ... } ]{ recommendationsId: "...", items: [ { id, name, sku, ... } ] }

Papéis Envolvidos

PapelResponsabilidade
Desenvolvedor Back-endAlterar controller, service e DTOs do BFF; integrar com Message BUS

Requisitos Funcionais

RF-01 — Geração do RecommendationsId

História de Usuário: Como sistema, quero gerar um identificador único para cada consulta de recomendação, para rastrear todo o fluxo de upsell.

Critérios de Aceitação:

  1. WHEN a controller receber uma requisição POST bff/customer-product-recommendation/{codeStore}/{cpf} THEN o sistema SHALL gerar um RecommendationsId (Guid) como PRIMEIRA operação do handler — antes de qualquer validação ou chamada ao serviço de IA
  2. WHEN o RecommendationsId for gerado THEN o sistema SHALL garantir que seja um Guid novo (Guid.NewGuid()) e nunca null

Casos de Borda:

  • O UUID é gerado mesmo quando a requisição falha em validações subsequentes (CPF inválido, loja inexistente, body inválido)

RF-02 — Novo formato da resposta (200 OK)

História de Usuário: Como front-end, quero receber a resposta com o RecommendationsId junto aos produtos recomendados, para trafegar o identificador nas etapas seguintes do upsell.

Critérios de Aceitação:

  1. WHEN a consulta retornar recomendações com sucesso (200) THEN o sistema SHALL retornar um DTO RecommendationsResponse contendo recommendationsId (Guid) e items (lista de ProductRecommendationResponse)
  2. WHEN um ProductRecommendationResponse for serializado THEN o sistema SHALL manter os mesmos campos existentes (id, name, sku, image, price, fullPrice, brand, status, sizes)
  3. WHEN a resposta for serializada em JSON THEN o sistema SHALL usar camelCase (padrão C# do projeto, sem JsonPropertyName para DTOs internos)

Novo DTO:

public record RecommendationsResponse(
Guid RecommendationsId,
List<ProductRecommendationResponse> Items
);

Formato esperado:

{
"recommendationsId": "a1b2c3d4-...",
"items": [
{ "id": 123, "name": "Produto X", "sku": "ABC123", ... },
{ "id": 456, "name": "Produto Y", "sku": "DEF456", ... }
]
}

Casos de Borda:

  • WHEN não houver produtos recomendados (lista vazia) THEN o sistema SHALL retornar { recommendationsId: "...", items: [] } (nunca null)

RF-03 — Publicação do evento na fila SQS FIFO

História de Usuário: Como sistema, quero publicar um evento de recomendação na fila AWS_SQS_CART_RECOMMENDATION_HISTORY_FIFO ao final de cada consulta, para registro no histórico.

Critérios de Aceitação:

  1. WHEN o handler finalizar (sucesso ou erro) THEN o sistema SHALL publicar um CartRecommendationsEvent usando o factory method RecommendationRequested
  2. WHEN o evento for publicado THEN o sistema SHALL usar SendFifoAsync com MessageGroupId = Guid.NewGuid() (sem MessageDeduplicationId)
  3. IF a publicação na fila falhar THEN o sistema SHALL logar o erro e NÃO impactar a resposta ao cliente (fire-and-forget)

Casos de Borda:

  • Timeout ou falha de rede no SQS: apenas logar, não lançar exceção ao cliente

RF-04 — População dos campos do evento

História de Usuário: Como consumer do evento (serviço de histórico), quero receber dados completos e consistentes sobre cada consulta, para registrar o histórico fielmente.

Critérios de Aceitação:

  1. WHEN o evento RecommendationRequested for montado THEN o sistema SHALL popular os seguintes campos:

    CampoValor
    RecommendationsIdGuid.NewGuid() já gerado
    EventType"RecommendationRequested"
    CustomerIdint (PK do cliente, obtido do CPF)
    CodeStorestring (código da loja, da rota)
    CartIdnull
    UrlString completa da URL: bff/customer-product-recommendation/{codeStore}/{cpf}
    RequestCorpo JSON da requisição (List<CartItemRequest>) serializado
    DurationMsDuração da chamada ao Databricks/IA (não inclui validações, mapeamentos ou publicação na fila)
  2. IF a chamada ao Databricks retornar sucesso THEN o sistema SHALL popular:

    CampoValor
    StatusCodeCódigo HTTP retornado pelo Databricks
    ResponseJSON { "responseBI": "[...]", "responseBff": "[...]" }
    Messagenull

    Onde:

    • responseBI = resposta RAW do Databricks serializada como string JSON
    • responseBff = resposta mapeada (List<ProductRecommendationResponse>) serializada como string JSON
  3. IF a chamada ao Databricks falhar (timeout, erro HTTP, exceção) THEN o sistema SHALL popular:

    CampoValor
    StatusCodeCódigo HTTP do erro do Databricks (ou 0 se não for HTTP)
    ResponseJSON { "error": "mensagem do erro" }
    MessageMensagem da exceção ou descrição do erro
  4. IF a validação de entrada falhar (CPF inválido, loja inexistente, body inválido) THEN o sistema SHALL publicar o evento com:

    CampoValor
    StatusCodeCódigo HTTP do erro (ex: 400)
    ResponseJSON { "error": "mensagem do erro" }
    MessageDescrição do erro de validação
    DurationMsnull (não houve chamada ao Databricks)

Casos de Borda:

  • Erro de serialização dos campos Request ou Response: serializar string vazia ""

RF-05 — Alteração na interface do Service

História de Usuário: Como desenvolvedor, quero que o service retorne o RecommendationsId junto à resposta, para que o controller possa montar o novo DTO.

Critérios de Aceitação:

  1. WHEN o ICustomerProductRecommendationService for alterado THEN o sistema SHALL mudar o retorno de GetAsync para um tipo que inclua o RecommendationsId
  2. IF o retorno do service continuar usando HttpResponseResult<T> THEN o RecommendationsId SHALL ser incluído como propriedade adicional ou o tipo genérico deve ser alterado

Decisão de design (a confirmar):

  • Opção A: Novo record que inclui HttpResponseResult + RecommendationsId
    Task<(Guid RecommendationsId, HttpResponseResult<List<ProductRecommendationResponse>> Result)> GetAsync(...);
  • Opção B: O RecommendationsId é gerado no service e retornado no próprio HttpResponseResult (wrapper genérico)

RF-06 — Alteração no Controller

História de Usuário: Como sistema, quero que o controller retorne o novo formato de resposta, para que o front-end receba o RecommendationsId.

Critérios de Aceitação:

  1. WHEN o controller receber o resultado do service THEN o sistema SHALL montar um RecommendationsResponse com recommendationsId e items
  2. WHEN o status for 200 THEN o sistema SHALL retornar Ok(new RecommendationsResponse(recommendationsId, result.Response))
  3. WHEN o status for 400, 404 ou outro erro THEN o sistema SHALL retornar o erro AS IS ao cliente (sem modificar o formato de erro original)

Casos de Borda:

  • Em caso de erro, o RecommendationsId foi gerado e o evento foi publicado — mas o retorno ao cliente é o erro original, sem envolver o RecommendationsId

RF-07 — Estrutura do RecommendationsResponse

História de Usuário: Como front-end (App e Portal), quero receber um contrato consistente e documentado.

Critérios de Aceitação:

  1. WHEN a classe RecommendationsResponse for declarada THEN o sistema SHALL residir em BFF.Coezzion.Application.DTO.Recommend
  2. WHEN o DTO for serializado THEN o sistema SHALL usar camelCase (sem JsonPropertyName) seguindo o padrão de DTOs internos do projeto

Código do DTO:

namespace BFF.Coezzion.Application.DTO.Recommend;

public record RecommendationsResponse(
Guid RecommendationsId,
List<ProductRecommendationResponse> Items
);

Representação Visual (Sequence)


Fora de Escopo

  • Alterar a lógica de recomendação em si (chamada ao Databricks, enriquecimento de stock, filtragem)
  • Cache de recomendações
  • Versionamento de API (v1/v2) — mudança é breaking, coordenada com tasks #196024 e #196025
  • DLQ (Dead Letter Queue) — tratado em task separada se necessário

Dependências

DependênciaDescriçãoStatus
Task #196020Fila SQS FIFO (AWS_SQS_CART_RECOMMENDATION_HISTORY_FIFO) e CartRecommendationsEventPendente
Task #196018Tabela CartRecommendationsHistoryPendente
Task #196024Ajuste no Front App para receber novo formatoPendente
Task #196025Ajuste no Front Portal para receber novo formatoPendente

Questões em Aberto

  • Nome do campo na resposta (recommendationsId vs RecommendationsId): Decidido — camelCase (padrão do projeto, sem JsonPropertyName)
  • Como o CustomerId (int) é obtido a partir do CPF: será resolvido na implementação (via serviço de cliente ou mock)
  • Nome do DTO: DecididoRecommendationsResponse
  • Design da alteração no service (Opção A tuple vs Opção B wrapper): a definir durante implementação