Você está na página 1de 14

30/10/2023, 21:10 DDD, Hexagonal, Cebola, Limpa, CQRS, ...

Como eu juntei tudo – @hgraca

DDD, Hexagonal, Cebola, Limpo, CQRS, ..

hgraca Arquitetura, Desenvolvimento, Série, As Crônicas Arquitetura Software, Sem categoria


Novembro 16, 2017 17 Minutos

Este post faz parte de As Crônicas Arquitetura Software, a série de posts sobre Arquitetura de Software.
Neles, escrevo sobre o que aprendi sobre Arquitetura de Software, como penso nisso e como uso esse
conhecimento. O conteúdo deste post pode fazer mais sentido se você ler os posts anteriores desta série.

Depois de me formar na Universidade, segui uma carreira como professor do ensino médio até alguns anos
atrás, decidi abandoná-lo e me tornar um desenvolvedor de software em tempo integral.

A partir daí, sempre senti que precisava de recuperar o tempo “lost” e aprender o máximo possível, o mais
rápido possível. Então eu me tornei um pouco viciado em experimentar, ler e escrever, com um foco especial
em design de software e arquitetura. Isso é porque eu escrevo esses posts, para me ajudar a aprender.

Nos meus últimos posts, I’ve tem escrito sobre muitos dos conceitos e princípios que aprendi e um pouco
sobre como raciocino sobre eles. Mas eu vejo isso como apenas peças de um grande quebra-cabeça.

O post de TodayNotis é sobre como eu encaixei todas essas peças juntas e, como parece que eu deveria dar
um nome, eu chamo isso Arquitetura Explícita. Além disso, estes conceitos têm todos os “passou seus
testes de batalha” e são utilizados em código de produção em plataformas altamente exigentes. Uma é uma
plataforma de e-com SaaS com milhares de lojas online em todo o mundo, outra é um mercado, ao vivo em 2
países com um barramento de mensagens que lida com mais de 20 milhões de mensagens por mês.

Blocos fundamentais do sistema


Ferramentas
Conectando as ferramentas e mecanismos de entrega ao Application Core
Portos
Adaptadores Primários ou de Condução
Adaptadores Secundários ou Conduzidos
Inversão de controle
Organização Central de Aplicações
Camada Aplicação
Camada Domínio
Serviços Domínio
Modelo de Domínio
Componentes
Desacoplamento dos componentes
Acionar a lógica em outros componentes
Obtendo dados de outros componentes
https://herbertograca.com/2017/11/16/explicit-architecture-01-ddd-hexagonal-onion-clean-cqrs-how-i-put-it-all-together/ 1/14
30/10/2023, 21:10 DDD, Hexagonal, Cebola, Limpa, CQRS, ... Como eu juntei tudo – @hgraca

Armazenamento de dados compartilhado entre componentes


Armazenamento de dados segregado por componente
Fluxo de controle

Blocos fundamentais do sistema

Começo por recordar EBI e Portas e Adaptadores arquiteturas. Ambos fazem uma separação explícita de
qual código é interno ao aplicativo, o que é externo e o que é usado para conectar código interno e externo.

Além disso, Portas e Adaptadores a arquitetura identifica explicitamente três blocos fundamentais de código
em um sistema:

O que torna possível executar um interface do usuário, qualquer que seja o tipo de interface de usuário;
O sistema lógica de negócios, ou núcleo da aplicação, é usado pela interface do usuário para realmente
fazer as coisas acontecerem;
Infraestrutura código, que conecta nosso núcleo de aplicativos a ferramentas como um banco de dados,
um mecanismo de pesquisa ou APIs de 3o partido.

O núcleo da aplicação é o que realmente devemos nos preocupar. É o código que permite que o nosso código
faça o que é suposto fazer, É a nossa aplicação. Ele pode usar várias interfaces de usuário (aplicativo web
progressivo, móvel, CLI, API, ..), mas o código realmente fazendo o trabalho é o mesmo e está localizado no
núcleo do aplicativo, ele deveria realmente importar o que a UI o aciona.

Como você pode imaginar, o fluxo típico do aplicativo vai do código na interface do usuário, passando pelo
núcleo do aplicativo até o código da infraestrutura, de volta ao núcleo da aplicação e, finalmente, entregar
uma resposta à interface do usuário.

Ferramentas

Longe do código mais importante em nosso sistema, o núcleo do aplicativo, temos as ferramentas que nosso
aplicativo usa, por exemplo, um mecanismo de banco de dados, um mecanismo de pesquisa, um servidor
Web ou um console CLI (embora os dois últimos também sejam mecanismos de entrega).

Embora possa parecer estranho colocar um console CLI no mesmo “bucket” como um mecanismo de banco
de dados, e embora eles tenham diferentes tipos de propósitos, eles podem ser usados para criar um console
CLI, eles são, de fato, ferramentas usadas pelo aplicativo. A principal diferença é que, enquanto o console
CLI e o servidor web estão acostumados a dizer nossa aplicação para fazer alguma coisa, é o mecanismo
de banco de dados contado por nossa aplicação para fazer algo. Esta é uma distinção muito relevante, pois
tem fortes implicações sobre como construímos o código que conecta essas ferramentas com o núcleo do
aplicativo.

https://herbertograca.com/2017/11/16/explicit-architecture-01-ddd-hexagonal-onion-clean-cqrs-how-i-put-it-all-together/ 2/14
30/10/2023, 21:10 DDD, Hexagonal, Cebola, Limpa, CQRS, ... Como eu juntei tudo – @hgraca

Conectando as ferramentas e mecanismos de


entrega ao Application Core

As unidades de código que conectam as ferramentas ao núcleo do aplicativo são chamadas de adaptadores
(Arquitetura de Portos e Adaptadores). Os adaptadores são aqueles que efetivamente implementam o código
que permitirá que a lógica de negócios se comunique com uma ferramenta específica e vice-versa.

Os adaptadores que dizer nosso aplicativo para fazer algo é chamado Adaptadores Primários ou de
Condução enquanto os que são contado por nossa aplicação para fazer algo são chamados Adaptadores
Secundários ou Conduzidos.

Portos

Estes Adaptadores, no entanto, não são criados aleatoriamente. Eles são criados para ajustar um ponto de
entrada muito específico ao Application Core, um Porto. Uma porta nada mais é do que uma
especificação de como a ferramenta pode usar o núcleo do aplicativo ou como é usada pelo Application Core.
Na maioria dos idiomas e em sua forma mais simples, essa especificação, a Porta, será uma Interface, mas
pode ser composta por várias Interfaces e DTOs.

É importante observar que as interfaces ( As interfaces ) pertencem à lógica dos negócios, enquanto os
adaptadores pertencem ao exterior. Para que esse padrão funcione como deveria, é de extrema importância
que as portas sejam criadas para atender às necessidades do Application Core e não apenas imitar as APIs de
ferramentas.

Adaptadores Primários ou de Condução

O Primário ou Adaptadores de motorista embrulho ao redor de um porto e use-o para dizer ao


Application Core o que fazer. Eles traduzem o que vem de um mecanismo de entrega em uma chamada
de método no Application Core.

Em outras palavras, nossos adaptadores de direção são controladores ou comandos de console que são
injetados em seu construtor com algum objeto cuja classe implementa a interface ( Porta ) que o comando do
controlador ou do console exige.

Em um exemplo mais concreto, uma porta pode ser uma interface de serviço ou uma interface de repositório
necessária para um controlador. A implementação concreta do Serviço, Repositório ou Consulta é então
injetada e usada no Controlador.

Alternativamente, uma porta pode ser uma interface Command Bus ou Query Bus. Neste caso, uma
implementação concreta do Command ou Query Bus é injetada no Controlador, que então constrói um
Command ou Query e o passa para o Bus relevante.

https://herbertograca.com/2017/11/16/explicit-architecture-01-ddd-hexagonal-onion-clean-cqrs-how-i-put-it-all-together/ 3/14
30/10/2023, 21:10 DDD, Hexagonal, Cebola, Limpa, CQRS, ... Como eu juntei tudo – @hgraca

Adaptadores Secundários ou Conduzidos

Ao contrário dos Adaptadores de Driver, que envolvem uma porta, os Adaptadores


Conduzidos implementar um Porto, uma interface, e são então injetados no Application Core, onde quer
que a porta é necessária (tipo-inted).

Por exemplo, letilits supõe que temos uma aplicação ingênua que precisa persistir dados. Assim, criamos uma
interface de persistência que atende às suas necessidades, com um método para salvar uma matriz de dados e
um método para excluir uma linha em uma tabela pelo seu ID. A partir de então, onde quer que nosso
aplicativo precise salvar ou excluir dados, exigiremos em seu construtor um objeto que implemente a
interface de persistência que definimos.

Agora nós criamos um adaptador específico para MySQL que irá implementar essa interface. Ele terá os
métodos para salvar uma matriz e excluir uma linha em uma tabela, e vamos injetá-lo onde quer que a
interface de persistência é necessária.

Se, em algum momento, decidirmos mudar o fornecedor do banco de dados, vamos dizer para PostgreSQL ou
MongoDB, ou, só precisamos criar um novo adaptador que implemente a interface de persistência e seja
específico para o PostgreSQL, e injetar o novo adaptador em vez do antigo.

Inversão de controle

Uma característica a ser observada sobre esse padrão é que os adaptadores dependem de uma ferramenta
específica e de uma porta específica (implementando uma interface). Mas nossa lógica de negócios depende
apenas da porta (interface), que é projetada para atender às necessidades de lógica de negócios, por isso
depende de um adaptador ou ferramenta específica.

Isso significa que a direção das dependências é em direção ao centro inversão do princípio de controle no
nível arquitetônico.

Embora, novamente, é de extrema importância que as Portas sejam criadas para atender às
necessidades do Application Core e não simplesmente imitar as APIs das ferramentas.

Organização do Application Core

O Arquitetura Cebola pega as camadas DDD e as incorpora no Arquitetura de Portos e Adaptadores. Essas
camadas destinam-se a trazer alguma organização para a lógica de negócios, o interior dos Ports & Adapters
“hexagon” e, assim como nos Ports & Adapters, os, a direção das dependências é em direção ao centro.

https://herbertograca.com/2017/11/16/explicit-architecture-01-ddd-hexagonal-onion-clean-cqrs-how-i-put-it-all-together/ 4/14
30/10/2023, 21:10 DDD, Hexagonal, Cebola, Limpa, CQRS, ... Como eu juntei tudo – @hgraca

Camada Aplicação

Os casos de uso são os processos que podem ser acionados em nosso Application Core por uma ou várias
Interfaces de Usuário em nosso aplicativo. Por exemplo, em um CMS, poderíamos ter a UI do aplicativo real
usada pelos usuários comuns, outra UI independente para os administradores do CMS, outra UI da CLI e uma
API da Web. Essas IUs (aplicativos) podem desencadear casos de uso que podem ser específicos para um
deles ou reutilizados por vários deles.

Os casos de uso são definidos na Camada de Aplicação, a primeira camada fornecida pelo DDD e usada pela
Onion Architecture.

Esta camada contém Serviços de Aplicação (e suas interfaces) como cidadãos de primeira classe, mas
também contém as interfaces Ports & Adapters (portas) que incluem interfaces ORM, interfaces de
mecanismos de pesquisa, interfaces, interfaces de mensagens e assim por diante. No caso em que estamos
usando um Command Bus e/ou um Query Bus, essa camada é onde os respectivos Handlers para os
Comandos e Consultas pertencem.

Os Serviços de Aplicação e/ou Manipuladores de Comando contêm a lógica para desdobrar um caso de uso,
um processo de negócios. Normalmente, o papel deles é:

1. use um repositório para encontrar uma ou várias entidades;


2. diga a essas entidades para fazer alguma lógica de domínio;
3. e use o repositório para persistir as entidades novamente, salvando efetivamente as alterações de dados.

Os Manipuladores de Comando podem ser usados de duas maneiras diferentes:

1. Eles podem conter a lógica real para executar o caso de uso;


2. Eles podem ser usados como meras peças de fiação em nossa arquitetura, recebendo um Comando e
simplesmente acionando a lógica que existe em um Serviço de Aplicativo.

Qual abordagem usar depende do contexto, por exemplo:

Nós já temos os Serviços de Aplicação em vigor e agora estamos adicionando um Command Bus?
O Command Bus permite especificar qualquer classe/método como um manipulador, ou eles precisam
estender ou implementar classes ou interfaces existentes?

Esta camada também contém o desencadeamento de Eventos de Aplicação, que representam algum resultado
de um caso de uso. Esses eventos acionam uma lógica que é um efeito colateral de um caso de uso, como
enviar e-mails, notificar uma API do 3o partido, enviar uma notificação push, etc, ou até mesmo iniciar outro
caso de uso que pertença a um componente diferente do aplicativo.

https://herbertograca.com/2017/11/16/explicit-architecture-01-ddd-hexagonal-onion-clean-cqrs-how-i-put-it-all-together/ 5/14
30/10/2023, 21:10 DDD, Hexagonal, Cebola, Limpa, CQRS, ... Como eu juntei tudo – @hgraca

Camada Domínio

Mais para dentro, temos a Camada de Domínio. Os objetos nesta camada contêm os dados e a lógica para
manipular esses dados, que é específica para o próprio domínio e itirates independente dos processos de
negócios que acionam essa lógica, que é a lógica, eles são independentes e completamente inconscientes da
Camada de Aplicação.

Serviços Domínio

Como mencionei acima, o papel de um Serviço de Aplicação é:

1. use um repositório para encontrar uma ou várias entidades;


2. diga a essas entidades para fazer alguma lógica de domínio;
3. e use o repositório para persistir as entidades novamente, salvando efetivamente as alterações de dados.

No entanto, às vezes encontramos alguma lógica de domínio que envolve entidades diferentes, do mesmo
tipo ou não, e sentimos que essa lógica de domínio não pertence às próprias entidades, nós sentimos que essa
lógica não é sua responsabilidade direta.

Portanto, nossa primeira reação pode ser colocar essa lógica fora das entidades, em um Serviço de Aplicativo.
No entanto, isso significa que essa lógica de domínio não será reutilizável em outros casos de uso: a lógica de
domínio deve ficar fora da camada de aplicação!

A solução é criar um Serviço de Domínio, que tem o papel de receber um conjunto de entidades e realizar
alguma lógica de negócio nelas. Um Serviço de Domínio pertence à Camada de Domínio e, portanto, não
sabe nada sobre as classes na Camada de Aplicativo, como os Serviços de Aplicativo ou os Repositórios. Por
outro lado, ele pode usar outros Serviços de Domínio e, é claro, os objetos Modelo de Domínio.

Modelo de Domínio

No próprio centro, dependendo de nada fora dele, está o Modelo de Domínio, que contém os objetos de
negócios que representam algo no domínio. Exemplos desses objetos são, em primeiro lugar, Entidades, mas
também Objetos de Valor, Enums e quaisquer objetos usados no Modelo de Domínio.

O Modelo de Domínio é também onde os Eventos de Domínio “live”. Esses eventos são acionados quando
um conjunto específico de dados muda e eles carregam essas alterações com eles. Em outras palavras, quando
uma entidade muda, um Evento de Domínio é acionado e carrega as propriedades alteradas novos valores.
Esses eventos são perfeitos, por exemplo, para serem usados no Event Sourcing.

https://herbertograca.com/2017/11/16/explicit-architecture-01-ddd-hexagonal-onion-clean-cqrs-how-i-put-it-all-together/ 6/14
30/10/2023, 21:10 DDD, Hexagonal, Cebola, Limpa, CQRS, ... Como eu juntei tudo – @hgraca

Componentes

Até agora, temos segregado o código com base em camadas, mas essa é a segregação de código refinado. A
segregação de grãos grosseiros do código é pelo menos tão importante quanto e trata de segregar o código de
acordo com subdomínios e delimitado contextos, seguindo Robert C. Martin ideias expressas em arquitetura
gritando. Isso é muitas vezes referido como “Pacote por recurso” ou “Pacote por componente” em oposição
a”Pacote por camada“, e itroits muito bem explicado por Simon Brown no seu post no blog “Pacote por
componente e teste alinhado arquitetonicamente“:

Sou um defensor dos “Pacote por componente” aproxime-se e, pegando no diagrama de Simon Brown
sobre Pacote por componente, eu mudaria descaradamente para o seguinte:

https://herbertograca.com/2017/11/16/explicit-architecture-01-ddd-hexagonal-onion-clean-cqrs-how-i-put-it-all-together/ 7/14
30/10/2023, 21:10 DDD, Hexagonal, Cebola, Limpa, CQRS, ... Como eu juntei tudo – @hgraca

Essas seções de código são transversais às camadas descritas anteriormente, elas são componentes da nossa
aplicação. Exemplos de componentes podem ser Autenticação, Autorização,Faturamento, Usuário, Revisão
ou Conta, mas eles estão sempre relacionados ao domínio. Contextos limitados como Autorização e/ou
Autenticação devem ser vistos como ferramentas externas para as quais criamos um adaptador e nos
escondemos atrás de algum tipo de porta.

Desacoplamento dos componentes

Assim como as unidades de código de granulação fina (classes, interfaces, traits, mixins, ..), também as
unidades de código de granulação grossa (componentes) se beneficiam de baixo acoplamento e alta coesão.

Para desacoplar classes, fazemos uso de Injeção de Dependência, injetando dependências em uma classe em
vez de instanciá-las dentro da classe, e Inversão de Dependência, em vez de, ao fazer a classe depender de
abstrações (interfaces e/ou classes abstratas) em vez de classes concretas. Isso significa que a classe
dependente não tem conhecimento sobre a classe concreta que vai usar, não tem referência ao nome de classe
totalmente qualificado das classes das quais depende.

Da mesma forma, ter componentes completamente dissociados significa que um componente não tem
conhecimento direto de nenhum outro componente. Em outras palavras, ele não tem referência a nenhuma
unidade de código de granulação fina de outro componente, nem mesmo interfaces! Isso significa que a

https://herbertograca.com/2017/11/16/explicit-architecture-01-ddd-hexagonal-onion-clean-cqrs-how-i-put-it-all-together/ 8/14
30/10/2023, 21:10 DDD, Hexagonal, Cebola, Limpa, CQRS, ... Como eu juntei tudo – @hgraca

Injeção de Dependência e a Inversão de Dependência não são suficientes para dissociar componentes,
precisaremos de algum tipo de construção arquitetônica. Podemos precisar de eventos, um kernel
compartilhado, consistência eventual e até mesmo um serviço de descoberta!

Acionar a lógica em outros componentes

Quando um de nossos componentes (componente B) precisa fazer algo sempre que algo acontece em outro
componente (componente A), você pode, não podemos simplesmente fazer uma chamada direta do
componente A para uma classe/método no componente B porque A seria então acoplado a B.

No entanto, podemos fazer com que A use um expedidor de eventos para despachar um evento de aplicativo
que será entregue a qualquer componente que o ouça, incluindo B, e o ouvinte de eventos em B acionará a
ação desejada. Isso significa que o componente A dependerá de um despachante de eventos, mas será
dissociado de B.

No entanto, se o evento em si “ vive ” em A, isso significa que B sabe sobre a existência de A, ele é acoplado
a A. Para remover essa dependência, podemos criar uma biblioteca com um conjunto de funcionalidades do
núcleo do aplicativo que serão compartilhadas entre todos os componentes, o Kernel compartilhado. Isso
significa que os componentes dependerão do Kernel Compartilhado, mas serão dissociados um do outro. O
Kernel Compartilhado conterá funcionalidades como eventos de aplicativo e domínio, mas também pode
conter objetos de Especificação e o que fizer sentido compartilhar, tendo em mente que deve ser o mínimo
possível, porque quaisquer alterações no Kernel Compartilhado afetarão todos os componentes do aplicativo.
Além disso, se temos um sistema poliglota, os letilitis dizem que é um ecossistema de micro-serviços onde
https://herbertograca.com/2017/11/16/explicit-architecture-01-ddd-hexagonal-onion-clean-cqrs-how-i-put-it-all-together/ 9/14
30/10/2023, 21:10 DDD, Hexagonal, Cebola, Limpa, CQRS, ... Como eu juntei tudo – @hgraca

eles são escritos em diferentes línguas, o Kernel Compartilhado precisa ser agnóstico da linguagem para que
possa ser entendido por todos os componentes, seja qual for a linguagem em que foram escritos. Por
exemplo, em vez do Kernel Compartilhado contendo uma classe Event, ele conterá a descrição do evento (ou
seja, nome, propriedades, etc,talvez até mesmo métodos embora estes seriam mais úteis em um objeto
Especificação) em uma linguagem agnóstica como JSON, de modo que todos os componentes/os micro-
serviços podem interpretá-lo e talvez até gerar automaticamente suas próprias implementações concretas.
Leia mais sobre isso no meu post de acompanhamento: Mais do que camadas concêntricas.

Essa abordagem funciona tanto em aplicações monolíticas quanto em aplicações distribuídas, como
ecossistemas de microsserviços. No entanto, quando os eventos só podem ser entregues de forma assíncrona,
para contextos em que a lógica de disparo em outros componentes precisa ser feita imediatamente, essa
abordagem não será suficiente! O componente A precisará fazer uma chamada HTTP direta para o
componente B. Nesse caso, para que os componentes sejam desacoplados, precisaremos de um serviço de
descoberta para o qual A perguntará para onde deve enviar a solicitação para acionar a ação desejada, ou,
alternativamente, faça a solicitação para o serviço de descoberta, que pode fazer proxy para o serviço
relevante e, eventualmente, retornar uma resposta ao solicitante. Esta abordagem irá acoplar os componentes
para o serviço de descoberta, mas irá mantê-los dissociados uns dos outros.

Obtendo dados de outros componentes

Do jeito que eu vejo, um componente não tem permissão para alterar dados que não “own”, mas é bom para
ele consultar e usar quaisquer dados.

Armazenamento de dados compartilhado entre componentes

Quando um componente precisa usar dados que pertençam a outro componente, digamos que um componente
de cobrança precise usar o nome do cliente que pertence ao componente de contas, o componente de
cobrança conterá um objeto de consulta que consultará o armazenamento de dados para esses dados. Isso
significa simplesmente que o componente de faturamento pode saber sobre qualquer conjunto de dados, mas
deve usar os dados que não “own” como somente leitura, por meio de consultas.

Armazenamento de dados segregado por componente

Nesse caso, o mesmo padrão se aplica, mas temos mais complexidade no nível de armazenamento de dados.
Ter componentes com seu próprio armazenamento de dados significa que cada armazenamento de dados
contém:

Um conjunto de dados que possui e é o único permitido mudar, tornando-se a única fonte de verdade;
Um conjunto de dados que é uma cópia de outros dados de componentes, que não pode mudar por conta
própria, mas é necessário para a funcionalidade do componente, e ele precisa ser atualizado sempre que
mudar no componente proprietário.

Cada componente criará uma cópia local dos dados de que precisa de outros componentes, para serem usados
quando necessário. Quando os dados são alterados no componente que os possui, esse componente
proprietário acionará um evento de domínio com as alterações de dados. Os componentes que possuem uma
cópia desses dados estarão ouvindo esse evento de domínio e atualizarão sua cópia local de acordo.
https://herbertograca.com/2017/11/16/explicit-architecture-01-ddd-hexagonal-onion-clean-cqrs-how-i-put-it-all-together/ 10/14
30/10/2023, 21:10 DDD, Hexagonal, Cebola, Limpa, CQRS, ... Como eu juntei tudo – @hgraca

Fluxo de controle

Como eu disse acima, o fluxo de controle vai, é claro, do usuário para o Application Core, para as
ferramentas de infraestrutura, de volta ao Application Core e finalmente de volta ao usuário. Mas como
exatamente as aulas se encaixam? Quais dependem de quais? Como os compomos?

Seguindo o Tio Bob, em seu artigo sobre Arquitetura Limpa, tentarei explicar o fluxo de controle com
diagramas UMLish…

Sem um Ônibus de Comando/Consulta

No caso de não usarmos um barramento de comando, os Controladores dependerão de um Serviço de


Aplicativo ou de um objeto de Consulta.

[EDITAR – 2017-11-18] Eu perdi completamente o DTO que uso para retornar dados da consulta, então eu
adicionei agora. Tkx para MorfinaAdministrado quem apontou para mim.

https://herbertograca.com/2017/11/16/explicit-architecture-01-ddd-hexagonal-onion-clean-cqrs-how-i-put-it-all-together/ 11/14
30/10/2023, 21:10 DDD, Hexagonal, Cebola, Limpa, CQRS, ... Como eu juntei tudo – @hgraca

No diagrama acima, usamos uma interface para o Serviço de Aplicativos, embora possamos argumentar que
não é realmente necessário, pois o Serviço de Aplicativos faz parte do nosso código de aplicativo e não
queremos trocá-lo por outra implementação, embora possamos refratá-lo completamente.

O objeto Query conterá uma consulta otimizada que simplesmente retornará alguns dados brutos para serem
mostrados ao usuário. Esses dados serão retornados em um DTO que será injetado em um ViewModel. O
ThisViewModel pode ter alguma lógica de visualização e será usado para preencher uma exibição.

O Serviço de Aplicação, por outro lado, conterá a lógica do caso de uso, a lógica que acionaremos quando
quisermos fazer algo no sistema, a, ao contrário de simplesmente ver alguns dados. Os Serviços de Aplicação
dependem de Repositórios que irão retornar o Entity(ies) que contém a lógica que precisa ser acionada.
Também pode depender de um Serviço de Domínio para coordenar um processo de domínio em várias
entidades, mas isso quase nunca é o caso.

Depois de desdobrar o caso de uso, o Serviço de Aplicativo pode querer notificar todo o sistema que esse
caso de uso aconteceu, nesse caso, também dependerá de um despachante de eventos para acionar o evento.

É interessante notar que colocamos interfaces tanto no mecanismo de persistência quanto nos repositórios.
Embora possa parecer redundante, eles servem a propósitos diferentes:

A interface de persistência é uma camada de abstração sobre o ORM para que possamos trocar o ORM
que está sendo usado sem alterações no Application Core.
A interface do repositório é uma abstração no próprio mecanismo de persistência. Letilitis diz que
queremos mudar do MySQL para o MongoDB. A interface de persistência pode ser a mesma e, se
quisermos continuar usando o mesmo ORM, até mesmo o adaptador de persistência permanecerá o
mesmo. No entanto, a linguagem de consulta é completamente diferente, então podemos criar novos
repositórios que usam o mesmo mecanismo de persistência, implemente as mesmas interfaces de
repositório, mas crie as consultas usando a linguagem de consulta MongoDB em vez de SQL.

Com um Ônibus de Comando/Consulta

No caso de nosso aplicativo usar um Command/Query Bus, o diagrama permanece praticamente o mesmo,
com a exceção de que o controlador agora depende do Bus e de um comando ou uma Consulta. Ele irá
instanciar o Comando ou a Consulta, e passá-lo ao longo do Ônibus que vai encontrar o manipulador
apropriado para receber e lidar com o comando.

No diagrama abaixo, o Manipulador de Comando usa um Serviço de Aplicativo. No entanto, isso nem sempre
é necessário, na verdade, na maioria dos casos, o manipulador conterá toda a lógica do caso de uso. Só
precisamos extrair a lógica do manipulador em um Serviço de Aplicativo separado se precisarmos reutilizar
essa mesma lógica em outro manipulador.

[EDITAR – 2017-11-18] Eu perdi completamente o DTO que uso para retornar dados da consulta, então eu
adicionei agora. Tkx para MorfinaAdministrado quem apontou para mim.

https://herbertograca.com/2017/11/16/explicit-architecture-01-ddd-hexagonal-onion-clean-cqrs-how-i-put-it-all-together/ 12/14
30/10/2023, 21:10 DDD, Hexagonal, Cebola, Limpa, CQRS, ... Como eu juntei tudo – @hgraca

Você deve ter notado que não há dependência entre o Bus e o Command, a Query nem os Handlers. Isso
ocorre porque eles devem, de fato, não ter consciência um do outro, a fim de proporcionar uma boa
dissociação. A maneira como o Bus saberá qual Handler deve lidar com qual Command, ou Query, deve ser
configurado com mera configuração.

Como você pode ver, em ambos os casos, todas as setas, as dependências, que atravessam a fronteira do
núcleo do aplicativo, apontam para dentro. Como explicado anteriormente, esta é uma regra fundamental da
Arquitetura de Portos e Adaptadores, Arquitetura de Cebola e Arquitetura Limpa.

Conclusão

O objetivo, como sempre, é ter uma base de código que seja fracamente acoplada e altamente coesa, para que
as mudanças sejam fáceis, rápidas e seguras de fazer.

https://herbertograca.com/2017/11/16/explicit-architecture-01-ddd-hexagonal-onion-clean-cqrs-how-i-put-it-all-together/ 13/14
30/10/2023, 21:10 DDD, Hexagonal, Cebola, Limpa, CQRS, ... Como eu juntei tudo – @hgraca

Planos são inúteis, mas planejamento é tudo.

Eisenhower
Este infográfico é um mapa conceitual. Conhecer e entender todos esses conceitos nos ajudará a planejar uma
arquitetura saudável, uma aplicação saudável.

No entanto:

O mapa não é o território.

Alfred Korzybski
Significando que estas são apenas orientações! A aplicação é o território, a realidade, o caso de uso
concreto onde precisamos aplicar nosso conhecimento, e é isso que definirá como será a arquitetura
real!

Precisamos entender todos esses padrões, mas também sempre precisamos pensar e entender
exatamente o que nossa aplicação precisa, até onde devemos ir em prol da dissociação e da coesão. Essa
decisão pode depender de muitos fatores, começando com os requisitos funcionais do projeto, mas também
pode incluir fatores como o período de tempo para construir o aplicativo, a vida útil do aplicativo, a
experiência da equipe de desenvolvimento, e assim por diante.

É assim, é assim que eu faço sentido de tudo. É assim que eu racionalizo isso na minha cabeça.

Eu expandi essas ideias um pouco mais em um post de acompanhamento: Mais do que camadas concêntricas.

No entanto, como explicitamos tudo isso na base de código? Isso é o assunto de um dos meus próximos
posts: como refletir a arquitetura e o domínio, no código.

Por último, mas não menos importante, graças ao meu colega Francesco Mastrogiacomo, por me ajudar a
fazer o meu infográfico ficar bonito.

https://herbertograca.com/2017/11/16/explicit-architecture-01-ddd-hexagonal-onion-clean-cqrs-how-i-put-it-all-together/ 14/14

Você também pode gostar