Você está na página 1de 10

30/10/2023, 21:16 Refletindo arquitetura e domínio no código – @hgraca

Refletindo arquitetura e domínio em código

hgraca Arquitetura, Desenvolvimento, As Crônicas da Arquitetura de Software


5 de junho de 2019 9 minutos

Esta postagem faz parte de As Crônicas da Arquitetura de Software, uma série de postagens sobre
arquitetura de software. Neles, escrevo sobre o que aprendi em Arquitetura de software, como penso e como
uso esse conhecimento. O conteúdo desta postagem pode fazer mais sentido se você ler as postagens
anteriores desta série.

Ao criar um aplicativo, a parte fácil é construir algo que funcione. Para construir algo que tenha desempenho
apesar de lidar com uma enorme carga de dados, isso é um pouco mais difícil. Mas o maior desafio é
construir um aplicativo que seja realmente sustentável por muitos anos (10, 20, 100 anos).

A maioria das empresas onde trabalhei tem um histórico de reconstrução de suas aplicações a cada 3 a 5 anos,
algumas até 2 anos. Isso tem custos extremamente altos, tem um grande impacto no sucesso do aplicativo e,
portanto, no sucesso da empresa, além de ser extremamente frustrante para os desenvolvedores trabalharem
com uma base de código confusa e fazê-los querer deixar a empresa. Uma empresa séria, com uma visão de
longo prazo, não pode arcar com nada disso, nem com a perda financeira, nem com a perda de tempo, nem
com a perda de reputação, nem com a perda de clientes, não a perda de talentos.

Refletir a arquitetura e o domínio na base de código é fundamental para a manutenção de um aplicativo e,


portanto, crucial na prevenção de todos esses problemas desagradáveis.

Arquitetura Explícita é como racionalizo um conjunto de princípios e práticas defendidas por


desenvolvedores muito mais experientes do que eu e como organizo uma base de código para fazê-la refletir e
comunicar a arquitetura e o domínio do projeto.

Em meu post anterior, eu falei sobre como eu coloquei todas essas idéias juntos e apresentou alguns
infográficos e UMBroso diagramas para tentar criar algum tipo de mapa conceitual de como eu penso nisso.

No entanto, como é que realmente colocá-lo para a prática em nossa base de código?!

Neste post, Vou falar sobre como eu reflito a arquitetura e o domínio de um projeto no código e vou propor
uma estrutura genérica que eu acho que pode nos ajudar a planejar a manutenção.

Os meus dois mapas mentais

Nos dois últimos posts desta série, expliquei os dois mapas mentais que uso para me ajudar a pensar em
código e organizar uma base de código, pelo menos na minha cabeça.

https://herbertograca.com/2019/06/05/reflecting-architecture-and-domain-in-code/ 1/10
30/10/2023, 21:16 Refletindo arquitetura e domínio no código – @hgraca

O primeiro é composto por uma série de camadas concêntricas, que no final são cortadas para compor os
módulos de domínio do aplicativo, os componentes. Neste diagrama, a direção da dependência vai para
dentro, o que significa que as camadas externas conhecem as camadas internas, mas não o contrário.

O segundo é um conjunto de camadas horizontais, em que o diagrama anterior fica no topo, seguido pelo
código compartilhado pelos componentes ( kernel compartilhado ), seguido por nossas próprias extensões aos
idiomas e, finalmente, pelas linguagens de programação reais na parte inferior. Aqui, a direção das
dependências desce.

https://herbertograca.com/2019/06/05/reflecting-architecture-and-domain-in-code/ 2/10
30/10/2023, 21:16 Refletindo arquitetura e domínio no código – @hgraca

Estilo de codificação arquitetonicamente evidente

Usar um estilo de codificação arquiteturalmente evidente significa que nosso estilo de codificação
(codificação de padrões, classe, métodos e variáveis convenções de nomenclatura, estrutura de código, …) de
alguma forma comunica o domínio e a arquitetura para quem está lendo o código. Existem duas ideias
principais sobre como alcançar um estilo de codificação arquitetonicamente evidente.

“[..] um estilo de codificação arquitetonicamente evidente que permite que você solte dicas para leitores
de código para que eles possam inferir corretamente o design.”

George Fairbanks

O primeiro é sobre o uso dos artefatos de código (classes, variáveis, módulos, ..) nomes para transmitir tanto
domínio e significado arquitetônico. Então, se temos uma classe que é um repositório que lida com entidades
de fatura, devemos nomeá-la como `, o que nos dirá que ele lida com o conceito de domínio Fatura e seu
papel arquitetônico é o de um repositório. Isso nos ajuda a saber e entender onde ele deve estar localizado,
além de como e quando usá-lo. No entanto, acho que precisamos fazer isso com todos os artefatos de código
em nossa base de código, por exemplo, sinto que a pós-fixação de uma entidade com ‘Entity’ é redundante e
apenas adiciona ruído.

“[..] o código deve refletir a arquitetura. Em outras palavras, se eu olhar para o código, eu deveria ser
capaz de identificar claramente cada um dos componentes [...]”

Simon Brown

O segundo é sobre tornar subdomínios explícitos como artefatos de nível superior de nossa base de código,
como módulos de domínio sábio, como componentes.
https://herbertograca.com/2019/06/05/reflecting-architecture-and-domain-in-code/ 3/10
30/10/2023, 21:16 Refletindo arquitetura e domínio no código – @hgraca

Então, o primeiro deve ser claro e eu acho que requer qualquer explicação adicional. No entanto, o segundo é
mais complicado, então vamos mergulhar nisso.

Tornando a arquitetura explícita

Weilitve viu, no meu primeiro diagrama, que no nível de zoom mais alto temos 3 tipos diferentes de código:

Interface do usuário, contendo o código adaptando um mecanismo de entrega a um caso de uso;


O núcleo da aplicação, contendo os casos de uso e a lógica de domínio;
A infraestrutura, contendo o código que adapta as ferramentas/bibliotecas às necessidades do núcleo da
aplicação.

Então, na raiz da nossa pasta de origem, podemos refletir esses tipos de código criando 3 pastas, uma para
cada tipo de código. Essas três pastas representarão três namespaces e, mais tarde, podemos até criar um teste
para afirmar que tanto a interface do usuário quanto a infraestrutura sabem sobre o núcleo, mas não o
contrário, em outras palavras, podemos testar que a direção das dependências vai para dentro.

Interface do usuário

Dentro de uma aplicação empresarial web, é comum ter várias APIs, por exemplo, uma API REST para
clientes, outra para web-hooks usados por aplicações 3rd party, talvez uma API SOAP legada que ainda
precise ser mantida, ou talvez uma API GraphQL para um novo aplicativo móvel…

Também é comum que tais aplicações tenham vários comandos CLI usados por trabalhos Cron ou operações
de manutenção sob demanda.

E, claro, ele terá o próprio site, usado por usuários regulares, mas talvez também outro site usado pelos
administradores do aplicativo.

Estas são todas as visões diferentes sobre o mesmo aplicativo, eles são todos diferentes interfaces de usuário
do aplicativo.

https://herbertograca.com/2019/06/05/reflecting-architecture-and-domain-in-code/ 4/10
30/10/2023, 21:16 Refletindo arquitetura e domínio no código – @hgraca

Portanto, nosso aplicativo pode realmente ter várias interfaces de usuário, mesmo que algumas delas sejam
usadas apenas por usuários não humanos (outras aplicações de 3o partido). Letilits refletem isso por meio de
pastas/namespaces para separar e isolar as várias interfaces de usuário.

Temos principalmente 3 tipos de interfaces de usuário, APIs, CLI e sites. Então vamos começar fazendo essa
diferença explícita dentro do UserInterface namespace de raiz criando uma pasta para cada um deles.

Em seguida, vamos mais fundo e dentro do namespace para cada tipo e, se necessário, criamos um
namespace para cada UI (talvez para a CLI que precisamos fazer).

A infraestrutura

De forma semelhante à Interface do Usuário, nosso aplicativo usa várias ferramentas (bibliotecas e
aplicativos de 3a parte), por exemplo, um ORM, uma fila de mensagens ou um provedor de SMS.

Além disso, para cada uma dessas ferramentas, talvez precisemos ter várias implementações. Por exemplo,
considere o caso em que uma empresa se expande para outro país e, por razões de preços, é melhor usar um
provedor de SMS diferente em cada país: precisaremos de implementações de adaptadores diferentes usando
a mesma porta para que possam ser usadas de forma intercambiável. Outro caso é quando estamos
refatorando o esquema do banco de dados, ou mesmo mudando o mecanismo DB, e precisamos (ou
decidimos) também mudar para outro ORM: então teremos 2 adaptadores ORM conectados em nossa
aplicação.

https://herbertograca.com/2019/06/05/reflecting-architecture-and-domain-in-code/ 5/10
30/10/2023, 21:16 Refletindo arquitetura e domínio no código – @hgraca

Então, dentro do namespace Infrastructure começamos criando um namespace para cada tipo de ferramenta
(ORM, MessageQueue, SmsClient), e dentro de cada um deles criamos um namespace para cada um dos
adaptadores dos fornecedores que usamos (Doctrine, Propel, MessageBird, Twilio, ...).

O Núcleo

Dentro do Core, no nível mais alto de zoom, temos três tipos de código, o Componentes, o Kernel
Compartilhado e o Portos. Assim, criamos pastas/namespaces para todos eles.

Componentes

No namespace Components, criamos um namespace para cada componente e, dentro de cada um deles,
criamos um namespace para a camada Application e um namespace para a camada Domain. Dentro dos
namespaces de Aplicação e Domínio, começamos apenas despejando todas as classes e à medida que o
número de classes cresce, nós começamos a agrupá-los conforme necessário (Eu acho que é muito zeloso
criar uma pasta para colocar apenas uma classe nela, então eu prefiro fazê-lo quando a necessidade disso
surgir).

https://herbertograca.com/2019/06/05/reflecting-architecture-and-domain-in-code/ 6/10
30/10/2023, 21:16 Refletindo arquitetura e domínio no código – @hgraca

Neste ponto, precisamos decidir se devemos agrupá-los por assunto (fatura, transação, ..) ou por função
técnica (repositório, serviço, objeto de valor, ..), mas eu sinto que, seja qual for a escolha, ela realmente tem
muito impacto porque estamos nas folhas da árvore da organização, então, se necessário, é fácil fazer
alterações nesse último bit de estrutura sem muito impacto para o resto da base de código.

Portos

O namespace Ports conterá um namespace para cada ferramenta que o Core usa, assim como fizemos para a
Infrastructure, onde colocaremos o código que o núcleo usará para usar a ferramenta subjacente.

Esse código também será usado pelos adaptadores, cuja função é traduzir entre a porta e a ferramenta real. Na
sua forma mais simples, uma porta é apenas uma interface, mas em muitos casos também precisa de objetos
de valor, DTOs, serviços, construtores, objetos de consulta ou mesmo repositórios.

https://herbertograca.com/2019/06/05/reflecting-architecture-and-domain-in-code/ 7/10
30/10/2023, 21:16 Refletindo arquitetura e domínio no código – @hgraca

Kernel Compartilhado

No Kernel Compartilhado, colocaremos o código que é compartilhado entre Componentes. Depois de


experimentar diferentes estruturas internas para o Kernel Compartilhado, posso decidir sobre uma estrutura
que se encaixe em todos os cenários. Eu sinto que para algum código faz sentido separá-lo por componente
como fizemos em Core\Component (ou seja. Entity IDs claramente pertencem a um componente),
mas outros casos não tanto (ou seja. Eventos podem ser acionados e ouvidos por vários componentes, então
eles não pertencem a nenhum). Talvez uma mistura seja o melhor ajuste.

Extensões de idioma userland

Por último, mas não menos importante, temos nossas próprias extensões para o idioma. Como explicado no
post anterior desta série, este é um código que poderia fazer parte da linguagem, mas, por algum motivo, não
é. No caso do PHP, podemos pensar, por exemplo, em uma classe DateTime baseada na fornecida pelo PHP,
mas com alguns métodos extras. Outro exemplo poderia ser uma classe UUID, que embora não fornecida
pelo PHP, é por natureza muito asséptica, agnóstica de domínio e, portanto, poderia ser usada por qualquer
projeto independentemente do domínio.

https://herbertograca.com/2019/06/05/reflecting-architecture-and-domain-in-code/ 8/10
30/10/2023, 21:16 Refletindo arquitetura e domínio no código – @hgraca

Este código é usado como se fosse fornecido pela própria linguagem, por isso precisa estar totalmente sob
nosso controle. Isso não significa, no entanto, que podemos usar bibliotecas do 3o partido. Podemos e
devemos usá-los quando fizer sentido, mas eles devem ser embrulhados por nossa própria implementação
(para que possamos alternar facilmente a biblioteca subjacente do 3o partido), que é o código que será usado
diretamente na base de código do aplicativo. Eventualmente, pode ser um projeto por conta própria, em seu
próprio repositório CVS, e usado em vários projetos.

Reforçando a arquitetura

Todas essas idéias e a maneira como decidimos colocá-las em prática, são muito para absorver e não são
fáceis de dominar. Mesmo que dominemos tudo isso, no final somos apenas humanos, então vamos cometer
erros e nossos colegas vão cometer erros, é exatamente do jeito que vai.

Assim como cometemos erros no código e temos um conjunto de testes para evitar que esses erros cheguem à
produção, devemos fazer o mesmo com a estrutura de base de código.

Para fazer isso, no mundo PHP temos uma pequena ferramenta chamada Deptrac (mas aposto que existem
ferramentas semelhantes para outras línguas também), criadas pela Sensiolabs. Nós o configuramos usando
um arquivo yaml, onde definimos as camadas que temos e as dependências permitidas entre elas. Em
seguida, executamos através da linha de comando, o que significa que podemos executá-lo facilmente em um
CI, assim como executamos um conjunto de testes no CI.

Podemos até fazer com que ele crie um diagrama das dependências, que nos mostrará visualmente as
dependências, incluindo as que quebram o conjunto de regras configurado:

https://herbertograca.com/2019/06/05/reflecting-architecture-and-domain-in-code/ 9/10
30/10/2023, 21:16 Refletindo arquitetura e domínio no código – @hgraca

Conclusão

Uma aplicação é composta por um domínio e uma estrutura técnica, a arquitetura. Esses são os diferenciais
reais em um aplicativo, não as ferramentas, bibliotecas ou mecanismos de entrega usados. Se queremos que
um aplicativo seja sustentável por um longo tempo, ambos precisam ser explícitos na base de código, para
que os desenvolvedores possam saber sobre isso, entendê-lo, e assim por diante, cumpra-o e evolua-o ainda
mais conforme necessário.

Essa explicitação nos ajudará a entender os limites à medida que codificamos, o que, por sua vez, nos ajudará
a manter o design do aplicativo modular, com alta coesão e baixo acoplamento.

Mais uma vez, a maioria dessas idéias e práticas que eu tenho falado em meus posts anteriores, vêm de
desenvolvedores muito melhores e mais experientes do que eu. Eu discuti-los longamente com muitos dos
meus colegas em diferentes empresas, eu experimentei com eles em bases de código de aplicações
empresariais, e, e eles têm trabalhado muito bem para os projetos com os quais Iroitve esteve envolvido.

No entanto, acredito que não balas de prata, não uma bota se encaixa em tudo, não Santo Graal.

Essas ideias e a estrutura que eu refiro neste post devem ser vistas como um modelo genérico que pode ser
usado na maioria das aplicações corporativas, mas, se necessário, deve ser adaptado sem arrependimentos.
Precisamos sempre avaliar o contexto (o projeto, a equipe, o negócio, ..) e fazer o melhor que pudermos, mas
acredito e espero que este modelo seja um bom ponto de partida ou, no mínimo, alimento para o pensamento.

Se você quiser ver isso implementado em um projeto de demonstração, eu bifurquei e refatorei o aplicativo
Symfony Demo para usar essas ideias. Você pode verificar como eu fiz isso aqui.

https://herbertograca.com/2019/06/05/reflecting-architecture-and-domain-in-code/ 10/10

Você também pode gostar