Padrões de desenvolvimento com Arquiteturas modernas
Circuit Breaker, CQRS, Event Sourcing, Sidecar, Backend para Frontend e Estrangulamento de Software
Muitas aplicações modernas do dia-a-dia tendem a ser construídas com escala industrial, algumas vezes na escala da internet. Cada aplicação precisa concentrar conceitos de escalabilidade, disponibilidade, segurança, confiabilidade e resiliência para suportar com louvor o constante e acelerado crescimento de demandas de negócio.
Neste artigo, vamos estudar juntos alguns padrões de design de aplicações que podem nos ajudar a entender melhor como alcançar as habilidades mencionadas acima. Nós vamos passar por cada padrão, para entender como cada um se comporta em um ambiente de nuvem nativo e quando e onde é melhor utiliza-los.
Alguns desses padrões de desenvolvimento não são novos, mas continuam muito uteis e atuais para o modelo de escala mundial da nuvem na internet.
Segue a lista de padrões discutidas nesse artigo:
1. Circuit Breaker
2. Command and Query Responsibility Segregation (CQRS)
3. Event Sourcing
4. Sidecar
5. Backend-for-Frontend
6. Estrangulamento de Software
Circuit Breaker
Sistemas distribuídos deveriam ser desenvolvidos levando em consideração as falhas que podem apresentar. Nos dias de hoje desenvolver micro serviços é algo muito comum no desenvolvimento de software, ao contrário dos antigos monolitos de código, os micro serviços nos trouxe uma série de benefícios como redução de custos, escalabilidade, facilidade de manutenção, baixo nível de acoplamento entre outros, mas, como nem tudo são flores, na maioria dos casos uma grande dependência de outros serviços.
Estes serviços remotos do qual sua aplicação depende, podem simplesmente parar de responder por várias razões como rede, alta carga na aplicação, indisponibilidade de infra, etc. E na maioria dos casos, não temos muito o que fazer, a não ser esperar por essa dependência voltar, principalmente quando é uma aplicação de terceiros, então implementar uma estratégia de Retry pode ser o suficiente para resolver esses problemas de instabilidade na comunicação como uma solução de contorno.
Mas algumas vezes esses problemas podem ser maiores, conforme a degradação de um serviço vai ficando mais crítica, fazendo essa dependência ficar muito lenta e até cair, transformando sua aplicação refém em uma situação que uma programação mais defensiva se torne necessária. É nesse ponto que utilizar o Circuit Breaker é realmente útil.
A imagem acima, mostra um caso de implementação de Circuit Breaker, onde quando o Service 1 entende que tem uma falha de timeout no Service 2, ao invés de continuar o consumo, aumentando assim a degradação do Service 2, o Service 1 desvia as chamadas que iriam para o Service 2 e retorna uma resposta do fallback informando que o circuito foi aberto.
Assim que o circuito é aberto e o Service 2 é protegido de nossas chamadas, precisamos assim como em um circuito elétrico, resetar o circuito quando Service 2 voltar a responder, por isso implementamos a estratégia de Half-Open, que verifica de tempos em tempos se o Service 2 voltou, assim que volta o circuito é fechado e a aplicação volta ao normal.
Existem muitas bibliotecas open-source que podem ser usadas para implementar esse padrão como a Netflix Hystrix.
É muito importante que tenhamos logs e alertas suficientes implementados quando o circuito estiver aberto, de modo que seja possível acompanhar de forma detalhada as requests recebidas durante esse período deixando assim toda a equipe ciente do que esta acontecendo.
Quando utilizar esse padrão?
- Quando um serviço depende de outro serviço remoto é provável que existam falhas.
- Quando um serviço tem uma grande dependia de outro serviço
Quando não utilizar esse padrão?
- Quando estiver consumindo dependências locais, um circuit breaker pode criar uma sobrecarga.
Command and Query Responsibility Segregation (CQRS)
CQRS é um padrão muito útil para aplicações modernas que envolvem o uso de armazenamento de dados e alta disponibilidade. É baseado no princípio de segregação de leitura(query) e escrita(command) de dados, esta segregação pode trazer grande ganho, mas esteja ciente de que, para a maioria dos sistemas, o CQRS adiciona uma complexidade arriscada.
Dizer que você esta construindo uma aplicação que é necessário armazenar dados em um Database como um MySQL/PostgresSQL/MongoDB, etc. Como todos sabemos, quando escrevemos dados dentro de um database, é uma operação que é feita em algumas etapas como validação, modelagem e persistência, e consequentemente uma simples instrução de insert/update leva mais tempo que uma simples operação de leitura.
Neste caso, o CQRS por ser útil, pois o mesmo sugere que usemos diferentes modelos de dados para leitura e escrita de operações. Algumas variações também sugerem utilizar diferentes databases para esse tipo de modelo.
Para separar esses modelos é normal ter modelos diferentes, rodando em processos e as vezes em maquinas separadas, o CQRS se ajusta naturalmente em outros padrões de arquitetura como modelos orientados a eventos DDD entre outros.
Muitos PaaS oferecem bancos de dados com a habilidade de criar réplicas de leitura e escrita, onde o próprio serviço de replicação trata de manter os dados sincronizados de maneira rápida e fácil.
Essa característica é encontrada em outros bancos de dados como Oracle, MongoDB etc.
É comum encontrar arquiteturas que salvam os dados de escrita em um MySQL e a réplica de leitura ser um NoSQL como MongoDB ou Redis.
Quando utilizar esse padrão?
- Quando sua aplicação espera um grande numero de leitura e escrita.
- Quando você precisa melhorar a performance de leitura e escrita de maneira separada
- Quando não tem problemas em ter os dados da leitura em tempo-real por causa da sincronização das bases.
Quando não utilizar?
- Quando sua aplicação é muito simples ou não espera um alto volume de trafego.
Event Sourcing
Event Sourcing é um interessante padrão onde a sequencia de eventos do domínio é armazenada como uma jornada ou linha do tempo como preferir, e uma agregação dessa jornada da o estado atual da aplicação, ou seja, o Event Sourcing assegura que todas as mudanças feitas no estado de uma aplicação são armazenadas como uma sequência de eventos, sendo possível assim reconstruir o estados passados e ajustar automaticamente o estado atual com mudanças retroativas.
Este padrão é tipicamente usado para sistemas que não podem ter locks de dados e que precisam manter auditoria do histórico de eventos — por instancia, aplicações como hotéis/conferencias/reservas de assentos, etc.
Considerando o caso de uso de um hotel em que o mesmo utiliza um sistema de reservas em que os usuários esperam realizar ou cancelar uma reserva. Aqui você precisa armazenar as reservas e cancelamentos como eventos em uma linha do tempo. Antes de cada reserva, uma visualização agregada dos eventos mostram os quartos disponíveis observando o histórico de eventos.
Quando utilizar esse padrão?
- Quando um CRUD padrão não é bom o suficiente para suportar a demanda de requests.
- Normalmente utilizado em sistemas de reservas, como ônibus, trens, cinemas — ou e-commerces que utilizam eventos em operações de carrinho, pagamentos etc.
- Quando há a necessidade de uma grande auditoria em eventos para realizar repetição de estado de uma aplicação com eventos em eventos passados.
Quando não utilizar esse padrão?
- Quando um CRUD aguentar a demanda de sua aplicação.
Sidecar
O padrão Sidecar tornou-se popular com a chegada dos micro serviços. Neste padrão, você faz o deploy de um componente de uma aplicação dentro de um processo separado ou um container, nos ajudando a alcançar uma alta abstração e encapsulamento de uma aplicação.
Com o Sidecar é possível ter compor aplicações com diferentes componentes, independente das tecnologias usadas em seu desenvolvimento.
Com o Sidecar, conseguimos abstrair os tipos de comunicação, como monitoramento, logs, configurações ou serviços de redes. Estas tarefas podem ser desenvolvidas em um container e acopladas a um aplicação permitindo você desenvolver funcionalidades isoladas da aplicação original.
Quando usar esse padrão?
- Quando você está lidando com múltiplos e heterogêneos micro serviços em seu escopo de produto.
- Quando você esta lidando com aplicações legado que tipicamente tem dificuldades em se comunicar com nos aplicações e desafios de segurança.
Quando não utilizar esse padrão?
- Quando você esta lidando com limitados números de serviços e que eles apenas se comuniquem entre si.
- Pequenas aplicações onde o deploy sidecar não pode ser economicamente viável, ou seja seja, aplicações pequenas de fácil reescrita.
Backend-for-Frontend (BFF)
Em um típico ciclo de desenvolvimento de software, desenvolvedores especializados em backend são responsáveis por criar serviços e interagir com armazenamento de dados, e os desenvolvedores front-end se preocupam com a usabilidade das telas e interfaces. As aplicações de hoje em dia precisam ser mantidas em sua versão tanto mobile quanto desktop.
Mesmo que a diferença do mobile para o desktop em termos de hardware estão muito perto, a comunicação continua ser uma desafio para o mobile.
Neste cenário, o padrão BFF torna-se muito útil. Aqui, estamos construindo/ customizando um serviço backend para um específico front-end.
Para otimizar a performance de clientes mobile, você pode querer construir um back-end separado, que responde de uma maneira mais leve e paginada os dados necessários.
Você pode também usar esse padrão para agregar vários serviços com o intuito de reduzir a complexidade da comunicação.
Note que se você utiliza um API gateway como Apigee ou outro, o padrão BFF pode ser facilmente implementado no mesmo, e você não precisa manter serviços separados.
Quando utilizar esse padrão?
- Quando você querer entregar um produto/serviço para diferentes clientes, igual desktop e mobile.
- Quando você quer otimizar a resposta para um particular tipo de cliente.
- Quando você quer reduzir a complexidade entre comunicações entre vários serviços.
Quando não utilizar esse padrão?
- Quando os usuários da aplicação esperam uma interface simples.
- Quando o mobile e desktop devem demonstrar uma informação similar com uma funcionalidade similar.
Estrangulamento de Software
Se você trabalha em uma organização que está em uma jornada de modernização de aplicações, então esse padrão é obrigatório em sua estratégia. O Estrangulamento de software é o conceito que prega a transformação gradual de sistema legados, defende a criação de um facade no topo de seu legado e uma nova aplicação, fornecendo um visualização abstrata para os seus consumidores.
Este padrão, desacopla os consumidores das atividades de migrações.
Note que uma organização de IT, se você esta migrando de um ERP para outro, este tipo de padrão é extremamente útil. Se você esta usando um API Gateway, é muito mais fácil para implementar esse padrão em seu proxy.
Você deve decidir se quer manter a facade ao terminar sua migração ou remove-la.
Quando utilizar esse padrão?
- Quando você esta migrando ou modernizando uma aplicação legado complexa, altamente dependente como ERPs.
Quando não utilizar esse padrão?
- Quando a migração é simples e a substituição direta é a melhor opção.