Buscar/Criar módulos .NET com design orientado a domínio (DDD) sem quebrar camadas nem APIs públicas

Criar módulos .NET com design orientado a domínio (DDD) sem quebrar camadas nem APIs públicas

Use quando uma IA alterar um app .NET modular com design orientado a domínio. Ela segue os padrões existentes, mantém as camadas em um só sentido, coloca agregados, repositórios e DTOs na camada certa e testa em todos os bancos de dados sem afetar quem já usa.

Para usar estas regras de IA
## Seguir os padrões existentes e manter as dependências entre camadas em um só sentido Antes de criar código, procure uma implementação comparável no repositório. Siga a estrutura, a nomenclatura, as dependências, os testes e os padrões de extensão estabelecidos, a menos que a alteração solicitada os substitua explicitamente. Mantenha os diffs focados; evite formatação, refatoração ou atualização de dependências incidentais. Use camadas explícitas com dependências unidirecionais: - **Domain.Shared:** constantes, enums, recursos de localização, contratos de eventos e tipos compartilhados seguros para todas as camadas e clientes externos. - **Domain:** entidades, raízes de agregados, contratos de repositório, serviços de domínio e regras de negócio. - **Application.Contracts:** interfaces de serviços de aplicação e objetos de transferência de dados. - **Application:** implementações de serviços de aplicação e orquestração de casos de uso. - **Persistence integrations:** contextos, mapeamentos e implementações de repositório específicos do provedor que dependem do domínio. - **HTTP API:** adaptadores de transporte que dependem dos contratos de aplicação, não das implementações. - **Remote client:** proxies gerados ou escritos manualmente que dependem dos contratos de aplicação. - **Web UI:** código de apresentação que consome a HTTP API. Mantenha fora do domínio as questões de Web, transporte, contexto de autenticação e ORM. Mantenha a lógica de aplicação independente da camada Web. Trate uma referência proibida como defeito arquitetural, mesmo quando ela encurtar uma implementação. Preserve as APIs públicas e os limites dos pacotes. Não introduza uma alteração incompatível, a menos que ela seja explicitamente solicitada, justificada, migrada e coberta por testes de compatibilidade. ## Criar agregados válidos desde o início e levar regras entre entidades para serviços de domínio Crie entidades na camada de domínio e faça com que sejam válidas na construção. O construtor principal deve impor invariantes e inicializar todas as coleções próprias. Quando um ORM exigir, forneça um construtor protegido sem parâmetros, sem enfraquecer as regras normais de criação. Receba um identificador do chamador em vez de gerá-lo dentro da entidade. Proteja a consistência com setters não públicos e métodos de domínio que revelem sua intenção. Permita a sobrescrita de membros quando o ambiente de execução, o sistema de proxy ou o modelo de extensão aceito exigir. As raízes de agregados devem usar um identificador estável em vez de uma chave composta, permanecer pequenas e referenciar outras raízes pelo identificador, não por uma propriedade de navegação. Coloque em um serviço de domínio as regras entre entidades que pertençam ao modelo. Crie uma interface somente quando várias implementações, substituição ou isolamento de testes realmente exigirem. Use um sufixo claro orientado ao domínio, como `Manager`, aceite objetos de domínio e valores primitivos em vez de DTOs e lance exceções de negócio localizadas com códigos de erro exclusivos e com namespace quando as regras falharem. Os serviços de domínio podem realizar busca, normalização, cache ou resolução específicos do domínio, mas consultas simples de persistência pertencem aos repositórios. Não leia o usuário autenticado atual dentro do domínio; passe os valores necessários de ator ou tenant a partir da camada de aplicação. ## Criar um repositório por raiz de agregado, independente do banco de dados Defina os contratos de repositório na camada de domínio, com um contrato dedicado por raiz de agregado. Não crie repositórios para entidades internas pertencentes a um agregado. Mantenha os contratos públicos de repositório independentes de provedor. Prefira recursos básicos ou somente para leitura e não exponha aos consumidores do módulo uma abstração de provedor de consultas como `IQueryable`. As implementações de provedor podem usar internamente APIs de consulta mais completas. Torne assíncrona toda operação do repositório e aceite um token de cancelamento opcional. Em um método que retorna um agregado, ative por padrão o carregamento de detalhes quando os chamadores normalmente precisarem do agregado completo. Em métodos de lista, desative-o por padrão para evitar joins e transferências de dados sem limites. Use métodos significativos que retornem agregados em vez de classes de projection compostas e improvisadas. Adicione métodos específicos de projection somente quando requisitos de desempenho medidos os justificarem. Mantenha a lógica de eager loading em um helper `IncludeDetails` específico do provedor para que os serviços de aplicação não saibam como o armazenamento monta um agregado. Passe os tokens de cancelamento pelo helper de normalização do framework quando houver um. Garanta que os filtros de dados do provedor, a tenancy e as regras de soft-delete permaneçam ativos ao obter internamente um queryable. ## Projetar interfaces de serviços de aplicação com um DTO próprio por operação Coloque no pacote de contratos uma interface por serviço de aplicação. Use DTOs e valores primitivos no limite; nunca aceite nem retorne entidades de domínio. Mantenha os métodos assíncronos, use um sufixo `Async` consistente e evite repetir o nome da entidade quando o contexto do serviço já estiver claro. Use formatos CRUD previsíveis: os argumentos identificadores permanecem separados dos DTOs de atualização; as operações de criação e atualização retornam um DTO detalhado quando útil; exclusões não retornam payload; e consultas de lista contêm campos opcionais de filtragem, ordenação e paginação com um tamanho máximo de página seguro. Crie um DTO de entrada separado para cada operação. Não reutilize DTOs de entrada apenas porque os campos coincidem atualmente e evite herança entre eles. Mantenha os DTOs livres de lógica de negócio, valide-os no limite e reutilize constantes compartilhadas para os limites. Prefira um DTO básico de saída e uma variante detalhada a muitos formatos sobrepostos. As implementações de aplicação orquestram casos de uso. Elas devem usar repositórios dedicados, carregar os agregados necessários, chamar métodos de domínio e persistir explicitamente as atualizações, em vez de presumir o change tracking. Mantenha consultas de dados fora dos serviços de aplicação. Mapeie propriedades extensíveis pela convenção de mapper aceita. Não passe tipos Web, abstrações de upload nem streams por contratos de aplicação. Permita que os adaptadores de transporte convertam arquivos em uma representação estável do nível de aplicação. Evite chamar outro serviço de aplicação no mesmo módulo; mova regras reutilizáveis para o domínio ou para um helper compartilhado e focado. Chamadas entre módulos devem passar pelos contratos públicos do outro módulo. Permita sobrescrever operações públicas e helpers de extensão previstos. Prefira pontos de extensão protected a helpers private quando a personalização fizer parte do contrato do framework. ## Separar o código de cada banco de dados e manter cada módulo utilizável sozinho Forneça a cada módulo uma interface de contexto e uma implementação separadas para cada provedor de persistência aceito. Exponha sets ou coleções somente para raízes de agregados. Associe explicitamente o nome de conexão do módulo, mantenha configuráveis os prefixos de tabelas ou coleções e reserve os prefixos pertencentes ao framework. Não dependa de lazy loading. Configure os mapeamentos em uma extensão nomeada de model builder em vez de preencher o método do ciclo de vida do contexto com detalhes das entidades. Aplique o mapeamento convencional do framework a cada entidade antes das regras específicas do módulo. As implementações de repositório específicas do provedor devem herdar de sua base padrão, usar a interface de contexto como limite, passar corretamente os tokens de cancelamento e implementar o carregamento de detalhes do agregado da forma adequada ao provedor. Um armazenamento de documentos pode carregar naturalmente as coleções próprias, enquanto um provedor relacional exige includes explícitos; nenhuma diferença deve vazar para o domínio ou o contrato de aplicação. Cada pacote deve expor uma classe de composição do módulo com declarações explícitas de dependências. Registre serviços e options durante a configuração; use hooks de inicialização da aplicação somente para trabalhos que realmente precisem da aplicação em execução. Um módulo deve continuar utilizável de forma independente, sem acoplamento oculto a outro pacote. Quando um módulo aceitar vários provedores, execute o mesmo contrato de repositório e de comportamento para cada integração. A independência de provedor é uma propriedade testada, não uma convenção de nomenclatura. ## Oferecer pontos de extensão e descontinuar APIs antigas aos poucos, sem quebrá-las Crie membros públicos e protected para o modelo de extensão aceito pelo framework. Use métodos sobrescrevíveis, registros de serviços substituíveis, interfaces em pontos reais de substituição e objetos de options para configuração. Documente os pontos de extensão e as invariantes que as implementações personalizadas devem preservar. Não remova nem renomeie um membro público sem um ciclo de descontinuação. Marque o membro antigo como obsolete, com orientações claras de migração, adicione parâmetros opcionais com valores padrão e forneça implementações padrão para novos membros de classes base sempre que possível. Prefira adicionar uma nova interface a quebrar uma existente. Mantenha as strings voltadas ao usuário em recursos de localização pertencentes ao pacote de domínio compartilhado. Use chaves com namespace e erros de negócio localizados em vez de texto codificado. Defina nomes de configurações, recursos e permissões como constantes em sua camada responsável e organize-os em namespaces estáveis do módulo. Use um barramento de eventos em processo para comunicação dentro de um processo e um barramento de eventos distribuído entre módulos ou serviços. Coloque os contratos de eventos distribuídos no pacote de domínio compartilhado, mantenha-os simples e serializáveis e use valores primitivos ou contratos de eventos aninhados em vez de entidades de domínio. Trate os eventos de integração na camada de aplicação. Trate compatibilidade, localização e comportamento de extensão como superfícies públicas do produto. Cubra suas alterações com testes direcionados e notas de migração. ## Testar cada alteração em todos os bancos de dados suportados e revisar o diff final Antes de uma alteração arquitetural ou de contrato público significativa, alinhe a decisão pelo processo de design ou issue aprovado do projeto. Mantenha o pull request limitado e fácil de revisar e inclua os testes necessários para comprovar o comportamento afetado. Use testes unitários para regras de domínio isoladas e testes de integração para repositórios, mapeamentos, permissões, validação e composição do módulo. Execute todos os provedores de persistência aceitos quando o contrato declarar independência de provedor. Nomeie os testes conforme a operação, o cenário e o resultado esperado. Compare o diff final com esta lista de verificação: - A direção das dependências permanece intacta, sem questões de Web ou persistência vazando para dentro. - As entidades impõem invariantes, as coleções próprias estão inicializadas e as referências a agregados usam identificadores. - Os contratos de repositório continuam assíncronos, canceláveis, centrados em agregados e independentes de provedor. - Os contratos de aplicação usam DTOs mínimos e validados e formatos de métodos estáveis. - Os serviços de aplicação delegam consultas a repositórios e mutações a métodos de domínio. - As integrações relacionais e de documentos seguem suas próprias convenções de mapeamento e carregamento de detalhes. - As APIs públicas, chaves de localização, configurações, recursos, permissões e eventos continuam compatíveis. - Os testes cobrem permissões, validação, casos de falha, diferenças entre provedores e condições de borda. - O diff não contém refatoração não relacionada, edição de arquivo gerado nem superfície de API desnecessária. Relate as verificações direcionadas executadas e qualquer caminho de provedor ou compatibilidade que permaneça sem verificação. Não alegue conformidade arquitetural apenas com base na compilação.