Esculpindo o monolito JavaEE em microsserviços: prefira verticais, não camadas

O texto original é de Christian Posta, e está disponível em: http://blog.christianposta.com/microservices/carving-the-java-ee-monolith-into-microservices-perfer-verticals-not-layers/


O monolito que explorarei nesses artigos será do tutorial Ticket Monster, que tem sido um exemplo canônico há muito tempo sobre como construir uma aplicação impressionante com JavaEE e as tecnologias RedHat. Usaremos o Ticket Monster porque é uma aplicação bem escrita situada entre o “não-trivial” e o “complexo demais para um exemplo”. É perfeito para propósitos ilustrativos e podemos apontar algumas coisas dele concretamente e discutir prós e contras de algumas abordagens com código de verdade. Por favor, veja o domínio e a arquitetura para as discussões.

ticket-monster_tutorial_gfx_ticket-monster-architecture-4

Observando a arquitetura atual, podemos ver que algumas coisas já estão bem separadas. Temos componentes de interface do usuário (UI), serviços de negócio, e o armazenamento de persistência, bem separados e desacoplados um dos outros, mas empacotados em um único implantável (no caso, um arquivo WAR). Se examinarmos o código fonte, vemos que o código tem uma estrutura parecida. Se fôssemos implantá-lo, qualquer mudança em qualquer um dos componentes exigiria um build, test, e release de todo o implantável. Um dos pré-requisitos de fazer microsserviços é a autonomia dos componentes, para que possam ser desenvolvidos, testados e implantados isoladamente, sem perturbar o resto do sistema. Então, e se desmontarmos as diferentes camadas e implantarmo-nos independentemente? Conseguiríamos alguma autonomia?

Muito tempo foi gasto no passado discutindo para esse tipo de arquitetura, e até parece ter algum sentido. Escalamos a camada web se precisarmos responder a mais requisições. Escalamos a camada de serviços se se tornar um gargalo. Lidamos e gerenciamos os bancos de dados e a camada de acesso aos dados independentemente do restante das aplicações e serviços. “Desacoplar” a lógica de UI  das camadas do meio e o acesso aos dados é um bom princípio, mas não confunda isso com camadas necessárias.

O que realmente acontece na prática é que todos esses componentes arquiteturais, mesmo com toda essa separação de responsabilidades, sucumbe aos caprichos dos dados e do banco de dados. Podemos adicionar quantas CPUs quisermos, todas as camadas do meio ou de UI, mas não importa quão rápidas são a rede, a computação, a memória se tornaram, o gargalo desse tipo de sistema são os modelos de domínio competindo entre si e, ultimamente, o banco de dados. Há uma ênfase aqui em “modelos de domínio“… Algumas empresas de Internet que praticam microsserviços podem não ter domínios complexos, ambíguos e contraditórios, como institutos financeiros, ou empresas de seguros ou empresas varejistas. Por exemplo, o Twitter tem um domínio simples, publicar e apresentar tweets, mas isso se torna complicado em larga escala. As empresas estão começando a ter os dois problemas ao mesmo tempo, o modelo do domínio e sua complexidade são tão importantes quanto sua escalabilidade, (e normalmente dificultam os esforços de escalabilidade). Agora você pensa “vamos usar um banco NoSQL como MongoDB para podermos escalar nosso backend”… e agora você tem ainda mais problemas.

E as equipes? Outra parte de arquitetar um sistema como esse, em camadas, é ter equipes especialistas trabalhando independentemente nessas camadas em ritmos diferentes, em locais diferentes, com ferramentas diferentes, etc. Só precisam compartilhar uma interface entre si e podem trabalhar de forma autônoma. Isso segue a lei de Conway:

empresas que desenham sistemas […] são forçadas a produzir desenhos que são cópias das estruturas da comunicação dessas empresas

Infelizmente, a verdade é o contrário do que se pensa. Não é fazendo essa arquitetura que criamos a oportunidade de especialização de equipes e eficiência. É por causa da estrutura organizacional que somos forçados a criar esse tipo de arquitetura. Assim como temos equipes de bancos de dados, de UI, de segurança, de operações, de qualidade, de build e release, etc, etc. É assim que as empresas são organizadas a décadas. No entanto, se você reparar no sucesso das empresas que praticam microsserviços, há algo um pouco diferente na estrutura organizacional.

Vamos ver o que acontece. Usando a aplicação Ticket Monster, a equipe de negócio pede para alterar a forma como a administração do website é feita. Pedem para adicionarmos uns campos extras relacionados ao rastreamento da frequência que os shows são adicionados e removidos do site porque querem adicionar uma análise preditiva quais eventos podem ou não ser bons no futuro com base na data, na localização, na previsão do tempo, etc. Isso pode envolver a equipe de UI se tivermos que mostrar essa análise para os usuários da administração. Vai certamente envolver a equipe das camadas de serviço de negócio da aplicação. E certamente vai impactar em mudanças no banco de dados. Queremos adicionar uma funcionalidade que força uma reverberação (ou um efeito cascata) em todas as camadas da aplicação e, mais importante ainda, em todas as equipes envolvidas. Agora precisamos de gerentes de projeto coordenando e rastreando reuniões com todas as equipes. Temos que criar tíquetes para que as equipes de UI e DB façam as coisas, sem contar que as equipes de qualidade, segurança, operações, etc também devem embarcar nessa mudança. Tudo isso cria pontos de sincronização complexos entre as equipes, e todas as mudanças, builds e releases de todas as camadas devem ser coordenadas (e fazer a implantação de tudo junto!) Não é o tipo de autonomia que desejamos. Não conseguimos fazer mudanças de forma independente entre si, e acabamos ficando bem fragilizados.

Para nossa aplicação do Ticket Monster, vamos preferir dividir a aplicação em “verticais” coesos, não em camadas tecnológicas ou organizacionais. Cada vertical terá sua própria UI (ou componente UI), serviços de negócio e banco de dados, que são específicos para a função de administração do site. (Para os primeiros passos, no entanto, vamos deixar a interface gráfica como um monolito e quebrar as peças por trás. Voltaremos para quebrar a UI depois, pois possui seus próprios desafios.) O Ticket Monster também permite aos usuários publicar críticas e agendar pedidos para shows. Vamos separá-lo em sua própria vertical. Também pode ter lealdade, recomendações, busca, anúncios, personalização, etc. Vamos separá-los em suas próprias verticais, cada uma com seu próprio banco de dados, UI e pontos de integração (serviços REST, backends, etc). Se um dia precisarmos mudar a funcionalidade de Lealdade do site, não precisaremos fazer a implantação de todo o monolito, com serviços de negócio ou qualquer outra coisa relacionada a Busca, por exemplo. Posso implantar o pedaço de Lealdade, da UI ao banco de dados, que eu precisar sem forçar mudanças que impactem outros serviços. Idealmente, uma equipe será dona e operará cada serviço.

next-arch

Isso também nos dá mais coesão dentro do código, e mais autonomia entre os serviços. Uma vez que você tiver em mente o que significa dividir em torno de funções de negócio verticais, podemos explorar para cada vertical como é o seu contexto limitado, ou se faz sentido ou não aplicar CQRS* nesse contexto. Ou que tipo de banco de dados deveria ser usado baseado nos padrões de leitura/escrita (Documento? Relacional? Grafo?) Ou se você favorece consistência ou se pode tolerar alguma perda ou inconsistência de dados. Ou que tipo de transações, compensações, desculpas, etc, devem ser. E assim por diante. Podemos tomar decisões com base no que é melhor para cada serviço individualmente, e não com base no melhor denominador comum para uma camada ou monolito. É o que exploraremos no próximo post! Fique ligado!

Atualização

Alguém no Twitter (obrigado, @herrwieger) me apontou para isso: Sistemas Autocontidos (SCS), que articula sobre esse conceito que bloguei aqui. Foi certeiro e exatamente ao que eu estava me referindo. O mais interessante é quando exploramos cada sistema autocontido dentro de um contexto limitado e como quebramos em microsserviços mais granulares, se necessário. As fronteiras são uma importante consideração ao falar de um monolito, e foi o que mencionei aqui e o que SCS definem.


*CQRS, ou Command Query Responsibility Segregation, é quando o modelo para inserir ou atualizar o banco é diferente do modelo usado para ler a informação. Ou seja, você tem um comando para inserir segregada, diferente, da query usada para buscar. Exemplo: você insere eventos no banco (“nome”, “tipo”, “hora”), mas precisa fazer uma query que traga os intervalos entre os eventos (“intervalo”, “tipoDeEvento”, “tempoDoIntervalo”).

Anúncios
Marcado com: , ,
Publicado em Microsserviços

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s

%d blogueiros gostam disto: