DEV Community

Cover image for Design: Monolitos Modulares - Parte 1
William Santos
William Santos

Posted on

Design: Monolitos Modulares - Parte 1

Design: Monolitos Modulares

Olá!

Este é mais um post da sessão Design e, desta vez, falaremos sobre um tema que, volta e meia, reaparece: monolitos.

Entretanto, a intenção deste post é expor uma forma diferente de lidar com este estilo arquitetural: monolitos modulares.

Esta abordagem ajuda a manter o monolito organizado, coeso e menos acoplado e, por isso, é ótima para evitar a chamada "big ball of mud" (grande bola de lama), que se caracteriza por um código com muitas dependências entre as funcionalidades, circulares inclusive, o que torna mais difícil manter e evoluir uma dada aplicação.

Vamos lá!

O Monolito

Durante a febre dos microsserviços, o monolito passou a ser tratado como obsoleto. E muito desse tratamento veio da falta de compreensão sobre quais cenários podem se beneficiar deste modelo, tornando muitas aplicações desnecessariamente complexas quando poderiam ser mais simples. Infelizmente, como efeito colateral deste tratamento, acabamos por ver a repetição da "big ball of mud" neste estilo, o que chamamos de "monolito distribuído", uma vez que os mesmos erros de gestão de dependências que ocorriam nos monolitos, afetaram também os processos distribuídos.

A imagem abaixo ilustra este cenário:
Monolito Distribuído

Nota: Algo importante, a esta altura, é entender que, além de repetir a "big ball of mud", há um outro efeito colateral: aumento da complexidade, que leva a um aumento direto do custo do software. Este aumento de complexidade se dá pela preocupação com falhas na rede, mensageria, aglutinação de logs entre outras. Ou seja, uma escolha inadequada por um estilo arquitetural pode levar o negócio a ter maiores despesas com o software, aumentando seu custo operacional.

Vejamos, então, quando o monolito faz sentido para, em seguida, entendermos como a ideia de módulos se ajusta a ele.

Lei de Conway

A Lei de Conway (em inglês) nos diz que, em organizações onde se desenvolve software, este tende a replicar a estrutura de comunicação dessas organizações.

Isso, no que tange à escolha de um estilo arquitetural, significa dizer que não faz sentido utilizar microsserviços quando uma dada aplicação não é mantida por múltiplos times, onde cada um é responsável por um dado conjunto de funcionalidades e, mais importante, onde todos precisem realizar deploys independentemente.

Este último critério, por sinal, é a régua do polegar para a escolha de microsserviços em detrimento do monolito. Se não há a necessidade de deploys independentes, não há razão para que se escolha microsserviços e, neste caso, monolito se torna o estilo de primeira escolha.

A ideia de modularidade

Modularidade, em software, significa criar conjuntos de funcionalidades agrupadas por coerência, e separadas do programa principal, aquele que serve de ponto de entrada.

Para os desenvolvedores dotnet e Java, por exemplo, isso seria equilavente a ter bibliotecas/pacotes, que vou chamar de componentes para generalizar, que representam as funcionalidades, separadas do programa que executa o método Main.

A partir desta separação, e do agrupamento entre as funcionalidades, seria definida uma API (não confundir com Web API) que serviria de ponto de entrada a este grupo, a partir do qual toda a comunicação entre o este grupo e os demais seria mediada.

Encapsulamento

Para garantir que a modularidade seja respeitada, é necessário isolar o código da API do restante do código contido no módulo e, para isso, é necessário ocultar o código interno às funcionalidade.

Para programadores dotnet, por exemplo, isso se faz possível pelo uso da palavra-chave internal, que permite que um dado código seja executado apenas a partir do arquivo onde o mesmo se encontra.

Como neste cenário apenas o código da API ficará visível, não será possível invocar o código ocultado em seu componente, tornando mais fácil manter a modularidade.

O resultado final seria um esquema como o da imagem abaixo:

Componentes Isolados

Isolando a interface com o usuário

Outro ponto de isolamento importante é aquele feito sobre a interface do usuário. Da mesma forma que o acesso a dados é entendido como parte da infraestrutura da aplicação, assim o é para as interfaces (ou controllers no caso de uma Web API). Assim sendo, é recomendável que sejam criados componentes separados para essas interfaces.

Em um projeto ASP.NET, por exemplo, haveria em uma solução um projeto como ponto de entrada da aplicação (aquele que possuirá o método Main e a classe de Startup), e um outro projeto para cada módulo com seus respectivos controllers (e/ou views).

Esta separação é importante pois, conforme a evolução da organização, caso surja a necessidade de realizar a decomposição da aplicação em serviços, basta mover estes projetos para uma nova solução, mantendo a infra (aplicação Web) separada do domínio (componente).

Isolando o acesso a dados

Outro ponto muito importante é manter o mesmo isolamento das funcionalidades no nível dos dados. Para que a modularidade se mantenha, cada módulo é exclusivamente responsável por obter seus dados, condição esta que pode ser forçada por meio da criação de esquemas separados em uma base de dados (ou mesmo o uso de múltiplas bases), e impedindo que joins sejam feitos entre eles a partir de restrições a cada usuário do banco associado a um módulo.

Desta forma, chegaríamos a um esquema semelhante ao da imagem abaixo:

Dados Isolados

DDD

A abordagem que considero mais indicada para a correta separação dos módulos é a ideia de bounded contexts do DDD. Modelando-se o domínio a partir deles, a separação entre os módulos se define automaticamente, assim como a comunicação entre os mesmos pode ser mapeada com maior precisão.

Esta abordagem nos faria chegar a um esquema semelhante ao da imagem abaixo:
Modularidade com DDD

Conclusão

Com este simples conjunto de princípios é possível manter os módulos de sua aplicação isolados, evitando assim que haja dependências indesejáveis seja por porções de código (classes e interfaces, ou funções), seja por tabelas e outros objetos de banco de dados.

Procedendo desta forma, a aplicação se mantém coesa, fácil de manter e evoluir e, portanto, com menor custo para o negócio (tempo) e para o time (estresse).

É possível que, a esta altura, você tenha notado algo interessante: os princípios mencionados acima são uma reprodução daqueles utilizados em microsserviços. A única diferença é o meio pelo qual a troca de mensagens acontece: com microsserviços ela se dá pela rede e, com monolitos, ela se dá pela memória do processo.
Isso significa, conforme dito na seção "Isolando a interface com o usuário", que uma eventual decomposição da aplicação em serviços, se assim se fizer necessário no futuro, seja facilitada, o que reduz -- e muito! -- a necessidade de uma eventual reescrita por conta de dependências diretas entre porções de diferentes módulos.

Na Parte 2 deste artigo vou me concentrar na aplicação destes princípios em uma aplicação de demonstração. A ideia é mostrar não apenas como o código seria organizado como, também, as diferentes formas de comunicação entre os módulos (síncrona e assíncrona).

Gostou? Me deixe saber pelos indicadores. Tem dúvidas ou sugestões? Deixe um comentário ou me procure pelas redes sociais.

Até a próxima!

Latest comments (10)

Collapse
 
wmscode profile image
Willian Menezes

A ideia de separar o main dos controladores eh nova para mim.
Todos os projetos que trabalhei eh criado um projeto comumente nomeado como {NomeEmpresa}{NomeAplicacao}.API... E nesse projeto de api ficam os controladores e conseguentemente a startup da aplicacao.

Ansioso para ler os proximos artigos e entender um pouco mais sobre a separacao modularizada.

Parabens pelo artigo, muito bom....

Collapse
 
biosbug profile image
Roberson Miguel

Adorei mano, excelente post.

Collapse
 
wsantosdev profile image
William Santos

Valeu, mestre! Muito obrigado. 💙

Collapse
 
marcelgsantos profile image
Marcel dos Santos

Excelente artigo. 👏 Conheci o conceito de 'monolitos modulares' em uma painel incrível sobre o assunto com o Luiz Costa da Beep Saúde e com o Rodrigo Miguel da Resultados Digitais na Codecon. A explicação e os casos de usos apresentados foram excelentes e me ajudaram a ter uma melhor compreensão. ;)

Collapse
 
fernandocardo profile image
Fernando Cardo

A correlação que fez com a Lei de Conway (maldita gravidade da tecnologia) foi brilhante 👏🏻👏🏻

Collapse
 
wsantosdev profile image
William Santos

Valeu, Fernando!

Tão importante quanto entender que a maçã cai, é entender o porquê dela cair. Haha

Muito obrigado pelo feedback! ✌🏾💙

Collapse
 
leandronsp profile image
Leandro Proença

Artigo sensacional! Mto bom mesmo, extremamente necessário em meio a uma indústria que abusa de monolitos distribuídos

Collapse
 
wsantosdev profile image
William Santos

Muito obrigado, Leandro! Tanto pelo feedback quanto pelo compartihamento.

E, sim, precisamos falar sobre isso. Monolitos Distribuídos são um problema sério que, infelizmente, só se resolve quando aprendemos a prevení-lo. ✌🏾💙

Collapse
 
wiliambuzatto profile image
Wiliam Buzatto

Muito bom xará!

Collapse
 
wsantosdev profile image
William Santos

Valeu, xará! ✌🏾

Fico feliz que tenha gostado. 😃💙