Skip to main content

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

PapelResponsabilidade
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()
ProductRepositoryExecutar 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ãoDetalhe
2Schema via parâmetroUsar schemaName recebido no método — não reconsultar IUserProvider no repositório
2bFonte de dadosBrand vem do DbCore (EDatabaseType.CoreDb), não do DbCoreOrg que o AS-IS consultava
2cProduto inexistenteNão exigir contagem 1:1 entre IDs solicitados e registros encontrados — AS-IS preservado
2dGuard defensivoschemaName null/whitespace → retorna false sem abrir conexão
5Cleanup de DIRemover 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:

  1. WHEN IsBrandsValid é invocado com uma lista de ProductId e schemaName THEN o ProductRepository SHALL abrir apenas uma conexão read-only via IDbConnectionFactory.GetConnectionAsync(EConnectionType.ReadOnly, EDatabaseType.CoreDb).
  2. WHEN IsBrandsValid processa os produtos THEN o ProductRepository SHALL NOT criar instâncias ad-hoc de MultiTenantContextProviderRead por produto.
  3. WHEN IsBrandsValid processa os produtos THEN o ProductRepository SHALL NOT utilizar Task.WhenAll com queries individuais por ProductId.
  4. WHEN IsBrandsValid processa os produtos THEN o ProductRepository SHALL NOT criar IServiceScope adicionais.
  5. WHEN a consulta é executada THEN o ProductRepository SHALL interpolar o schemaName recebido como parâmetro no SQL ({schemaName}."Products") — sem reconsultar IUserProvider.
  6. WHEN a consulta é executada THEN o ProductRepository SHALL buscar a coluna Brand de todos os ProductId informados em uma única query Dapper com cláusula IN / ANY.
  7. WHEN a lista de ProductId contém duplicatas THEN o ProductRepository SHALL deduplicar os IDs antes da consulta.
  8. WHEN a execução da query é concluída THEN o ProductRepository SHALL liberar a conexão via using, seguindo o padrão de ResolveEcommercePrice.
  9. WHEN schemaName for null ou whitespace THEN IsBrandsValid SHALL retornar false imediatamente, 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:

  1. WHEN nenhuma marca é retornada pela consulta THEN IsBrandsValid SHALL retornar false (comportamento AS-IS).
  2. WHEN todos os produtos possuem a mesma marca THEN IsBrandsValid SHALL retornar true.
  3. WHEN os produtos possuem exatamente as marcas ActionBrands.AREZZO e ActionBrands.BRIZZA (combinação multibrand permitida) THEN IsBrandsValid SHALL retornar true.
  4. WHEN os produtos possuem qualquer outra combinação de marcas distintas THEN IsBrandsValid SHALL retornar false.
  5. WHEN a validação é concluída THEN o ProductRepository SHALL continuar delegando a decisão ao método privado IsMultibrandStore (ou lógica semanticamente idêntica), sem alterar os critérios acima.
  6. WHEN algum ProductId não existe em Products THEN IsBrandsValid SHALL 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:

  1. WHEN a refatoração for concluída THEN a assinatura de IProductRepository.IsBrandsValid(IEnumerable<int> products, string schemaName) SHALL permanecer inalterada.
  2. WHEN SaleEcommerceBrandsValidHandler recebe CreateCartCommand.SaleEcommerce = false THEN o handler SHALL NOT invocar IsBrandsValid (comportamento AS-IS).
  3. WHEN SaleEcommerceBrandsValidHandler recebe SaleEcommerce = true e IsBrandsValid retorna false THEN o handler SHALL retornar BaseResult<CartCreatedDTO>.WithError com a mensagem "Não é possível realizar venda de prateleira para produtos de marcas diferentes!".
  4. WHEN SaleEcommerceBrandsValidHandler recebe SaleEcommerce = true e IsBrandsValid retorna true THEN o handler SHALL propagar o CreateCartContext sem erro.
  5. WHEN o fluxo Assistant em CartService valida marcas antes de montar o CreateCartCommand THEN o serviço SHALL continuar invocando IsBrandsValid com os mesmos parâmetros (ProductId distintos + 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:

  1. WHEN os testes de SaleEcommerceBrandsValidHandler forem executados THEN todos os cenários existentes SHALL continuar passando sem alteração de comportamento esperado.
  2. WHEN forem criados testes para ProductRepository.IsBrandsValid THEN o projeto Cart.UnitTests SHALL cobrir: marca única válida, combinação AREZZO+BRIZZA válida, combinação inválida, lista sem resultados no banco, schemaName vazio (guard 2d), e produto parcialmente encontrado (decisão 2c).
  3. WHEN os testes de IsBrandsValid forem executados THEN eles SHALL verificar que GetConnectionAsync(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:

  1. WHEN a refatoração for concluída THEN IServiceProvider SHALL ser removido do construtor do ProductRepository (decisão 5).
  2. WHEN a refatoração for concluída THEN IConfiguration SHALL ser removido do construtor do ProductRepository (decisão 5).
  3. WHEN os testes que instanciam ProductRepository manualmente forem atualizados THEN eles SHALL compilar sem os mocks de IServiceProvider / 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 ProductId solicitados e registros encontrados em Products
  • Refatoração de outros métodos do ProductRepository que usam _contextProviderRead
  • Mudanças no pipeline de handlers do CreateCart alé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ênciaDescriçãoStatus
coezzion-service-cartRepositório alvo da refatoração (ProductRepository)Disponível
IDbConnectionFactoryEConnectionType.ReadOnly + EDatabaseType.CoreDbDisponível
DbCore / tabela Products{schemaName}."Products" — coluna BrandDisponível
SaleEcommerceBrandsValidHandlerConsumidor principal no pipeline CreateCartDisponível
CartService (Assistant)Segundo ponto de chamada de IsBrandsValidDisponível
Task #197343 — [QA] ValidaçãoValidação funcional pós-implementaçãoPendente