
1. Introdução
No primeiro artigo, destacamos o escopo das práticas indicadas, para quais projetos eles podem ser aplicados e para os quais não deveriam.
Neste artigo, gostaria de dar uma breve visão geral dos princípios básicos do DDD, além de compartilhar minha experiência pessoal com a aplicação deles. Falaremos mais detalhadamente sobre comunicação e abordagens estruturais com exemplos de sua implementação.
No artigo a seguir, escreverei as possíveis combinações dos padrões de design aplicados, levando em consideração sua implementação e, finalmente, darei um exemplo de uma implementação específica de um pequeno microsserviço.
DDD
Lembre-se da definição anterior:
O DDD (Domain-driven Design) é uma abordagem para o desenvolvimento de software para a satisfação complexa das necessidades, vinculando fortemente a implementação aos principais modelos de negócios que estão em processo de desenvolvimento constante.
O livro de referência que descreve a prática de construção de sistemas complexos é o Domain Driven Dedign de Eric Evans (Big Blue Book). Se você leu algum artigo de revisão sobre este tópico, já o conhece. Quando você usar o DDD na prática, precisará lê-lo. Este não é o livro mais fácil de ler:
A fonte canônica para DDD é o livro de Eric Evans. Não é a leitura mais fácil na literatura de software, mas é um daqueles livros que retribui amplamente um investimento substancial.
Martin Fowler: 15 de janeiro de 2014
Se você percorrer o conteúdo do livro, parecerá que você não está totalmente estruturado. Mas o mapa nos ajudará.

No mapa estão aquelas práticas que consideraremos hoje.
O escopo das práticas abordadas no livro é enorme. O escopo das práticas que podem ser aplicadas fora deste livro é ainda maior. Antes de colocar em serviço pelo menos parte deles, identifique seus objetivos. Vou dar o meu próprio como exemplo.
- Aumente a produtividade.
- Escreva um código compreensível.
- Dimensionamento no nível de desenvolvimento de software.
Língua única
O desenvolvimento de software raramente leva à criação de algo novo, como regra, é uma simulação de algo existente.
Um modelo é uma representação de um objeto real, que inclui apenas as propriedades e funções necessárias.
Não podemos criar um produto de software que cubra toda a área de assunto. É possível reproduzir apenas a parte que reproduzirá a funcionalidade necessária.
Um bom exemplo de modelo seria um mapa topográfico. Ela é um modelo de terreno. O mapa não contém prados de campos e rios, mas apenas reflete a localização de objetos reais um em relação ao outro.
Para criar um modelo claro e claro para todos, você precisa falar o mesmo idioma. Não apenas Eric Evans nos diz isso, mas também senso comum. Se os programadores usam seus termos e usam suas próprias gírias, os primeiros simplesmente não entenderão o que precisa ser feito. As empresas, neste caso, não serão capazes de perceber o custo real do desenvolvimento de um ou outro "recurso". Quantas vezes você já ouviu: "Sim, é apenas um botão para adicionar"?
Seu objetivo como projetista de sistemas deve ser obter o máximo entendimento um do outro de toda a equipe. Como conseguir isso? Comece a falar. Se as pessoas começam a se comunicar em qualquer grupo fechado, elas têm um certo conjunto de termos geralmente aceito. Em empresas diferentes, é provável que o processo de introdução de um idioma comum seja diferente. Pode ser uma decisão de força de vontade ou um procedimento democrático. O idioma pode ser indicado explicitamente, e não é digitado explicitamente; nesse caso, eles simplesmente começam a falar. Uma boa técnica para introduzir uma linguagem comum é a documentação geral.
Como manter a documentação do projeto
- Qualquer comunicação entre negócios e desenvolvimento deve melhorar seu modelo.
- Após a reunião, registre o resultado na forma de documentação (artefato Scrum) e mostre esta documentação a todos os participantes no processo de desenvolvimento.
- Use um único idioma na documentação.
- Mais importante: não perca tempo com a documentação. Você ainda precisa escrever o código e a documentação será reescrita muitas vezes, o gasto de recursos é caro. Em vez de mexer com o aplicativo de gráficos UML por um longo tempo, use um guardanapo, caneta e câmera no telefone.
- A documentação requer disciplina, você não pode escrevê-la de tempos em tempos.
- Separe a documentação:
- Comentários no código - descreva momentos incompreensíveis diretamente no código, deixe
#ODO:
(remova ao mesclar o código no mestre). Expresse sua opinião nos comentários, por exemplo, você deve usar uma ou outra muleta ao trabalhar com o código legasy. - Os comentários no projeto
README.md
no diretório raiz do seu projeto devem conter informações técnicas: como iniciar o projeto, como executar testes etc. Também é uma boa ideia obter um mapa onde você tem todos os projetos e em quais servidores eles estão sendo executados. Registre separadamente todos os contratos aceitos. - E o mais importante, a base de conhecimento. Uma coleção de documentos que descrevem os processos de negócios, essa é a parte dos documentos disponíveis para você e os negócios.
- O principal erro de quem escreve documentação é redundância. Não tente cobrir tudo e tudo, transmitir apenas o significado geral. A documentação deve complementar seu projeto, mas não substituí-lo de forma alguma. Não anote todos os termos que são apenas ambíguos. Se uma definição leva mais de duas frases, é uma definição ruim.
Exemplo de documentação:
# . # : : - - email - ## : ### , email , 1 ( email ). ### . email . ### email email . , email. . ### , , . . ### email, , . 2 . ### . , , .
Observe que, neste exemplo, não especificamos um dicionário explícito; no entanto, corrigimos o conceito de Usuário , Autorização , Registro . Escrever essa documentação não levará mais de 20 minutos para o especialista.
Para uma pessoa que não é especialista na área de assunto, o processo de escrever documentação é percebido como algo complicado. É necessário separar a coleção de conhecimentos e o registro dos conhecimentos coletados. != + .
"O que vocês chamam de universo", disse o quarto, "é, de fato, um acúmulo de mundos que, como a pele de um arco, estão um em cima do outro e são gradualmente separados um do outro."
- Excepto claramente claro! - admirava os abderitas. - Surpreendentemente claro! "Eles pensaram que entendiam o filósofo, porque sabiam muito bem o que era a cebola".
História de Aberdeen, Cristov Martin Wieland
Contextos e domínios limitados
Imagine que agimos como projetista de uma startup progressiva. Todos nós adoramos pizza resfriada, xingando com correios e preenchendo formulários no site por horas. Portanto, criamos uma maravilhosa startup “Quatro tartarugas e um rato”:
- Existe um site no qual as pizzarias são registradas e postam seus pratos reais.
- Essas pizzarias não possuem serviço de correio próprio.
- Existem clientes que não podem acessar a pizzaria, mas estão prontos para fazer um pedido pelo site ou aplicativo móvel.
- Assassino 'recurso': correios não são pessoas especialmente contratadas, mas pessoas comuns que se registraram através do aplicativo móvel
- Os correios recebem ordens e, após sua execução, recebem pagamento pelo trabalho realizado.
- Leva mais tempo para esperar por esses correios, mas a entrega e, consequentemente, a pizza, são mais baratas.
Vamos relembrar a documentação que descrevemos no capítulo anterior. Lá, em nosso dicionário único, o termo registro foi usado . Mas neste projeto, temos vários deles:
- Registro de cliente
- Registro de Pizzaria
- Registro de Correio
- Pedido de Registro
Uma linguagem unificada pertence a um contexto limitado. O domínio da documentação acima é 'Sistema de Autorização'. Vamos tentar alocar domínios para nossa inicialização.
Mas antes de começarmos, vejamos uma pequena terminologia sobre o que é um domínio e o que é um contexto limitado.
Domínio (domínio) - uma representação de uma estrutura de negócios real que resolve um problema específico.
Por exemplo: Sistema de logística, Sistema de pagamento, Sistema de autorização, Sistema de gerenciamento de pedidos.
O domínio é dividido em subdomínios, que descrevem estruturas menores, por exemplo: uma cesta de pedidos, um sistema para a construção de rotas.
Cada domínio tem uma área limitada de responsabilidade - funcionalidade limitada.
Contexto limitado - um conjunto de restrições de domínio que ajuda o domínio a se concentrar em apenas uma tarefa para obter a melhor solução.
Eu gosto de apresentar esse termo como uma abstração. Um domínio é um círculo. Um contexto restrito é um círculo.

Mesmo na terminologia DDD, o kernel é alocado.
O núcleo (domínio principal) - o domínio mais importante que caracteriza a sua empresa.
Então, os domínios do projeto "Quatro tartarugas e um rato":
Trabalhar com uma pizzaria (Pizzarias)
Contexto : b2b tudo relacionado a pizzarias
Subdomínios :
- registro de novas pizzarias
- adicionando sortimento
- atualizando o status da disponibilidade de um produto
Trabalhar com o cliente (clientes)
Contexto : b2c, tudo relacionado ao trabalho com clientes de pizza
Subdomínios :
- ver sortimento
- materiais informativos
Trabalhar com correios (sistema de entrega)
Contexto : b2e, tudo relacionado ao trabalho com correios
Subdomínios :
- registro de correio
- atribuição de tarefas
- registo de pedidos de levantamento de fundos obtidos pelo correio.
Sistema de pedidos
Contexto : Kernel. Permite coordenar todos os domínios individuais, fornecendo um ciclo completo desde o recebimento de um pedido até a entrega da pizza ao usuário. Não é um artista, mas desempenha o papel de condutor.
Subdomínios :
- aceitação de pedidos
- execução de ordens
- rastreamento do status do pedido
Sistema de liquidação (faturamento)
Contexto : contém todas as transações financeiras. Ele fornece interação com o centro de processamento.
Subdomínios :
- aceitando dinheiro para pedidos
- dando dinheiro aos correios pelo trabalho realizado
Sistema de Estatística
Contexto : A coleta e o processamento (não emissão) de informações analíticas.
Subdomínios :
- estatísticas de fundos
- estatísticas da aplicação
Sistema de Gerenciamento (painel Gerenciamento )
Contexto : A emissão de informações analíticas. Kit de ferramentas de decisões de gerenciamento.
- análises baseadas em estatísticas coletadas
- pré-moderação de pagamentos a correios
Com base nos domínios, vamos mapeá-lo.
Um mapa de domínio (mapa de contexto) é uma ferramenta gráfica que permite descrever os relacionamentos entre domínios individuais.

O mapa mostra links entre domínios. Este mapa é muito superficial, mas a área de assunto não é bem conhecida. Este é o primeiro esboço, reescrevendo o que você obterá o resultado esperado.
O mais importante no mapa é que vemos conexões entre domínios. Essa estrutura se encaixa muito bem na arquitetura de microsserviço:
O principal princípio da arquitetura de microsserviços: conectividade fraca e forte adesão.
Esse princípio é dado no livro de Sam Newman - Criando microsserviços , este é o segundo livro que você precisará ler para iniciar o uso prático das abordagens descritas neste artigo. O que se quer dizer: os domínios devem ser fracamente acoplados, mas intimamente ligados internamente.
A tradução desses termos é retirada da tradução oficial para o russo e, talvez, reflita mal o significado transmitido. Nos termos originais, soa como: Baixo acoplamento (conectividade, engajamento, aderência, conjugação), alta coesão (conectividade, força).
Prática de implementação de compartilhamento de domínio
Gostaria de compartilhar minha experiência pessoal - um conjunto de decisões informadas. Não exorto você a usar essas soluções. Mas eles podem ser uma boa opção se você não souber por onde começar. Com experiência pessoal, as ferramentas serão mais adaptadas às suas necessidades.
Os principais princípios que nos guiaram:
- A simplicidade da solução. Faça coisas complexas simples, não simples complicadas.
- Pragmatismo Você sempre precisa olhar para a situação e, na ausência de uma solução existente, desenvolver uma nova. Tente tipificar tudo, mas evite o dogmatismo.
- Código! = Documentação. O código é instruções para a máquina, a documentação é uma instrução para as pessoas. Não há necessidade de confundi-los e distribuir um após o outro.
- SÓLIDO
Como implementar domínios?
É muito conveniente selecionar domínios como microsserviços separados.
Microsserviços é um aplicativo separado que implementa a lógica de um domínio.
No desenvolvimento do DDD, o princípio de alocar um microsserviço em um aplicativo separado será um contexto limitado. Isso não nega o princípio técnico da separação de serviços (se isso ocorrer devido à necessidade de garantir alto desempenho). Mas o princípio contextual será dominante e obrigatório.
Como destacar o relacionamento entre domínios?
Relações entre domínios é sempre uma API. Pode ser RESTful json api, gRPC, AMPQ. No âmbito deste artigo, não compararemos um protocolo com outro e destacaremos suas vantagens e desvantagens, cada um deles com seu próprio campo de aplicação. Ainda assim, vamos nos debruçar sobre recomendações gerais:
Seja flexível na escolha de um protocolo e rígido na uniformidade de sua implementação.
Escolha um protocolo para cada par de domínios individualmente, não tente usar http em qualquer lugar, talvez seja necessário filas assíncronas em algum lugar e as vantagens do AMPQ se tornarão óbvias para você. Não ignore esta oportunidade porque você tem RESTful em todos os lugares.
Por outro lado, se você estiver implementando o RESTful json, use um padrão de estruturação de dados. Você pode preparar, por exemplo, jsonapi ou openapi. Se, por algum motivo, as soluções prontas não se adequarem a você e você sentir que pode desenvolver seu padrão, descrevê-lo e usá-lo. Mas use-o em qualquer lugar, não crie padrões de "zoológico". Se você precisar se comunicar com um sistema externo onde eles não sabem nada sobre seus padrões, escreva um adaptador de microsserviço.

Como implementar subdomínios?
Como módulos separados dentro de um microsserviço.
Um módulo é uma implementação de um subdomínio, colocando a lógica em um espaço para nome separado (Espaço para Nome) em um único microsserviço.
Como é tudo isso? Vejamos um exemplo. Como lembramos, temos um domínio do sistema de entrega - esse domínio possui três subdomínios:
- registro de correio
- questão de tarefas (tarefas)
- registro de pedidos de retirada de fundos auferidos pelo correio (retirada)
- verificando se o seu microsserviço está funcionando, ferramenta técnica auxiliar (healt_checker)
Imagine tudo isso na forma de uma estrutura de pastas:
$ tree --dirsfirst delivery_system delivery_system ├── app/ │ ├── health_checker/ │ │ └── endpoints.rb │ ├── registrations/ │ │ ├── entities/ │ │ ├── forms/ │ │ ├── repositories/ │ │ ├── interactor/ │ │ ├── services/ │ │ ├── validations/ │ │ ├── endpoints.rb │ │ └── helpers.rb │ ├── tasks │ │ ├── entities/ │ │ ├── queries/ │ │ ├── repositories/ │ │ ├── endpoints.rb │ │ └── helpers.rb │ └── withdrawals │ ├── entities/ │ ├── forms/ │ ├── repositories/ │ ├── interactor/ │ ├── services/ │ ├── validations/ │ ├── endpoints.rb │ └── helpers.rb ├── config/ ├── db/ ├── docs/ ├── lib/ │ ├── schemas/ │ └── values/ ├── public ├── specs ├── config.ru ├── Gemfile ├── Gemfile.lock ├── Rakefile └── README.md
Cada pasta no diretório apps/
implementa um ou outro subdomínio. Dentro de cada domínio, existem vários padrões: entities
, forms
, services
etc. Vamos considerar cada um dos padrões aplicados em detalhes em um dos artigos futuros.
Cada padrão é implementado no espaço de nome correspondente (espaço de nome). Por exemplo, um formulário para criar um pedido de pagamento a um mensageiro:
module Withdrawal
Como implementar a comunicação entre subdomínios?
Vejamos um exemplo específico. Temos uma conta de correio: Registrations::Entities::Account
. Refere-se ao subdomínio Registrations
- já que consideramos esse domínio não como um processo de registro, mas como uma tabela de contas e um livro de registro, conforme indicado em nossa documentação comercial.
Temos dois processos em execução dos quais acessamos esta conta.
- Crie uma conta (Registro)
- Criação de um pedido de retirada de fundos ganhos por um mensageiro (Wihtdrawal)
Como vemos, esses dois processos pertencem a subdomínios diferentes - Registro e Retirada.
module Registrations module Serivices class CreateAccount def call account = Entities::Account.new end end end end module Withdrwals module Serivices class CreateOrder def call account = Registrations::Entities::Account.new end end end end
No primeiro caso, uma chamada para a classe será implementada através de uma chamada para Entities::Account
. E no segundo caso, através de uma chamada explícita para Registrations::Entities::Account
. I.e. se especificarmos explicitamente um subdomínio, a classe será de outro subdomínio e, portanto, indicamos claramente a conexão.
Se a classe não se aplicar explicitamente a nenhum dos subdomínios, faz sentido movê-la para a pasta lib/
. Como regra, são classes que implementam o padrão 'ValueObject'. Examinaremos esse padrão em mais detalhes em um dos seguintes artigos.
Implementação através do modelo.
Para citar Eric Evans:
Se a arquitetura do programa, ou pelo menos uma parte central dele, não corresponder à estrutura do modelo de domínio, esse modelo será praticamente inútil e a operação correta do programa também deverá ser questionada. Ao mesmo tempo, relacionamentos muito complexos entre modelos e funções na arquitetura de software são difíceis de entender e, na prática, são difíceis de manter à medida que a arquitetura muda.
Vamos relembrar o exemplo de um bom modelo que eu já citei no início deste artigo - um mapa topográfico. Nosso objetivo é encontrar rapidamente a distância entre dois assentamentos. Poderíamos usar uma tabela de referência mostrando dois pontos entre as cidades. E nós podemos usar o cartão. Lá e lá, obtemos o mesmo resultado quase ao mesmo tempo. Mas o mapa é mais compacto, exibe com mais precisão a área de assunto, é mais universal. O mapa como modelo é incrivelmente expressivo. E se a considerarmos no âmbito desta tarefa, medir a distância é mais conveniente no mapa do que no próprio território, o que reflete. Um modelo que reflete uma área de assunto pode superá-lo em algumas propriedades. É realmente incrível.
A implementação do modelo é sempre um processo criativo com resultados imprevisíveis. A qualidade do seu código não é o desempenho nem a complexidade, é a simplicidade e a expressividade. Melhore-o através de refatoração constante, torne-o flexível e elimine todos os desnecessários. Separe a camada que será responsável pela lógica de negócios do modelo das camadas cuja necessidade é devido à implementação técnica. Como conseguimos fazer isso será descrito mais adiante.