
Por que os DDDs geralmente são abordados do lado errado? E de que lado você quer? O que girafas e ornitorrincos têm a ver com tudo isso?
Especialmente para Habr - uma transcrição em texto do relatório "Design orientado a domínio: uma receita para um pragmático". O relatório foi feito na conferência DotNext .NET, mas pode ser útil não apenas para doadores, mas para todos os interessados em DDD (acreditamos que você domine alguns exemplos de código C #). Uma gravação em vídeo do relatório também é anexada.
Olá pessoal, meu nome é Alexey Merson. Vou lhe dizer o que é Design Orientado a Domínio e qual é sua essência, mas primeiro, vamos descobrir por que ele é necessário.

Martin Fowler disse: "Existem poucas coisas que são menos lógicas que a lógica de negócios". A girafa é definitivamente uma dessas poucas. A distância entre o cérebro e a laringe de uma girafa é de apenas alguns centímetros. No entanto, o nervo que os liga atinge 4 metros. Primeiro, ele desce pelo pescoço todo, ali passa pela artéria e depois volta quase da mesma maneira.
À primeira vista, realmente não há lógica. Mas este é apenas um denso legado deixado por peixes antigos. Nos peixes, como você sabe, não há pescoço, então esse nervo percorre o caminho ideal. E quando os mamíferos apareceram após vários milhões de anos de refatoração, o nervo teve que ser estendido para manter a compatibilidade com versões anteriores. Bem, não remodele por causa de alguma girafa?
Mas a girafa está bem, porque há um ornitorrinco.

Pense sobre isso. O mamífero Com bico. Vive principalmente na água. Põe ovos. E além disso, venenoso. Pode parecer que a única explicação lógica para sua existência é que ele é da Austrália.
Mas acho que tudo é mais banal. O contratado simplesmente esqueceu o design e economizou com o StackOverflow, bem, ou o que havia naquele tempo.

Sei o que você está pensando agora: "Alexey, bem, você nos prometeu um Design Orientado a Domínios, e aqui está um tipo de" No mundo animal "!"
Colegas, o que é desenvolvimento? Desenvolvimento é quando pegamos parte do mundo real, um processo de negócios, e o transformamos em código, ou seja, construímos um modelo de software. Que problemas nos aguardam ao longo do caminho?
A primeira é a complexidade dos próprios processos de negócios, ou seja, a dificuldade de entender como os negócios funcionam, quais processos estão ocorrendo lá, por que lógica eles são construídos.
O segundo problema é a implementação desses processos de negócios na forma de código, o uso dos padrões corretos, as abordagens corretas e assim por diante. Este também é um tópico bastante complicado.

Veja, os processos de negócios são como aquela girafa: eles começaram com os mais simples unicelulares, e então "ele" olha para você, e ninguém entende "de onde veio" ou "como funciona".
Para construir um modelo bem-sucedido desse processo, você deve primeiro responder à pergunta "por quê?". Por que queremos construir esse modelo? Quais objetivos queremos alcançar? Afinal, se o cliente quisesse uma girafa empalhada, mas tivesse coragem, ele ficará chateado, mesmo que a digestão neste modelo seja implementada para um banquete para os olhos. E o cliente perderá não apenas dinheiro e tempo, ele perderá a confiança em nós como desenvolvedores e perderemos nossa reputação e cliente.
Mas mesmo se descobrimos os objetivos, isso ainda não garante que não obteremos o ornitorrinco como resultado. O fato é que o objetivo é pouco para entender. O objetivo deve ser alcançado. E isso nos ajuda a um design orientado a domínio.

O principal objetivo do Design Orientado a Domínio é combater a complexidade dos processos de negócios e sua automação e implementação em código. "Domínio" é traduzido como "domínio", e o desenvolvimento e o design, dentro da estrutura dessa abordagem, são afastados do domínio.
O Design Orientado a Domínio inclui muitas coisas. Esse design estratégico, a interação entre as pessoas, as abordagens da arquitetura e os padrões táticos - esse é um arsenal inteiro que realmente funciona e realmente ajuda a criar projetos. Existe apenas um "mas". Antes de começar a lidar com a complexidade do Design Orientado a Domínio, você precisa aprender a lidar com a complexidade do próprio Design Orientado a Domínio.

Quando uma pessoa começa a mergulhar nesse tópico, uma enorme quantidade de informação cai sobre ele: livros grossos, um monte de artigos, padrões, exemplos. Tudo isso é confuso, e é fácil, como eles dizem, não notar atrás das árvores da floresta. Certa vez, senti isso comigo mesmo, mas hoje quero compartilhar minha experiência com você e ajudá-lo a atravessar essa selva, finalmente começando a usar o Design Orientado a Domínio.

O termo Design Orientado a Domínio foi proposto por Eric Evans em 2003 em seu livro impronunciável, que é simplesmente chamado de Livro Azul na comunidade. O problema é que a primeira metade do livro Evans fala sobre padrões táticos (todos vocês os conhecem: são fábricas, entidades, repositório, serviços), e as pessoas geralmente não chegam à segunda metade. O homem parece: tudo é familiar, eu vou buscar o aplicativo DDD.

À direita está o que acontece se você lançar loucamente padrões táticos no compilador. Esquerda - se você usar padrões estratégicos.

Desde o lançamento do Blue Book, uma comunidade DDD bastante forte se formou, muitas coisas foram repensadas. Sim, e o próprio Evans admitiu que não entende mais como poderia pôr fim a uma coisa tão importante quanto o design estratégico.
E 10 anos depois, em 2013, o Red Book foi publicado por Vaughn Vernon. E neste livro, a apresentação já foi criada na ordem correta: começa com o design estratégico, com o básico. E quando o leitor recebe a base necessária, ele já começa a falar sobre padrões táticos e detalhes de implementação.
Geralmente, nos relatórios sobre DDD, eles recomendam a leitura de Evans; na Internet, existem até manuais inteiros em que ordem é necessário ler os capítulos para uma imersão adequada. Eu recomendo que seja mais fácil: comece com o Red Book, leia-o e só então passe para o Blue.
E como o design estratégico é uma coisa tão importante, vamos falar sobre suas ideias principais.
"Ideias-chave do design estratégico"
Em qualquer projeto de automação comercial, sempre há especialistas em domínio. São pessoas que entendem melhor como funcionam os processos de negócios a serem modelados. Estes podem ser desenvolvedores líderes, executivos, gerentes de topo. Em geral, pode ser qualquer um, se ele entender os processos de negócios com os quais precisamos lidar.

Por outro lado, existem especialistas técnicos: desenvolvedores, arquitetos que estão diretamente envolvidos na automação e implementação de aplicativos. No exemplo mostrado, o cliente provavelmente queria uma ferrovia infantil, mas acabou sendo uma espécie de monstro.
Por que isso está acontecendo? Como a interação entre especialistas técnicos e especialistas em domínio em uma situação típica é mais ou menos assim: existe um grande muro entre eles, e um gerente caminha ao longo do topo desse muro e primeiro tenta ouvir o que eles estão gritando em um lado do muro, depois tenta gritar com o melhor dos ligamentos. do outro lado da parede e assim por diante em um círculo.
Às vezes, um gerente é surdo, então toda uma cadeia de gerentes pode ser construída, o que, é claro, não contribui para o sucesso do projeto. E como deveria ser?

Deve haver interação constante. Especialistas técnicos, especialistas em domínio - todos os participantes do projeto devem manter constantemente a comunicação, sincronizar, discutir metas, maneiras de alcançá-las e por que fazemos tudo isso.
E aqui chegamos ao primeiro e, provavelmente, o ponto chave mais importante do design estratégico e do Design Orientado a Domínios em geral.

A comunicação entre os participantes do projeto forma o que o Design Orientado a Domínio chama de linguagem onipresente. Ele não é um no sentido de ser um para todas as ocasiões. Exatamente o oposto. É único no sentido em que todos os participantes se comunicam nele, toda a discussão ocorre em termos de um único idioma e todos os artefatos devem estar no máximo em termos de um único idioma, ou seja, iniciando no TK e terminando com um código.
Cenários de negócios
Para uma discussão mais aprofundada, precisamos de algum tipo de cenário comercial. Vamos imaginar esta situação:

O diretor do grupo JUG.ru chega até nós e diz: “Pessoal, o fluxo de relatórios está crescendo, as pessoas, em geral, são torturadas para fazer tudo manualmente ... Vamos automatizar o processo de preparação da conferência.” Nós respondemos: "Ok!" - e comece a trabalhar.
O primeiro cenário que automatizaremos é: "O palestrante envia uma solicitação de relatório em um evento específico e adiciona informações sobre o relatório". O que vemos neste cenário? O que é um palestrante, há um evento e há um relatório, o que significa que já é possível criar o primeiro modelo de domínio.

Aqui temos um modelo de domínio: Speaker - speaker, Talk - report, Event - event. Mas o modelo de domínio não pode ser ilimitado, não pode abranger tudo; caso contrário, ficará embaçado e perderá o foco; portanto, o modelo de domínio deve ser limitado por algo. Este é o próximo ponto-chave.

O modelo de domínio e a linguagem onipresente são limitados pelo contexto que o Design Orientado a Domínio chama de contexto limitado. Ele restringe o modelo de domínio de forma que todos os conceitos contidos nele sejam inequívocos e todos entendam o que está em jogo.
Se eles dizem "Usuário", tudo deve ficar claro de uma só vez, deve ter um papel compreensível, um significado compreensível, não deve ser algum tipo de usuário abstrato do ponto de vista da indústria de TI.

No nosso caso, esse modelo de domínio é válido para o contexto da preparação da conferência; portanto, é no contexto que chamaremos de "contexto de planejamento de eventos". Mas, para o palestrante adicionar algo, alterar informações, ele deve, de alguma forma, fazer login, ele precisa receber alguns direitos. E esse já será outro contexto, "contexto de identidade", no qual haverá algum tipo de entidade própria: usuário, função, perfil.
E veja qual é a coisa aqui. Quando uma pessoa efetua login no sistema e pretende inserir algum tipo de informação, fisicamente é a mesma pessoa, mas em contextos diferentes, ela é representada por entidades diferentes, e essas entidades não estão diretamente relacionadas.
Se pegarmos e, por exemplo, herdarmos Speaker do User, misturaremos coisas que não podem ser misturadas e alguns atributos poderão ser misturados pela lógica. E o modelo perderia o foco no significado específico que possui, sendo dividido em vários contextos.
Demo: serviço de vendas
Vamos discordar um pouco da teoria seca e ver o código.
Uma conferência não é apenas a preparação do conteúdo, mas também as vendas. Vamos imaginar que um serviço de venda de ingressos já tenha sido gravado e um gerente de vendas venha até nós e diga: “Gente! Depois que alguém escreveu esse serviço, vamos descobrir, algo não está claro para mim como o desconto para clientes regulares é considerado. ”
Após conversar com o gerente, descobrimos que todo o cenário desse serviço é o seguinte: ao clicar em Checkout, o preço final do ingresso é considerado, levando em consideração o desconto regular do cliente, e o pedido entra no estado “Aguardando pagamento”.
O código que analisaremos agora pode ser exibido separadamente no
repositório .
Solução aberta, observe a estrutura:

Parece que tudo parece bom: há Application e Core (aparentemente, as pessoas sabem sobre camadas), Repositório ... Aparentemente, a pessoa dominou a primeira metade de Evans.
Abra OrderCheckoutService. O que vemos lá? Aqui
está o código :
public void Checkout(long id) { var ord = _ordersRepository.GetOrder(id); var orders = _ordersRepository.GetOrders() .Count(o => o.CustomerId == ord.CustomerId && o.StateId == 3 && o.OrderDate >= DateTime.UtcNow.AddYears(-3)); ord.Price *= (100 - (orders >= 5 ? 30m : orders >= 3 ? 20m : orders >= 1 ? 10m : 0)) / 100; ord.StateId = 1; _ordersRepository.SaveOrder(ord); }
Observamos a linha com preço: aqui o preço muda. Chamamos nosso gerente de vendas e dizemos: “Aqui, em resumo, o desconto é considerado aqui, tudo está claro”:
ord.Price *= (100 - (orders >= 5 ? 30m : orders >= 3 ? 20m : orders >= 1 ? 10m : 0)) / 100;
Ele olha por cima do ombro: “Oh! Então é assim que o Brainfuck se parece! E eles meio que me disseram que os caras estão escrevendo em C # ”.
Obviamente, o desenvolvedor deste código respondeu bem a uma entrevista sobre algoritmos e estruturas de dados. Escrevi nas olimpíadas da escola quase no mesmo estilo. Depois de algum tempo, usando a formatação e a refatoração
obscena , descobrimos o que é o quê e explicamos ao nosso gerente de vendas que a lógica é a seguinte: se o número de pedidos nos últimos 3 anos não for menor que um, ele receberá 10% de desconto , não inferior a três - 20% e não inferior a cinco - 30%. Ele sai alegre - agora está claro como tudo funciona.
Acho que muitos leram o Código Limpo de Bob Martin. Lá, ele diz sobre a regra dos escoteiros: "O estacionamento depois de sairmos deve ser mais limpo do que era antes de chegarmos lá". Portanto, vamos refatorar esse código para que pareça humano e corresponda ao que falamos um pouco antes sobre linguagem onipresente e seu uso no código.
Aqui está o código refatorado.
public class DiscountCalculator { private readonly IOrdersRepository _ordersRepository; public DiscountCalculator(IOrdersRepository ordersRepository) { _ordersRepository = ordersRepository; } public decimal CalculateDiscountBy(long customerId) { var completedOrdersCount = _ordersRepository.GetLast3YearsCompletedOrdersCountFor(customerId); return DiscountBy(completedOrdersCount); } private decimal DiscountBy(int completedOrdersCount) { if (completedOrdersCount >= 5) return 30; if (completedOrdersCount >= 3) return 20; if (completedOrdersCount >= 1) return 10; return 0; } }
A primeira coisa que fazemos é transferir o cálculo do desconto para um DiscountCalculator separado, no qual o método CalculateDiscountBy customerId aparece. Tudo é lido humanamente, tudo é claro: o que, por que e como. Dentro desse método, vemos que temos duas etapas globais para calcular o desconto. Primeiro: obtemos algo do repositório de pedidos, tudo de acordo com o caso do usuário, você nem precisa entrar, se essa não é a parte que lhe interessa agora. O fato é que obtemos o número de alguns pedidos concluídos, após os quais consideramos imediatamente o segundo desconto dessa quantidade como a segunda etapa.
Se queremos ver como isso é considerado, vamos ao DiscountBy, e aqui quase a mesma coisa está escrita em inglês quase humano do que nosso "tipo de cérebro" antes, tudo é claro e preciso.
A única questão que pode surgir é em que unidades o desconto é medido. Seria possível adicionar a palavra "porcentagem" no nome do método, para deixar claro, mas, a partir do contexto e das figuras envolvidas, provavelmente supõe-se que essas são porcentagens e, por brevidade, pode ser omitida. Se quisermos ver qual era o número de pedidos, iremos ao código do Repositório e veremos. Agora não faremos isso. Em nosso serviço, precisamos adicionar uma nova dependência DiscountCalculator. E vamos ver o que acabamos na segunda versão do método Checkout.
public void CheckoutV2(long orderId) { var order = _ordersRepository.GetOrder(orderId); var discount = _discountCalculator.CalculateDiscountBy(order.CustomerId); order.ApplyDiscount(discount); order.State = OrderState.AwaitingPayment; _ordersRepository.SaveOrder(order); }
Veja, o método Checkout recebe orderId e, em seguida, recebe um orderId por orderId, de acordo com o CustomerId desse pedido, ele considera o desconto usando a calculadora de descontos, aplica o desconto ao pedido, define o status como AwaitingPayment e salva o pedido. Tínhamos um script em russo no slide, mas aqui praticamente lemos a tradução desse script para o inglês e tudo está claro, tudo é óbvio.
Você vê qual é o charme? Esse código pode ser mostrado a qualquer pessoa: não apenas programadores, mas também QA, analistas e clientes. Todos eles entenderão o que está acontecendo, porque tudo está escrito em linguagem humana. Eu uso isso em nosso projeto, realmente o controle de qualidade pode analisar algumas partes, verificar com o Wiki e entender que há algum tipo de erro. Porque o Wiki diz isso, e o código é um pouco diferente, mas ele entende o que está acontecendo lá, embora ele não seja um desenvolvedor. E da mesma maneira, podemos discutir o código com o analista e discuti-lo em detalhes. Eu digo: "Veja, é assim que funciona no código". Nosso último recurso não é o Wiki, mas o código. Tudo funciona como está escrito no código. É muito importante usar linguagem onipresente ao escrever código.

Este é o terceiro ponto-chave.
Há muita confusão sobre o Design Orientado a Domínio em coisas como Domínio, Subdomínio, Contexto Limitado, como elas se relacionam com o que significam. Parece que todo mundo está limitando alguma coisa, todos estão de alguma forma arrumados. Mas não está claro então qual é a diferença, por que elas são tão diferentes inventadas.

Domínio é uma coisa global, é uma área de assunto global em que esse negócio em particular ganha dinheiro. Por exemplo, para DotNext, é uma conferência; para Pyaterochka, é uma venda a varejo de mercadorias.
Grandes corporações podem ter vários domínios. Por exemplo, a Amazon está envolvida na venda de mercadorias via Internet e na prestação de serviços em nuvem, essas são áreas diferentes.
No entanto, é algo global e não pode ser automatizado diretamente, mesmo para investigar é difícil. Para análise, o domínio é inevitavelmente dividido em subdomínios, ou seja, em subdomínios.

Subdomínios são partes de uma empresa que, em nossa linguagem, são altamente conectadas, ou seja, são algum tipo de processo lógico isolado que interage entre si em algum nível principal.
Por exemplo, se pegarmos uma loja on-line, será a formação e o processamento de pedidos, a entrega, o trabalho com fornecedores, o marketing, a contabilidade. Aqui estão algumas dessas peças - é nisso que o negócio está dividido.

Do ponto de vista do DDD, os subdomínios são divididos em três tipos. E aqui quero dizer mais uma coisa: geralmente em livros e artigos, o subdomínio é simplesmente reduzido a domínio, mas geralmente no caso em que é combinado com o tipo de subdomínio. Ou seja, quando eles dizem "Domínio principal", eles significam Subdomínio principal, por favor, não se confunda com isso. Isso me impressionou a princípio.
Subdomínios são divididos em três tipos.

O primeiro e mais importante é o Core. O núcleo é o principal subdomínio, é a vantagem competitiva da empresa, o que faz com que a empresa ganhe dinheiro, como ela difere de seus concorrentes, seu know-how, como você chama. Se fizermos a conferência DotNext, esse é o conteúdo. Vocês todos vieram aqui em busca de conteúdo, se não houvesse esse conteúdo aqui, não iriam ou participariam de outra conferência. Não haveria DotNext no formato em que está.

O segundo tipo é Subdomínio de suporte. Isso também é importante para ganhar dinheiro, também é algo sem o qual é impossível, mas não é algum tipo de know-how, uma vantagem competitiva real. É isso que o Subdomínio Principal suporta. Do ponto de vista da aplicação do Design Orientado a Domínio, isso significa que menos esforço é gasto no Subdomínio de Suporte, todas as principais forças são lançadas no Core.
Um exemplo para o mesmo DotNext é o marketing. É impossível sem marketing, caso contrário, ninguém saberia sobre a conferência, mas sem marketing de conteúdo não é necessário.

E, finalmente, o subdomínio genérico. Genérico é uma tarefa comercial típica, que, como regra, pode ser automatizada com produtos acabados ou terceirizada. Isso é o que também é necessário, mas não requer necessariamente uma implementação independente por nós e, mais do que isso, geralmente será uma boa ideia usar um produto de terceiros.
Por exemplo, vendendo ingressos. DotNext vende bilhetes através do TimePad. Esse subdomínio é perfeitamente automatizado pelo TimePad, e você não precisa escrever um segundo TimePad.

E, finalmente, contexto limitado. O contexto limitado e o subdomínio estão sempre em algum lugar próximo, mas há uma diferença significativa entre eles. Isso é muito importante.

Há uma pergunta no StackExchange sobre como o contexto delimitado difere do Subdomínio. Subdomínio é um pedaço de negócio, um pedaço do mundo real, é o conceito de um espaço de declaração de problemas. O contexto limitado limita o modelo de domínio e a linguagem onipresente, ou seja, qual é o resultado da modelagem e, consequentemente, o contexto limitado é o conceito de espaço de solução. No processo de implementação do projeto, um tipo de mapeamento de Subdomínios ocorre em contextos limitados.

Um exemplo clássico: a contabilidade como um subdomínio, como o processo é mapeado, é automatizada, por exemplo, 1C Bookkeeping, Elba ou "My business" - de alguma forma é automatizada por algum produto. Esse é o contexto limitado da contabilidade, no qual há sua linguagem onipresente, sua própria terminologia. Essa é a diferença entre eles.

Se retornarmos ao DotNext, como eu disse, os tickets serão mapeados para o TimePad e o conteúdo do nosso Subdomínio Principal será mapeado para um aplicativo personalizado que estamos desenvolvendo para o gerenciamento de conteúdo.
Tamanho do contexto limitado
Há um momento que levanta muitas questões. Como escolher o tamanho certo para o contexto delimitado? Nos livros, pode-se encontrar essa definição: "O contexto limitado deve ser exatamente tal que a linguagem onipresente seja completa, consistente, inequívoca, inequívoca, consistente". Definição legal, no estilo de um matemático de uma piada famosa: muito precisa, mas inútil.
Vamos discutir como entendemos o mesmo: se deve ser uma solução, um projeto ou um espaço para nome - qual escala deve ser anexada ao contexto delimitado?

A primeira coisa que você pode ler em quase todos os lugares: idealmente,
um subdomínio deve mapear para um contexto limitado , ou seja, ser automatizado por um contexto limitado. Parece lógico, porque, além de haver limitações de um processo de negócios separado, em ambos os casos, alguns termos de negócios, um único idioma é exibido. Mas aqui você precisa entender que esta é uma situação ideal, você não terá necessariamente isso e não é necessário tentar conseguir isso.
Como, por um lado, o Subdomínio pode ser bastante grande e vários aplicativos ou serviços podem ser obtidos para automatizá-lo; portanto, vários contextos limitados correspondem a um Subdomínio.
Mas há uma situação inversa, como regra, isso é típico para o Legacy. Ou seja, quando eles criaram um aplicativo grande que automatiza tudo o que há no mundo nesta empresa, o oposto será o contrário. Uma aplicação é um contexto limitado; é provável que o modelo seja ambíguo, mas os Subdomínios não desapareceram disso, respectivamente, um contexto limitado corresponderá a vários Subdomínios.
Quando a arquitetura de microsserviço se tornou moda, outra recomendação apareceu (embora não se contradigam):
um contexto limitado por microsserviço . Mais uma vez, parece lógico, as pessoas realmente fazem isso. Como o microsserviço deve assumir alguma função clara, que possui alta conectividade internamente, e se comunica com outros serviços por meio de algum tipo de interação. Se você usa uma arquitetura de microsserviço, você pode seguir esta recomendação.
Mas isso não é tudo. Deixe-me lembrá-lo mais uma vez que o Design Orientado a Domínio é muito: sobre o idioma, sobre as pessoas. E você não pode ignorar as pessoas e fazer apenas critérios técnicos nesse assunto. Portanto, escrevi o seguinte:
um contexto é igual ao X-man . Eu pensava que x é cerca de 10, mas conversamos um pouco com Igor Labutin (
twitter.com/ilabutin ) e a questão permaneceu em aberto.
Aqui é importante entender isso: um único idioma permanece unificado enquanto todos os participantes falam, discutem e todos o entendem sem ambiguidade. É claro que um número infinito de pessoas não pode falar a mesma língua. Nossa história da humanidade mostra isso claramente. De qualquer forma, alguns dialetos aparecem, alguns de seus significados, agora você pode até adicionar memes e assim por diante. De uma forma ou de outra, o idioma ficará desfocado.
Portanto, deve-se entender que o número de pessoas que usam essa linguagem única e, consequentemente, participam do desenvolvimento, da automação, é limitado. Os livros também falam sobre algumas razões políticas: se duas equipes trabalham sob a liderança de gerentes diferentes e trabalham no mesmo contexto limitado, e por alguma razão esses gerentes não são amigos, conflitos começarão e o foco será perdido. Portanto, será muito mais simples e mais correto criar dois contextos limitados para cada comando e não tentar combinar o que não está combinado.
Arquitetura e Gerenciamento de Dependências
Do ponto de vista do Design Orientado a Domínio, não importa realmente qual arquitetura você escolher. Design Orientado a Domínio não é sobre isso; Design Orientado a Domínio é sobre linguagem e comunicação.

Mas há um ponto importante, do ponto de vista dos critérios para escolher a arquitetura que nos interessa da perspectiva do Design Orientado a Domínio:
nosso objetivo é livrar ao máximo a lógica de negócios das dependências de terceiros . Como, assim que aparecem dependências de terceiros, a terminologia aparece, aparecem palavras que não entram em um único idioma e começam a desarrumar nossa lógica de negócios.

Vejamos um exemplo clássico de arquitetura: a conhecida arquitetura de três camadas. Assim que eles não chamam uma camada de domínio (aqui a camada Negócios): negócios, núcleo e domínio são todos iguais. De qualquer forma, essa é a camada na qual a lógica de negócios está localizada e, se ela depende da camada de dados, significa que alguns conceitos da camada de dados de alguma forma fluem para a camada de domínio e a desarrumam.

A arquitetura de quatro camadas é essencialmente a mesma, a camada de domínio ainda depende e, como depende de dependências desnecessárias de terceiros, chegarão a ela.

E, nesse sentido, existe uma arquitetura que permite que isso seja evitado - é a arquitetura da cebola (“cebola”). Sua diferença é que consiste em camadas concêntricas, as dependências vão de fora para o centro. Ou seja, a camada externa pode depender de quaisquer camadas internas, a camada interna não pode depender das externas.
A camada mais externa é a interface do usuário em um sentido global (ou seja, não é necessariamente uma interface do usuário humana, pode ser uma API REST ou qualquer outra coisa). E a infraestrutura, que geralmente também se parece com E / S, é o mesmo banco de dados, de fato, uma camada de dados. Todas essas coisas estão na camada externa. Ou seja, devido ao qual o aplicativo de alguma forma recebe alguns dados, comandos e assim por diante, ele é removido e a camada de domínio se livra da dependência dessas coisas.
Em seguida, vem a camada Aplicativo - um tema bastante holístico, mas é a camada na qual os scripts e casos de usuário estão localizados. Essa camada usa a camada de domínio para implementar seus conceitos.
No centro está a camada de domínio. Como vemos, ele não depende mais de nada, ele se torna uma coisa em si mesmo. E é por isso que a camada de domínio costuma ser chamada de "núcleo", porque é o núcleo, é o que está no centro, o que não depende de coisas de terceiros.

Uma das opções para implementar essa arquitetura de cebola é a arquitetura hexagonal, ou “portas e adaptadores”. Eu trouxe essa foto por intimidação, não vou falar sobre isso. No final do post, há um link para um de um milhão de artigos sobre essa arquitetura, você pode ler.
Um pouco sobre padrões táticos: Interface Separada
Como eu disse, em primeiro lugar, a maioria dos padrões táticos é familiar a todos e, em segundo lugar, o ponto principal do meu relatório é que eles não são a essência. Mas gosto do padrão Separated Interface separadamente e quero falar sobre isso separadamente.
Vamos voltar ao código do nosso microsserviço e ver o que aconteceu com o repositório.

A camada de domínio tinha
a interface do
repositório IOrdersRepository.cs e sua
implementação, OrdersRepository.cs.
using System.Linq; namespace DotNext.Sales.Core { public interface IOrdersRepository { Order GetOrder(long id); void SaveOrder(Order order); IQueryable<Order> GetOrders(); #region V2 int GetLast3YearsCompletedOrdersCountFor(long customerId); #endregion } }
Aqui, adicionamos aqui um determinado método para receber pedidos nos últimos três anos GetLast3YearsCompletedOrdersCountFor.
E eles o implementaram de alguma forma (neste caso, através do Entity Framework, mas pode ser qualquer coisa):
public int GetLast3YearsCompletedOrdersCountFor(long customerId) { var threeYearsAgo = DateTime.UtcNow.AddYears(-3); return _dbContext.Orders .Count(o => o.CustomerId == customerId && o.State == OrderState.Completed && o.OrderDate >= threeYearsAgo); }
Veja qual é o problema. O repositório acabou na camada de domínio, sua implementação na camada de domínio, mas o código, começando com DateTime.UtcNow.AddYears (-3), não pertence inerentemente à camada de domínio e não é uma lógica de negócios. Sim, o LINQ o torna mais ou menos humanizado, mas se, por exemplo, houvesse consultas SQL aqui, tudo seria completamente triste.
O significado do padrão Interface Separada é que a interface de serviço que usamos na lógica do domínio seja declarada na camada de domínio. Estamos falando de repositórios e serviços similares, nos quais os detalhes da implementação desses serviços não são lógicos de negócios. A lógica de negócios é o fato da existência desses serviços e o fato de serem chamados e usados na camada de domínio. Portanto, a interface do repositório permanece na camada de domínio e a implementação é movida para a camada de infraestrutura.
Eu preparei outra opção. A interface do repositório permanece no assembly Principal, mas a implementação é movida para Infrastructure.EF.
Assim, trouxemos para a infraestrutura os conceitos que não eram peculiares à camada de domínio. Como efeito colateral, podemos substituir essa infraestrutura por outra implementação. Mas esse não é o objetivo principal, o objetivo principal é, como eu disse, livrar a lógica do domínio das dependências de terceiros.
Mais uma vez sobre o idioma
Vamos falar de novo e de novo e de novo sobre o idioma.
Desde o início, criamos o modelo de domínio “speaker - talk - event”. Eu acho que ninguém levantou nenhuma pergunta especial.
E aqui está o cenário com base no qual construímos este modelo de domínio:

Veja, o script está em russo e o modelo de domínio está em inglês.
Para desenvolvedores que não falam inglês, é algo com o qual você precisa conviver constantemente.

Provavelmente, cada um de vocês faz esse processo constantemente: traduz do russo para o inglês e vice-versa. Aqueles que trabalham com clientes e projetos que falam inglês são um pouco mais fáceis, porque os requisitos são em inglês, discussões com clientes em inglês, como regra geral, todos os cenários estão em inglês, o código está em inglês e há apenas comunicação dentro da equipe em russo, que cresce rapidamente em inglês (cliente - cliente, pedido - pedido). E essa carga cognitiva, essa sobrecarga, criada por uma tradução constante, recua um pouco.
, , , . , .
1, . , — , , .

1. PascalCase , , ,
, , , , - - .
, - ?

, use case, , .
, . C#, , , . , , , .
, , Domain-Driven Design. , , , , C# . .
, - Continuous Integration. , , , - - . , - , , . , 95% , , Continuous Integration, , TeamCity . .
, . Não. , 1-, , . «» , . , , .

, Domain-Driven Design.
— , . Domain-Driven Design — , . — , , ubiquitous language. , , . , , , .
. , . - , , , , , , , , , , — . .
. . . . , , . DSL-. .NET-, - , , , , . , .
, - . , , ubiquitous language -. .
- marshinov . , DDD: , « DDD, CQRS Event Sourcing » . , , .
- , AutoMapper MediatR. , Domain-Driven Design, .
- «F# for fun and profit» , , F# Domain-Driven Design. , , , .
- , , .
, ,
GitHub .
DotNext:
, « , ». DotNext ( 15-16 ) . , 1 , .