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:
| Antes | Depois |
|---|---|
[ { id, name, sku, ... } ] | { recommendationsId: "...", items: [ { id, name, sku, ... } ] } |
Papéis Envolvidos
| Papel | Responsabilidade |
|---|---|
| Desenvolvedor Back-end | Alterar 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:
- WHEN a controller receber uma requisição
POST bff/customer-product-recommendation/{codeStore}/{cpf}THEN o sistema SHALL gerar umRecommendationsId(Guid) como PRIMEIRA operação do handler — antes de qualquer validação ou chamada ao serviço de IA - WHEN o
RecommendationsIdfor gerado THEN o sistema SHALL garantir que seja umGuidnovo (Guid.NewGuid()) e nuncanull
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:
- WHEN a consulta retornar recomendações com sucesso (200) THEN o sistema SHALL retornar um DTO
RecommendationsResponsecontendorecommendationsId(Guid) eitems(lista deProductRecommendationResponse) - WHEN um
ProductRecommendationResponsefor serializado THEN o sistema SHALL manter os mesmos campos existentes (id,name,sku,image,price,fullPrice,brand,status,sizes) - WHEN a resposta for serializada em JSON THEN o sistema SHALL usar camelCase (padrão C# do projeto, sem
JsonPropertyNamepara 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: [] }(nuncanull)
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:
- WHEN o handler finalizar (sucesso ou erro) THEN o sistema SHALL publicar um
CartRecommendationsEventusando o factory methodRecommendationRequested - WHEN o evento for publicado THEN o sistema SHALL usar
SendFifoAsynccomMessageGroupId = Guid.NewGuid()(semMessageDeduplicationId) - 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:
-
WHEN o evento
RecommendationRequestedfor montado THEN o sistema SHALL popular os seguintes campos:Campo Valor RecommendationsIdGuid.NewGuid()já geradoEventType"RecommendationRequested"CustomerIdint(PK do cliente, obtido do CPF)CodeStorestring(código da loja, da rota)CartIdnullUrlString completa da URL: bff/customer-product-recommendation/{codeStore}/{cpf}RequestCorpo JSON da requisição ( List<CartItemRequest>) serializadoDurationMsDuração da chamada ao Databricks/IA (não inclui validações, mapeamentos ou publicação na fila) -
IF a chamada ao Databricks retornar sucesso THEN o sistema SHALL popular:
Campo Valor StatusCodeCódigo HTTP retornado pelo Databricks ResponseJSON { "responseBI": "[...]", "responseBff": "[...]" }MessagenullOnde:
responseBI= resposta RAW do Databricks serializada como string JSONresponseBff= resposta mapeada (List<ProductRecommendationResponse>) serializada como string JSON
-
IF a chamada ao Databricks falhar (timeout, erro HTTP, exceção) THEN o sistema SHALL popular:
Campo Valor StatusCodeCódigo HTTP do erro do Databricks (ou 0se não for HTTP)ResponseJSON { "error": "mensagem do erro" }MessageMensagem da exceção ou descrição do erro -
IF a validação de entrada falhar (CPF inválido, loja inexistente, body inválido) THEN o sistema SHALL publicar o evento com:
Campo Valor 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
RequestouResponse: 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:
- WHEN o
ICustomerProductRecommendationServicefor alterado THEN o sistema SHALL mudar o retorno deGetAsyncpara um tipo que inclua oRecommendationsId - IF o retorno do service continuar usando
HttpResponseResult<T>THEN oRecommendationsIdSHALL 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+RecommendationsIdTask<(Guid RecommendationsId, HttpResponseResult<List<ProductRecommendationResponse>> Result)> GetAsync(...); - Opção B: O
RecommendationsIdé gerado no service e retornado no próprioHttpResponseResult(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:
- WHEN o controller receber o resultado do service THEN o sistema SHALL montar um
RecommendationsResponsecomrecommendationsIdeitems - WHEN o status for
200THEN o sistema SHALL retornarOk(new RecommendationsResponse(recommendationsId, result.Response)) - WHEN o status for
400,404ou 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
RecommendationsIdfoi gerado e o evento foi publicado — mas o retorno ao cliente é o erro original, sem envolver oRecommendationsId
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:
- WHEN a classe
RecommendationsResponsefor declarada THEN o sistema SHALL residir emBFF.Coezzion.Application.DTO.Recommend - 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ência | Descrição | Status |
|---|---|---|
| Task #196020 | Fila SQS FIFO (AWS_SQS_CART_RECOMMENDATION_HISTORY_FIFO) e CartRecommendationsEvent | Pendente |
| Task #196018 | Tabela CartRecommendationsHistory | Pendente |
| Task #196024 | Ajuste no Front App para receber novo formato | Pendente |
| Task #196025 | Ajuste no Front Portal para receber novo formato | Pendente |
Questões em Aberto
- Nome do campo na resposta (
recommendationsIdvsRecommendationsId): Decidido — camelCase (padrão do projeto, semJsonPropertyName) - Como o
CustomerId(int) é obtido a partir do CPF: será resolvido na implementação (via serviço de cliente ou mock) - Nome do DTO: Decidido —
RecommendationsResponse - Design da alteração no service (Opção A tuple vs Opção B wrapper): a definir durante implementação