Projete um microsserviço orientado a DDD
Gorjeta
Este conteúdo é um trecho do eBook, .NET Microservices Architecture for Containerized .NET Applications, disponível no .NET Docs ou como um PDF para download gratuito que pode ser lido offline.
O design orientado a domínio (DDD) defende a modelagem baseada na realidade dos negócios como relevante para seus casos de uso. No contexto da construção de aplicações, o DDD fala de problemas como domínios. Ele descreve áreas problemáticas independentes como Contextos Delimitados (cada Contexto Delimitado se correlaciona a um microsserviço) e enfatiza uma linguagem comum para falar sobre esses problemas. Ele também sugere muitos conceitos e padrões técnicos, como entidades de domínio com modelos avançados (sem modelo de domínio anêmico), objetos de valor, agregações e regras de raiz agregada (ou entidade raiz) para dar suporte à implementação interna. Esta seção apresenta o design e a implementação desses padrões internos.
Às vezes, essas regras e padrões técnicos de DDD são percebidos como obstáculos que têm uma curva de aprendizado íngreme para a implementação de abordagens DDD. Mas a parte importante não são os padrões em si, mas organizar o código para que ele esteja alinhado aos problemas de negócios, e usando os mesmos termos de negócios (linguagem ubíqua). Além disso, as abordagens DDD devem ser aplicadas somente se você estiver implementando microsserviços complexos com regras de negócios significativas. Responsabilidades mais simples, como um serviço CRUD, podem ser gerenciadas com abordagens mais simples.
Onde traçar os limites é a tarefa chave ao projetar e definir um microsserviço. Os padrões DDD ajudam-no a compreender a complexidade do domínio. Para o modelo de domínio para cada Contexto Delimitado, você identifica e define as entidades, objetos de valor e agregações que modelam seu domínio. Você cria e refina um modelo de domínio que está contido dentro de um limite que define seu contexto. E isso é explícito na forma de um microsserviço. Os componentes dentro desses limites acabam sendo seus microsserviços, embora em alguns casos um BC ou microsserviços de negócios possam ser compostos por vários serviços físicos. DDD é sobre limites e microsserviços também.
Mantenha os limites de contexto do microsserviço relativamente pequenos
Determinar onde colocar limites entre Contextos Delimitados equilibra dois objetivos concorrentes. Primeiro, você deseja criar inicialmente os menores microsserviços possíveis, embora isso não deva ser o driver principal; Você deve criar um limite em torno de coisas que precisam de coesão. Em segundo lugar, você quer evitar comunicações tagarelas entre microsserviços. Estes objetivos podem contradizer-se. Você deve equilibrá-los decompondo o sistema em tantos microsserviços pequenos quanto puder até ver os limites de comunicação crescendo rapidamente a cada tentativa adicional de separar um novo Contexto Delimitado. A coesão é fundamental dentro de um contexto único.
É semelhante ao cheiro do código de intimidade inadequada ao implementar aulas. Se dois microsserviços precisam colaborar muito um com o outro, eles provavelmente devem ser o mesmo microsserviço.
Outra forma de olhar para este aspeto é a autonomia. Se um microsserviço precisa depender de outro serviço para atender diretamente uma solicitação, ele não é verdadeiramente autônomo.
Camadas em microsserviços DDD
A maioria dos aplicativos corporativos com complexidade técnica e de negócios significativa é definida por várias camadas. As camadas são um artefato lógico e não estão relacionadas à implantação do serviço. Eles existem para ajudar os desenvolvedores a gerenciar a complexidade no código. Diferentes camadas (como a camada de modelo de domínio versus a camada de apresentação, etc.) podem ter tipos diferentes, o que exige traduções entre esses tipos.
Por exemplo, uma entidade pode ser carregada do banco de dados. Em seguida, parte dessas informações, ou uma agregação de informações, incluindo dados adicionais de outras entidades, pode ser enviada para a interface do usuário do cliente por meio de uma API Web REST. O ponto aqui é que a entidade de domínio está contida na camada de modelo de domínio e não deve ser propagada para outras áreas às quais não pertence, como a camada de apresentação.
Além disso, você precisa ter entidades sempre válidas (consulte a seção Projetando validações na camada de modelo de domínio) controladas por raízes agregadas (entidades raiz). Portanto, as entidades não devem ser vinculadas a exibições de cliente, porque no nível da interface do usuário alguns dados ainda podem não ser validados. É para isso que serve o ViewModel. O ViewModel é um modelo de dados exclusivamente para as necessidades da camada de apresentação. As entidades de domínio não pertencem diretamente ao ViewModel. Em vez disso, você precisa traduzir entre ViewModels e entidades de domínio e vice-versa.
Ao lidar com a complexidade, é importante ter um modelo de domínio controlado por raízes agregadas que garantam que todas as invariantes e regras relacionadas a esse grupo de entidades (agregação) sejam executadas através de um único ponto de entrada ou porta, a raiz agregada.
A Figura 7-5 mostra como um design em camadas é implementado no aplicativo eShopOnContainers.
Figura 7-5. Camadas DDD no microsserviço de pedidos no eShopOnContainers
As três camadas em um microsserviço DDD como o Pedido. Cada camada é um projeto VS: a camada Application é Ordering.API, a camada Domain é Ordering.Domain e a camada Infrastructure é Ordering.Infrastructure. Você deseja projetar o sistema para que cada camada se comunique apenas com determinadas outras camadas. Essa abordagem pode ser mais fácil de impor se as camadas forem implementadas como bibliotecas de classes diferentes, porque você pode identificar claramente quais dependências são definidas entre bibliotecas. Por exemplo, a camada de modelo de domínio não deve depender de nenhuma outra camada (as classes de modelo de domínio devem ser classes Plain Old Class Objects, ou POCO). Como mostra a Figura 7-6, a biblioteca de camadas Ordering.Domain tem dependências apenas nas bibliotecas .NET ou pacotes NuGet, mas não em qualquer outra biblioteca personalizada, como biblioteca de dados ou biblioteca de persistência.
Figura 7-6. Camadas implementadas como bibliotecas permitem um melhor controle das dependências entre camadas
A camada do modelo de domínio
O excelente livro de Eric Evans, Domain Driven Design , diz o seguinte sobre a camada de modelo de domínio e a camada de aplicação.
Camada de Modelo de Domínio: Responsável por representar conceitos do negócio, informações sobre a situação do negócio e regras de negócio. O estado que reflete a situação do negócio é controlado e usado aqui, embora os detalhes técnicos de armazená-lo sejam delegados à infraestrutura. Esta camada é o coração do software de negócios.
A camada de modelo de domínio é onde o negócio é expresso. Quando você implementa uma camada de modelo de domínio de microsserviço no .NET, essa camada é codificada como uma biblioteca de classes com as entidades de domínio que capturam dados mais comportamento (métodos com lógica).
Seguindo os princípios de Ignorância de Persistência e Ignorância de Infraestrutura , essa camada deve ignorar completamente os detalhes de persistência de dados. Essas tarefas de persistência devem ser executadas pela camada de infraestrutura. Portanto, essa camada não deve ter dependências diretas da infraestrutura, o que significa que uma regra importante é que suas classes de entidade de modelo de domínio devem ser POCOs.
As entidades de domínio não devem ter nenhuma dependência direta (como derivar de uma classe base) em qualquer estrutura de infraestrutura de acesso a dados, como Entity Framework ou NHibernate. Idealmente, suas entidades de domínio não devem derivar ou implementar qualquer tipo definido em qualquer estrutura de infraestrutura.
A maioria das estruturas ORM modernas, como o Entity Framework Core, permite essa abordagem, para que suas classes de modelo de domínio não sejam acopladas à infraestrutura. No entanto, nem sempre é possível ter entidades POCO ao usar determinados bancos de dados e estruturas NoSQL, como Atores e Coleções Confiáveis no Azure Service Fabric.
Mesmo quando é importante seguir o princípio de Ignorância de Persistência para o seu modelo de Domínio, você não deve ignorar as preocupações de persistência. Ainda é importante entender o modelo de dados físicos e como ele é mapeado para seu modelo de objeto de entidade. Caso contrário, você pode criar designs impossíveis.
Além disso, esse aspeto não significa que você pode pegar um modelo projetado para um banco de dados relacional e movê-lo diretamente para um banco de dados NoSQL ou orientado a documentos. Em alguns modelos de entidade, o modelo pode se encaixar, mas geralmente não se encaixa. Ainda há restrições às quais seu modelo de entidade deve aderir, com base na tecnologia de armazenamento e na tecnologia ORM.
A camada de aplicação
Passando para a camada de aplicação, podemos novamente citar o livro de Eric Evans Domain Driven Design:
Camada de Aplicação: Define os trabalhos que o software deve fazer e direciona os objetos de domínio expressivos para resolver problemas. As tarefas pelas quais essa camada é responsável são significativas para o negócio ou necessárias para a interação com as camadas de aplicativos de outros sistemas. Esta camada é mantida fina. Ele não contém regras de negócios ou conhecimento, mas apenas coordena tarefas e delega trabalho a colaborações de objetos de domínio na próxima camada abaixo. Ele não tem estado refletindo a situação de negócios, mas pode ter estado que reflete o progresso de uma tarefa para o usuário ou o programa.
A camada de aplicativo de um microsserviço no .NET é normalmente codificada como um projeto ASP.NET Core Web API. O projeto implementa a interação do microsserviço, o acesso remoto à rede e as APIs Web externas usadas a partir da interface do usuário ou dos aplicativos cliente. Ele inclui consultas se estiver usando uma abordagem CQRS, comandos aceitos pelo microsserviço e até mesmo a comunicação orientada a eventos entre microsserviços (eventos de integração). O ASP.NET Core Web API que representa a camada de aplicativo não deve conter regras de negócios ou conhecimento de domínio (especialmente regras de domínio para transações ou atualizações); eles devem pertencer à biblioteca de classes do modelo de domínio. A camada de aplicativo deve apenas coordenar tarefas e não deve manter ou definir qualquer estado de domínio (modelo de domínio). Ele delega a execução de regras de negócios às próprias classes de modelo de domínio (raízes agregadas e entidades de domínio), que em última análise atualizarão os dados dentro dessas entidades de domínio.
Basicamente, a lógica do aplicativo é onde você implementa todos os casos de uso que dependem de um determinado front-end. Por exemplo, a implementação relacionada a um serviço de API da Web.
O objetivo é que a lógica de domínio na camada de modelo de domínio, seus invariantes, o modelo de dados e as regras de negócios relacionadas sejam completamente independentes das camadas de apresentação e aplicativo. Acima de tudo, a camada de modelo de domínio não deve depender diretamente de qualquer estrutura de infraestrutura.
A camada de infraestrutura
A camada de infraestrutura é como os dados inicialmente mantidos em entidades de domínio (na memória) são mantidos em bancos de dados ou outro armazenamento persistente. Um exemplo é usar o código Entity Framework Core para implementar as classes de padrão Repository que usam um DBContext para persistir dados em um banco de dados relacional.
De acordo com os princípios de Ignorância de Persistência e Ignorância de Infraestrutura mencionados anteriormente, a camada de infraestrutura não deve "contaminar" a camada de modelo de domínio. Você deve manter as classes de entidade do modelo de domínio agnósticas da infraestrutura que você usa para persistir dados (EF ou qualquer outra estrutura) sem depender fisicamente das estruturas. Sua biblioteca de classes de camada de modelo de domínio deve ter apenas seu código de domínio, apenas classes de entidade POCO implementando o coração do seu software e completamente dissociadas das tecnologias de infraestrutura.
Assim, suas camadas ou bibliotecas de classes e projetos devem, em última análise, depender da camada do modelo de domínio (biblioteca), e não vice-versa, como mostra a Figura 7-7.
Figura 7-7. Dependências entre camadas no DDD
Dependências em um Serviço DDD, a camada Aplicativo depende de Domínio e Infraestrutura e Infraestrutura depende de Domínio, mas Domínio não depende de nenhuma camada. Esse design de camada deve ser independente para cada microsserviço. Como observado anteriormente, você pode implementar os microsserviços mais complexos seguindo padrões DDD, enquanto implementa microsserviços orientados por dados mais simples (CRUD simples em uma única camada) de uma maneira mais simples.
Recursos adicionais
DevIQ. Princípio da ignorância da persistência
https://deviq.com/persistence-ignorance/Oren Eini. Ignorância da infraestrutura
https://ayende.com/blog/3137/infrastructure-ignoranceAnjo Lopez. Arquitetura em camadas no design controlado por domínio
https://ajlopez.wordpress.com/2008/09/12/layered-architecture-in-domain-driven-design/