Arquitetura de microserviços ou monolítica?

Dentro da Caelum, temos a experiência de termos criado várias aplicações web, seja para nós mesmos, para clientes ou para iniciativas nossas. Uma dessas aplicações de vital importância para nós é o nosso ERP. É um sistema desenvolvido internamente e que cuida basicamente de toda a empresa, desde o financeiro, RH, pagamento, até as matrículas e contratos dos alunos, contatos com clientes, alocação de instrutores e várias outras funcionalidades. Todas essas funcionalidades estão agrupadas dentro desse grande sistema, fazendo dela uma aplicação monolítica, ou seja, uma aplicação feita em uma só unidade.

Sistema monolítico

Um único sistema, com todos os módulos dentro dele

 

Como tudo em desenvolvimento de software, existem vantagens e desvantagens nos sistemas monolíticos. Um dos principais pontos negativos é que você tem um grande ponto único de falha, que significa que se houver algum erro no cadastro de funcionários que deixe o sistema fora do ar, isso vai levar junto todo o sistema, incluindo funcionalidades que não possuem nenhuma relação com essa funcionalidade, como por exemplo a geração de contrato para alunos. Outro ponto negativo é a base de código, que se torna muito extensa, podendo deixar novos membros do projeto menos produtivos por algum tempo, já que a complexidade do código é bem maior.

Por outro lado, temos um sistema cujo o deploy é fácil de ser feito, já que o banco de dados facilmente evoluirá junto para todas as funcionalidades e há apenas um ponto onde o deploy precisa ser feito. Além disso, não há duplicidade de código e classes necessárias entre os diferentes módulos, já que todas elas fazem parte da mesma unidade.

O cenário da Casa do Código

Ao criarmos a Editora Casa do Código, decidimos seguir outro caminho para o e-commerce. Lá não podemos correr o risco de ficar com a loja fora do ar caso alguma funcionalidade periférica falhe, então quebramos nossa arquitetura em serviços menores. Dessa forma, há um sistema principal, que é a loja e está hospedada no Shopify, e vários outros sistemas que gravitam em torno dela. Temos uma aplicação que faz a liberação dos e-books para os clientes, outro que contabiliza os royalties para autores, outro para cuidar da logística de envio dos livros impressos para o cliente, um painel de visualização dos livros comprados, um para fazer a liberação de vale presentes e outro para promoções.

Arquitetura em microserviços

Vários sistemas que são notificados por outro via HTTP quando um determinado evento ocorre. Cada sistema decide o que fazer com o JSON que é enviado para ele

 

Dessa forma, quando um evento acontece na nossa loja online, os diferentes sistemas precisam ser notificados. Para essas notificações usamos requisições HTTP, assim, quando uma compra é confirmada na loja online, todos os sistemas recebem em um Endpoint essa requisição HTTP, contendo um JSON com todos os dados da compra. Cada sistema decide o que fazer com as informações recebidas, de acordo com a necessidade. Por exemplo, o sistema de liberação dos e-books verifica se a compra possuía e-books e gera os links de download para o comprador, enquanto que o sistema de logística já dá baixa no estoque quando a compra é de um impresso, além de notificar as pessoas quando algum livro está ficando com estoque crítico. Essa característica importante para o maior desacoplamento dos serviços é conhecida como Smart endpoints, dumb pipes, que em uma tradução livre pode ser entendida como Endpoints inteligentes e fluxos simples.

Essa independência entre os diferentes serviços também traz consigo algumas vantagens e também desvantagens. Pelo lado positivo, minimizamos a existência daquele ponto único de falha. Caso algo dê errado no sistema que contabiliza os royalties, a loja continua no ar, os clientes continuam conseguindo baixar seus e-books, ou seja, não compromete a execução de outros servidos. Além disso temos sistemas com base de código menor, facilitando a barreira inicial na compreensão do projeto para um novo membro do time. Outro fator de suma importância é que conseguimos facilmente trabalhar com diferentes tecnologias. Temos serviços desenvolvidos em Java com VRaptor, Java com Play Framework, Rails e novos serviços sendo criados em PHP.

Por outro lado, há uma intersecção de código entre todos os sistemas, gerando uma repetição de código. Por exemplo, o código que recebe a requisição, transforma o JSON vindo do Shopify nos objetos que queremos trabalhar é o mesmo para todos os projetos, assim como classes de modelo. Minimizamos esse impacto com a criação de bibliotecas, que são compartilhadas entre os projetos. Além disso, temos uma pequena duplicidade de informações que raramente sofrem alterações entre os serviços, já que cada um possui sua própria instância de banco de dados.

Essa arquitetura onde temos um grande sistema quebrado em serviços menores e mais leves, por critério de funcionalidades de negócio, integrados via HTTP (ou alguma arquitetura de mensageria) é o que forma a famosa arquitetura de microserviços. Evidentemente, existem outros casos de arquiteturas de microserviços que podem usar diferentes tecnologias, bancos de dados compartilhados entre serviços, serviços que se comunicam com outros serviços e assim por diante.

Faz parte do papel do arquiteto de software tomar decisões baseadas em trade-offs. Nunca teremos uma solução perfeita. Sempre precisamos escolher entre vantagens e desvantagens, como foi o caso da Caelum com seu ERP monolítico e da Casa do Código com a utilização de microserviços. O importante é analisar calmamente qual é a melhor abordagem para cada situação. E você, já trabalhou em algum projeto com arquitetura de microserviços? O que achou? E se quiser saber mais sobre mobile, microserviços, APIs e vários outros assuntos, não deixe de visitar a Mobile Conf, dia 30/05 no Rio de Janeiro!

63 Comentários

  1. Vinicius 07/10/2016 at 15:07 #

    Trabalho com aplicações Web Monolíticas e agora estou estudando microserviços. No caso do sistema que vocês desenvolveram, quando as informações precisam ser replicadas aos demais bancos, quem é responsável por esse serviço? Triggers no DB origem? Implementações de replicação do SGBD? Ou possui um sistema em forma de serviço fazendo isso?
    Obrigado.

  2. Gleizer 16/10/2016 at 08:26 #

    Parabéns pelo seu post.
    Baseado nas suas explicações e nas respostas acima gostaria novamente se tocar no assunto sobre o compartilhamento de dados entre os serviços.
    Ex.: vamos imaginar dois serviços. Um serviço de cadastro e outro de nota fiscal.
    No módulo nota fiscal precisamos de uma lista de notas com o nome dos clientes.
    Qual a melhor forma de montar a lista, levando em consideração que os dois serviços trabalham no mesmo banco banco? Fazer chamadas http passando uma lista de ids para o módulo de cadastro?

  3. Vagner 19/10/2016 at 14:15 #

    Ficou bem claro.

  4. Michel Guedes 01/11/2016 at 10:04 #

    Bem legal o conceito, eu tinha alguma teoria em mente sobre, isso principalmente em aplicações, grandes mas não sabia como se chamava isso até ler esse artigo.

    Uma dúvida: no começo do artigo é mencionado que o intuito do micro-serviço é que se um “módulo” do sistema esteja fora do ar, o restante continue trabalhando normalmente. Daí é usado uma espécie de “hub” ou “bus” que cuida de entregar a mensagem para interações entre os módulos, certo?

    Se esse é o caso, como lidar quando de fato um dos módulos esteja fora, e a mensagem não conseguiria ser entregue para esse módulo?

    Obrigado!

  5. Antonio Lazaro 15/11/2016 at 10:16 #

    @Adriano, ficou bem claro o texto e o vídeo. Alguns aspectos que eu adicionaria como negativo é a questão da orquestração das transações entre serviços (transações distribuídas?), bem como o tratamento e implementação de uma regra de garantia de entrega das mensagens. Como tratar isso? Entendo que o sistema de loja de vocês é o core do negócio da empresa “casa do código”, porém em outros contextos, a complexidade da administração desses requisitos não funcionais devem ser levados em consideração. Estou estudando o refactoring de um grande sistema da empresa que trabalho atualmente e estou pesquisando sobre arquiteturas de microserviços, mas somente ao ler sobre a forma como teríamos que gerenciar transações distribuídas, garantia de entrega das mensagens, dentre outros itens, já considero que não será a melhor opção, devido ao tempo de projeto e expertise do time para tocar esse projeto que envolve diversos desafios de aprendizado nas camadas de apresentação e negócio. Mas foi bastante esclarecedor sua explicação.

  6. LUIZ HENRIQUE CUNHA 06/12/2016 at 11:10 #

    Caramba, video aula perfeita!

  7. Bruno 09/12/2016 at 18:42 #

    Implementei um sistema com micro serviços baseados em JSON, porém todos respondendo a requisições de uma servlet “centralizadora”. Então do lado dos “clientes”, faço tudo com Ajax. Só que recebo os dados por essa servlet centralizadora e encaminho para a classe de destino (que é informada no próprio layout do JSON). Essa seria uma boa solução ? Tecnicamente esta funcionando muito bem, porém estamos problema de performance, e não sei se é por causa de usar Servlet.

  8. Adriano Almeida 12/12/2016 at 10:23 #

    @Bruno, usar Servlet não é o problema, já que qualquer solução que você for usar e que trabalhe com requisições http, muito possivelmente terão Servlets por trás dos panos, mesmo que você algum framework para te ajudar, como o Spring.

    Por experiência própria, eu investigaria primeiro as queries que são feitas no banco e também se a quantidade de requisições AJAX não está sendo excessiva. Depois disso partiria para tentar olhar outras coisas.

  9. Nilo Júnior 12/12/2016 at 14:13 #

    Oi Adriano.

    Dá para usar microserviços através do PHP?
    Especificamente usando o Laravel?
    Você poderia me dá uma ajuda por onde começar?

    Abraços,

  10. G. Marcial 12/12/2016 at 17:36 #

    Parabéns e Obrigado Adriano, sensacional sua explicação, de forma muito bem entendível.

Deixe uma resposta