Requisitos — [Back] Substituir consultas concorrentes por consulta única na validação de marcas
Task #197183
Task pai: US 197129
Design doc: desing-doc.md
Glossário: ../context/CONTEXT.md
ADRs relacionados: —
AS-IS validado: coezzion-service-cart/src/Cart.Infrastructure/Data/Repositories/ProductRepository.cs (IsBrandsValid), SaleEcommerceBrandsValidHandler, CartService (fluxo Assistant)
Status: refinado Sessão de grilling: 26/06/2026 — decisões 2, 2b, 2c, 2d e 5 cristalizadas (ver seção abaixo)
Visão geral
Refatorar o método ProductRepository.IsBrandsValid na API Cart para eliminar consultas concorrentes por produto e consolidar a validação de marcas em uma única conexão e uma única query Dapper ao DbCore.
Problema atual (AS-IS): para cada ProductId recebido, o método cria um IServiceScope, instancia um novo MultiTenantContextProviderRead<CoreOrgDbContextRead> e executa uma query EF em paralelo via Task.WhenAll. Isso abre N conexões/contextos para o mesmo schema.
Objetivo (TO-BE): abrir uma única conexão read-only via IDbConnectionFactory (EDatabaseType.CoreDb), buscar as marcas de todos os produtos em uma query com cláusula IN na tabela {schemaName}."Products", e aplicar a regra de negócio existente em IsMultibrandStore sem alterar o contrato de IProductRepository nem o comportamento dos handlers.
Papéis
| Papel | Responsabilidade |
|---|---|
API Cart (SaleEcommerceBrandsValidHandler) | Invocar IProductRepository.IsBrandsValid quando CreateCartCommand.SaleEcommerce = true, passando context.SchemaName |
API Cart (CartService — fluxo Assistant) | Invocar IsBrandsValid com ProductId distintos e schema de _userProvider.GetSchemaName() |
ProductRepository | Executar a consulta consolidada e retornar se a combinação de marcas é permitida |
DbCore (Products) | Fonte da coluna Brand por Product.Id no schema do tenant ({schemaName}."Products") |
Decisões de refinamento (grilling)
| # | Decisão | Detalhe |
|---|---|---|
| 2 | Schema via parâmetro | Usar schemaName recebido no método — não reconsultar IUserProvider no repositório |
| 2b | Fonte de dados | Brand vem do DbCore (EDatabaseType.CoreDb), não do DbCoreOrg que o AS-IS consultava |
| 2c | Produto inexistente | Não exigir contagem 1:1 entre IDs solicitados e registros encontrados — AS-IS preservado |
| 2d | Guard defensivo | schemaName null/whitespace → retorna false sem abrir conexão |
| 5 | Cleanup de DI | Remover IServiceProvider e IConfiguration do construtor do ProductRepository nesta entrega |
Requisitos
RF-01 — Consulta única com uma connection
User Story: Como API Cart, eu quero validar as marcas dos produtos do carrinho com uma única conexão ao banco, para reduzir overhead de conexões paralelas no fluxo de venda ecommerce.
Acceptance Criteria:
- WHEN
IsBrandsValidé invocado com uma lista deProductIdeschemaNameTHEN oProductRepositorySHALL abrir apenas uma conexão read-only viaIDbConnectionFactory.GetConnectionAsync(EConnectionType.ReadOnly, EDatabaseType.CoreDb). - WHEN
IsBrandsValidprocessa os produtos THEN oProductRepositorySHALL NOT criar instâncias ad-hoc deMultiTenantContextProviderReadpor produto. - WHEN
IsBrandsValidprocessa os produtos THEN oProductRepositorySHALL NOT utilizarTask.WhenAllcom queries individuais porProductId. - WHEN
IsBrandsValidprocessa os produtos THEN oProductRepositorySHALL NOT criarIServiceScopeadicionais. - WHEN a consulta é executada THEN o
ProductRepositorySHALL interpolar oschemaNamerecebido como parâmetro no SQL ({schemaName}."Products") — sem reconsultarIUserProvider. - WHEN a consulta é executada THEN o
ProductRepositorySHALL buscar a colunaBrandde todos osProductIdinformados em uma única query Dapper com cláusulaIN/ANY. - WHEN a lista de
ProductIdcontém duplicatas THEN oProductRepositorySHALL deduplicar os IDs antes da consulta. - WHEN a execução da query é concluída THEN o
ProductRepositorySHALL liberar a conexão viausing, seguindo o padrão deResolveEcommercePrice. - WHEN
schemaNamefornullou whitespace THENIsBrandsValidSHALL retornarfalseimediatamente, sem abrir conexão (decisão 2d).
RF-02 — Preservação da regra de negócio de marcas
User Story: Como negócio, eu quero manter as mesmas regras de combinação de marcas permitidas na venda de prateleira, para que a otimização não altere o comportamento funcional.
Acceptance Criteria:
- WHEN nenhuma marca é retornada pela consulta THEN
IsBrandsValidSHALL retornarfalse(comportamento AS-IS). - WHEN todos os produtos possuem a mesma marca THEN
IsBrandsValidSHALL retornartrue. - WHEN os produtos possuem exatamente as marcas
ActionBrands.AREZZOeActionBrands.BRIZZA(combinação multibrand permitida) THENIsBrandsValidSHALL retornartrue. - WHEN os produtos possuem qualquer outra combinação de marcas distintas THEN
IsBrandsValidSHALL retornarfalse. - WHEN a validação é concluída THEN o
ProductRepositorySHALL continuar delegando a decisão ao método privadoIsMultibrandStore(ou lógica semanticamente idêntica), sem alterar os critérios acima. - WHEN algum
ProductIdnão existe emProductsTHENIsBrandsValidSHALL validar apenas com as marcas retornadas, sem exigir correspondência 1:1 entre IDs solicitados e registros encontrados (decisão 2c).
RF-03 — Contrato e integração inalterados
User Story: Como consumidor da API Cart, eu quero que a otimização seja transparente aos handlers existentes, para evitar regressões no pipeline de criação de carrinho.
Acceptance Criteria:
- WHEN a refatoração for concluída THEN a assinatura de
IProductRepository.IsBrandsValid(IEnumerable<int> products, string schemaName)SHALL permanecer inalterada. - WHEN
SaleEcommerceBrandsValidHandlerrecebeCreateCartCommand.SaleEcommerce = falseTHEN o handler SHALL NOT invocarIsBrandsValid(comportamento AS-IS). - WHEN
SaleEcommerceBrandsValidHandlerrecebeSaleEcommerce = trueeIsBrandsValidretornafalseTHEN o handler SHALL retornarBaseResult<CartCreatedDTO>.WithErrorcom a mensagem"Não é possível realizar venda de prateleira para produtos de marcas diferentes!". - WHEN
SaleEcommerceBrandsValidHandlerrecebeSaleEcommerce = trueeIsBrandsValidretornatrueTHEN o handler SHALL propagar oCreateCartContextsem erro. - WHEN o fluxo Assistant em
CartServicevalida marcas antes de montar oCreateCartCommandTHEN o serviço SHALL continuar invocandoIsBrandsValidcom os mesmos parâmetros (ProductIddistintos +schema).
RF-04 — Testes unitários
User Story: Como desenvolvedor, eu quero testes cobrindo a nova implementação de IsBrandsValid, para garantir que a otimização não quebre as regras de negócio.
Acceptance Criteria:
- WHEN os testes de
SaleEcommerceBrandsValidHandlerforem executados THEN todos os cenários existentes SHALL continuar passando sem alteração de comportamento esperado. - WHEN forem criados testes para
ProductRepository.IsBrandsValidTHEN o projetoCart.UnitTestsSHALL cobrir: marca única válida, combinação AREZZO+BRIZZA válida, combinação inválida, lista sem resultados no banco,schemaNamevazio (guard 2d), e produto parcialmente encontrado (decisão 2c). - WHEN os testes de
IsBrandsValidforem executados THEN eles SHALL verificar queGetConnectionAsync(ReadOnly, CoreDb)é chamado uma vez por invocação (sem paralelismo por produto).
RF-05 — Remoção de dependências mortas
User Story: Como desenvolvedor, eu quero eliminar dependências não utilizadas do ProductRepository, para manter o construtor enxuto após a refatoração.
Acceptance Criteria:
- WHEN a refatoração for concluída THEN
IServiceProviderSHALL ser removido do construtor doProductRepository(decisão 5). - WHEN a refatoração for concluída THEN
IConfigurationSHALL ser removido do construtor doProductRepository(decisão 5). - WHEN os testes que instanciam
ProductRepositorymanualmente forem atualizados THEN eles SHALL compilar sem os mocks deIServiceProvider/IConfiguration.
Fora de Escopo
- Alteração das regras de negócio de
IsMultibrandStore(ex.: incluir novas marcas na combinação multibrand) - Validação de contagem 1:1 entre
ProductIdsolicitados e registros encontrados emProducts - Refatoração de outros métodos do
ProductRepositoryque usam_contextProviderRead - Mudanças no pipeline de handlers do
CreateCartalém do que for estritamente necessário para manter compatibilidade - Instrumentação de métricas/APM ou benchmarks de performance
- Alterações em frontends (App/Portal) — validação é exclusivamente back-end
- Task de QA (#197343) — coberta separadamente
Dependências
| Dependência | Descrição | Status |
|---|---|---|
coezzion-service-cart | Repositório alvo da refatoração (ProductRepository) | Disponível |
IDbConnectionFactory | EConnectionType.ReadOnly + EDatabaseType.CoreDb | Disponível |
DbCore / tabela Products | {schemaName}."Products" — coluna Brand | Disponível |
SaleEcommerceBrandsValidHandler | Consumidor principal no pipeline CreateCart | Disponível |
CartService (Assistant) | Segundo ponto de chamada de IsBrandsValid | Disponível |
| Task #197343 — [QA] Validação | Validação funcional pós-implementação | Pendente |