O documento discute estratégias para refatorar um sistema monolítico em microsserviços, abordando tópicos como identificação de domínios, implementação de novos requisitos, divisão do monolito e refatoração do banco de dados. Uma estratégia sugerida é criar serviços que se comunicam com o monolito através de uma camada anti-corrupção para garantir o sincronismo dos dados durante a transição.
2. MINDSET
“ Decompor uma aplicação existente monolítica, requer pensar em uma estratégia de
desacoplamento, migração de dados, convivência entre sistemas e tecnologias. “
MONOLITO
3. MONOLITO MINDSET
Identificar os
domínios (DDD)
Go Macro first, then micro Atomic Evolutionary Steps
+ +
Anti-corruption Layer
Sincronismo dos
dados
Réplica de dados
Colaboração entre
serviço e monolito
LEGADO
5. Estratégias de refatoração
● Isolar chamadas ao monolito
● Separar a camada de apresentação do
backend
● Implementar novos requisitos como serviços
● Dividir o monolito
(*) A quarta abordagem é a única capaz de migrar
completamente o monolito para a estrutura de microsserviços
https://microservices.io/patterns/refactoring/strangler-application.html
(*)
6. Blindar o monolito atrás de um proxy reverso para garantir um virada gradual e de fácil rollback.
1 - Isolar chamadas ao monolito
7. 2 - Separar a camada de apresentação do backend
● Camada de apresentação - Camada
HTTP, páginas HTML, …
● Camada de negócio - Módulos com
a lógica de negócio.
● Camada de dados - Módulos com
acesso a dados, Banco de dados ou
Brokers.
Essa solução transforma o monolito
em 2 monólitos menores.
É uma solução parcial e não resolve
o problema.
8. 2 - Implementar novos requisitos como serviços
Monolito
New
Service
Old features New features
● API Gateway
Roteia chamadas novas para o sistema
estrangulador e chamadas antigas para o
monolito
● Integration Glue Code
Integra o serviço com o monolito.
Habilita o serviço a consultar dados e
invocar funcionalidades que estejam no
monolito
Monolito
New
Service
adapters adapters
event
publisher
event
subscriber
Integration
glue
9. 3.a - Dividir o monolito : Dividindo o modelo de domínio
A idéia é encontrar e desfazer todas as dependências que
estão fora do domínio do novo serviço.
Utilizar DDD: Ao invés de carregar a referência como uma
dependência em código. Utiliza-se o conceito de aggregate e
a dependência se torna um Id para o outro aggregate.
Esse processo vai além de “mover código”. Muitas classes são persistidas em banco. Ou seja, movê-las
implica em também mover os dados. É necessário “mover as tabelas” do banco do legado para o banco
do serviço.
MONOLITO
10. 3.b - Dividir o monolito : Corte “vertical”
Fazer um corte “vertical” no monolito,
incluindo:
● Adaptadores de entrada (API)
● Lógica de domínio
● Adaptadores de saída
● Schema do database do monolito
Dificuldades:
1. Dividir o modelo de domínio
(Separar as dependências)
2. Refatorar o banco de dados
12. Refatorar o banco de dados
● Replicar dados para evitar mudanças muito grandes no monolito:
Mover as classes para o serviço geralmente gera um grande refactor no monolito.
Para evitar esse problema pode-se:
● Manter o schema do banco original.
● Utilizar gatilhos para garantir o sincronismo entre as bases.
● Deixar os dados no monolito como somente leitura e fazer o sincronismo dos dados do
serviço para o monolito.
● Levar os dados gradualmente para o banco do serviço.
Para os domínios que foram extraídos. Seus respectivos serviços passam a ser a fonte da verdade,
e o monolito apenas uma réplica.
14. Colaboração entre serviço e monolito (integration glue code)
É comum que o serviço e o monolito façam algum tipo de
colaboração.
Eventualmente o serviço precisa de algum dado que está no
monolito ou precisa acessar suas operações e vice-versa.
Monolito
New
Service
adapters adapters
event
publisher
event
subscriber
Integration
glue
● O código do serviço não deve conheça
a implementação do “integration glue
code”.
● O mecanismo utilizado para a
obtenção dos dados estará
encapsulado.
● Será um repository quando precisar
de dados (mesmo que seja uma
requisição HTTP ao monolito)
● Será um service quando precisar
executar algo
● O código do serviço não deve
conhecer a implementação do
“integration glue code”.
● O mecanismo utilizado para a
obtenção dos dados estará
encapsulado.
● Será chamado repository quando
precisar de dados (mesmo que seja
uma requisição HTTP ao monolito)
● Será chamado service quando
precisar consumir algo do monolito.
15. Garantir que o próprio micro serviço sanitize sua base, ou seja, que ele seja capaz de transpor o dado do
legado para o seu banco quando for preciso. Desta forma evitaremos fazer grandes migrações de dados no
início da transição, podendo deixar isso para um segundo momento.
MONOLITO Integridade / Elasticidade
LEGADO
select ( 1* )
NOT_FOUND
select ( 1* )
ou GET xxx
1*
insert ( 1* )
repository
16. Colaboração entre serviço e monolito - ACL (Anti-corruption Layer)
O objetivo da camada ACL, é garantir que o domínio do monolito não impacte no domínio do
serviço e vice-versa.
Essa camada existe tanto para requisições síncronas quanto para as assíncronas.
Essa camada também existe no monolito, se este precisar de dados ou funcionalidades do serviço
É uma camada de código que faz a tradução entre os diferentes domínios
17. Colaboração entre serviço e monolito - Como o monolito vai publicar e assinar eventos?
Em geral é fácil para um novo serviço subscrever ou publicar eventos. Contudo, para o monolito isto
é um desafio.
Encontrar todos os locais onde alguma entidade é alterada e fazer esse trecho de código
publicar evento é desafiador
Tarefa geralmente difícil de fazer no monolito e muito sujeita a falhas.
É mais fácil utilizar “transaction log tailing” para publicar eventos
sempre que o dado no banco for alterado. Entretanto, será difícil
publicar eventos de domínio, os eventos ficarão muito associados às
tabelas.
A forma mais fácil é uma “aplicação ajudante” que vai
subscrever aos eventos do serviço e alterar
diretamente a base do legado.
18. Colaboração entre serviço e monolito - Replicando dados de domínios não extraídos
Fazer chamadas síncronas ao monolito, é simples, porém aumenta a indisponibilidade do serviço.
O ideal é subscrever nos eventos que alterem os dados no monolito e manter uma réplica dos
dados no serviço.
Os dados replicados são um subconjunto dos dados totais, apenas o que o serviço precisa.
As principais vantagens dessa abordagem são:
● Não sobrecarrega o monólito com chamadas de query
● Não impacta na disponibilidade do serviço
● Os dados podem ser modelados no serviço de forma a otimizar a consulta
O maior desafio é manter os dados sincronizados!
19. Colaboração entre serviço e monolito - Garantindo o sincronismo dos dados com SAGA
Em um sistema distribuído, não é possível fazer rollback de uma operação de maneira simples
como ocorre em um SGBD relacional. Para garantir a consistência de dados utiliza-se SAGA.
Os termos chaves da SAGA são:
Saga - Sequência de transações locais coordenadas por mensageria.
Compensatable transaction - Uma transação local que mesmo bem sucedida não garante que a SAGA
funcionará. Será necessário desfazer as alterações dessa transação local caso a SAGA falhe.
Compensating transaction - Uma transação que desfaz uma atualização feita por uma transação local.
Pivot transaction - Transação limítrofe da falha. Se der sucesso não há mais como a SAGA falhar.
Retriable transaction - Transações que ocorrem após a transação pivot e são garantidas de serem bem
sucedidas.
Countermeasure - Técnica usada para gerir a falta de isolamento entre as transações locais.
Semantic lock - Técnica para informar que um registro está sendo atualizado por uma saga.
two-phase commit protocol
poderia ser uma alternativa, mas
não é escalável e compromete a
disponibilidade.
21. Quais serviços extrair e quando
● Definir a arquitetura ideal: Primeiro, deve-se estudar qual seria a estrutura ideal (MICROSERVICE GUIDELINE vai
ajudar neste ponto)
● Definir o conjunto de serviços com maior benefício a serem extraídos.
● Revisar a arquitetura definida: A definição anterior não será escrita em pedra. Com o amadurecimento na
extração, é importante que a estrutura ideal seja avaliada e alterada, caso necessário.
Pontos importantes para a escolha da ordem de extração: (Definindo “valor”)
● Acelerar o desenvolvimento: Se uma parte da aplicação será muito alterada no próximo ano;
● Resolver um problema de performance, escalabilidade ou disponibilidade: Se uma parte da aplicação
provoca alguns desses problemas;
● Habilitar que outros serviços sejam extraídos: Quando a extração de uma parte irá facilitar a extração de
diversas outras por causa de suas dependências.
● Evitar que o monolito precise de Compensatable transaction: Geralmente é difícil implementar
Compensatable transaction no monolito. Sempre que possível, faça a extração de transações que sejam
Pivot(Saga) ou Retriable(Saga).
22. Escolher um serviço de menor complexidade.
Servirá para amadurecer o processo.
Valor :
Complex :
Valor :
Complex :
Valor :
Complex :
Valor :
Complex :
Valor :
Complex :
Seguir a migração com os serviços que entregam maior
“valor”.
Valor :
Complex :
Valor :
Complex :
Valor :
Complex :
Quais serviços extrair e quando : Uma forma simplista
24. Implementando o novo serviço
Após definido qual serviço será extraído, será necessário seguir esses passos:
● Identificar quais commands(alterado dado) e quais queries(não altera dado) “sairão” do monolito para o
serviço.
● Responder às seguintes perguntas:
○ Qual comportamento e dados serão movidos para o novo serviço?
○ Qual API o serviço vai expor para o monolito?
○ Qual API o monolito vai expor para o serviço?
● Decidir quais dados mover:
○ Como o serviço vai acessar os dados que ficaram no monolito?
○ Como o monolito vai acessar os dados que passaram para o serviço?
○ Como vai ser mantido o sincronismo desses dados?
● Encapsular a alteração no monolito:
○ Criar uma interface com as operações que passaram para o serviço
○ O código existente no legado irá implementar essa interface
○ Criar uma nova implementação que chamará o novo serviço
○ Eventualmente utilizar togglz para decidir se o monolito fará o processamento ou o serviço.
○ Quando estiver confiante que o serviço funciona, o código poderá ser removido do legado
26. Aplicação estranguladora
- Oferece novos requisitos como
serviços e também extrai
funcionalidades do monolito como
serviços.
- Composta de micro serviços que
rodam em conjunto com o monolito.
- Com o tempo, a quantidade de
funcionalidades no monolito diminui
até que este desapareça, ou vire um
micro serviço.
- Deve-se evitar alterações amplas no
monolito. Contudo, será inevitável
que alguns ajustes sejam feitos para
dar suporte aos micro serviços.
https://microservices.io/patterns/refactoring/strangler-application.html
27. MONOLITO Problemas conhecidos
Durante a migração
God classes - "Caras" que fazem muitas coisas com alto acoplamento (e baixa coesão)
Depois da migração
Latência de rede
Disponibilidade reduzida em comunicação síncrona
Manter a integridade do dado
28. Depois de ter criado um micro serviço, ter redirecionado o fluxo para o mesmo e ter validado o sistema, será a hora de desligar a
rota/entrada do monolito.
MONOLITO Estratégias para criação de novos artefatos - 3 / 3
fallback