Photo by Kelly Sikkema on Unsplash
Aprendendo Arquitetura de Software
Uma jornada por fundamentos e conceitos em resumo
A arquitetura de software é uma disciplina de engenharia de software, mas está diretamente ligado ao desenvolvimento de código. Uma vez definida a arquitetura, pode afetar diretamente a distribuição dos times, estrutura dos componentes e serviços.
Usando a arquitetura de software, podemos relacionar os objetivos de negócio e suas restrições com os componentes a serem criados, assim como, suas responsabilidades.
Visão de Arquitetura de Software
Noções que uma pessoa com estudo em arquitetura desenvolve naturalmente
Poder navegar entre visão macro e micro em um ou mais produtos
Entender quais são as opções para seguir um caminho, e quais são seus trade-offs
Pensar melhor em custo, sustentabilidade e longo prazo
Ser menos influenciável a tendências do mercado, que podem não gerar nenhum valor efetivo ao contexto onde está inserido.
Desenvolvimento de padrões e boas práticas sendo constantemente aprimorado.
Ter clareza do software como produto para a empresa.
Arquitetura vs Design
Arquitetura foca no escopo global ou alto nível para garantir que os atributos e os objetivos sejam atendidos.
Design foca no escopo local ou baixo nível para garantir um código compreensivo, reutilizável e limpo.
Toda decisão de arquitetura também é de design mas não o contrário.
Pilares da arquitetura de software
Estruturação
- Fácil evolução, componentização e reusabilidade.
Componentização
Relacionamento entre sistemas
Governança
Regras
Padrões
Documentações
Sustentabilidade
Fazer código é caro, construir uma boa arquitetura e um bom design vai economizar muito tempo e dinheiro, para o caso de uma solução que persiste sem ter muitos problemas ou precisar se reescrita, como é bem comum no desenvolvimento de software.
Requisitos de arquitetura de software
Pontos a serem levantados para entregar uma arquitetura a um software
Performance
Armazenamento de dados
Escalabilidade
Segurança
Legislação
Auditoria
Marketing
Operações de arquitetura
Quando pensamos em operar sistemas precisamos pensar e avaliar os tradeoffs de cada ponto listado abaixo. Obviamente gostariamos de ter todos os pontos ao máximo sempre, porém sabemos que isso não é possível, seja por custo ou tempo é importante avaliar o contexto da aplicação e tomar a melhor solução
Disponibilidade
Observabilidade
SLA
Recuperação de desastres
Processos
Postmortem
Diversificar em várias regiões?
Diversificar em vários hosts de cloud?
Performance
Qual carga a aplicação precisa suportar
Como otimizar a aplicação
Como gerenciar recursos
Confiabilidade
Segurança
Evitar bots
Evitar bruteforce
Escalabilidade
Vertical com aumento de recurso, custa mais caro.
Quando adicionamos mais dividido por várias máquinas.
Auto escalável, aumenta recursos
Twelve factors
Stateful vs Stateless
Estruturas de arquitetura
Diferente das operações as estruturas são pontos de atenção no software para que ele seja mais resiliente e flexivel e para isso precisa ser,
Configurável
Extensibilidade
Interfaces são necessárias para não ser refém de terceiros
Ser fácil trocar qualquer ferramenta usada no projeto.
Camadas anti-corrupção
Fácil instalação
- Padronização do ambiente, ou melhor, docker.
Componentização
Reuso
Padronizar features compartilhaveis entre times.
Internacionalização
Fácil manutenção
Patterns
Testes
Documentação
Cross Cutting de arquiteturas
Acessibilidade
Retenção e recuperação de dados
Autenticação e autorização
Leis do país vigente
Privacidade
Segurança
Performance
É o desempenho que um software possui para completar uma determinada tarefa, e as medidas para avaliar isso são
Latência
- Tempo de resposta ou response time
Throughput
- A carga de requisição que o software aguenta
Performance pode ser visto facilmente como escala mas um sistema performático pode não ser escalável, assim como um sistema escalável pode não ser performático.
Como melhorar a performance?
Diminuindo a latência
Processamento da aplicação
Rede
Chamadas externas
Aumentando o throughput
- Quantidade de requisições
Quais podem ser os gargalos e como podem ser melhorados?
Processamento ineficiente
Escala da capacidade computacional
- CPU, Disco, Memória ou Rede.
Algoritmos e queries
Recursos computacionais limitados
- Caching
Implementação com código bloqueante
Acesso serial de recursos
- Concorrência e Paralelismo
Banco de dados
Escala vertical vs horizontal
Escala vertical é quando eu resolvo a capacidade de lidar com mais requisições aumentando meu poder computacional.
Já a escala horizontal é quando eu resolvo a capacidade de lidar com mais requisições aumentando o número de máquinas rodando em paralelo, tendo suas chamadas diluídas entre o número de máquinas através de um load balancer.
Concorrência vs Paralelismo
“Concorrência é sobre lidar com muitas coisas ao mesmo tempo. Paralelismo é fazer muitas coisas ao mesmo tempo”.
- Rob Pike
Um exemplo sobre o assunto é imaginarmos o cenário do start de uma aplicação, esse start depende de 5 requisições para começar e cada uma delas custa 10ms
, rodando esse código de maneira serial seu tempo de execução seria ao total 50ms
, entretanto caso esse código seja concorrente ou paralelo seu tempo total processando em 5 threads será 10ms
.
Caching
Um ponto de memória mais leve e fácil de ser encontrado, em pontos muito exigidos, diminuindo a carga sobre serviços e bancos de dados.
Para cada cenário temos diferentes tipos de implementações de cache, podemos ter cache exclusivo ou compartilhado. O cache será exclusivo quando a demanda for por baixa latência, não vou ter muitos nós de cache na aplicação ou não vou ter que lidar com sessão. Já o cache compartilhado carrega sob maior latência, gerenciando um estado global, é único entre todos os nós e as sessões são compartilhadas.
Os usos de cache
Edge Computing
Recursos totalmente processados antes de chegar no servidor principal
Muito eficiente para aplicações web
Serviços como por exemplo cloudflare
Dados estáticos
Assets
CSS
Aplicação web por inteiro
- Homepage respondendo 100% em cache
Funções internas
Auxilio de processamento de tempo complexo
Acesso ao banco de dados
Objetos
- Auxilio de processamento para relacionamentos entre entidades
Serviços
MySQL
Redis
Memcache
Escalabilidade
Escalabilidade é a capacidade de sistemas suportarem o aumento(ou a reduzindo) o custo em menor ou igual proporção.
Enquanto performance tem o foco em reduzir a latência e aumentar o throughput, escalabilidade visa termos a possibilidade de aumentar ou diminuir o throughput adicionando ou removendo a capacidade computacional.
Relembrando as escalas, quando mais vertical escalamos mais recurso computacional usamos enquanto na horizontal usamos mais máquinas separadamente.
Descentralização
Uma vez que a escala vertical pode não atender diversos requisitos de custo ou resiliência, torna-se necessário utilizar da escala horizontal entretanto para isso precisamos também nos atentarmos a um segundo detalhe, a descentralização.
Descentralizar o que? Pense que quando temos diversas máquinas em funcionamento repartimos os dados e as aplicações de tal maneira que essas máquinas sejam descartáveis, você descentraliza tudo.
O disco na descentralização é efêmetro, deletar ou criar máquinas não podem ser um problema, para que tudo que esteja no disco possa deliberadamente ser perdido, assim como também gravações e uploads direto no disco precisam respeita a natureza efêmera do disco.
Seguindo o princípio de disco, quando precisarmos de assets nas aplicações faz sentido estarem em um servidor específico separado da aplicação.
O cache e as sessões para dados descentralizados se torna global.
Escala de banco de dados
Aumento de recursos verticais
Distribuir responsabilidades
Escrita
Leitura
Shards de forma horizontal
Serverless
Otimização de queries e índices
APM
Trabalhar com índices de forma consistente
Explain na query
CQRS! (Command Query Responsability Segregation)
Proxy reverso
Um servidor que se põe a frente dos servidores web e encaminha as solicitações do cliente para esses servidores web.
Nginx
HAProxy
Traefik
Resiliência
É um conjunto de estratégias adotadas intencionalmente para a adaptação de um sistema quando uma falha ocorre, quando temos estratégias de resiliência nos possibilita minimizar os riscos de perda de dados e transações importantes.
Quando um sistema tem a sua arquitetura distribuída precisa adotar mecanismos de autopreservação para garantir ao máximo sua operação com qualidade.
Muitas vezes um sistema lento é mais prejudicial do que um sistema fora do ar pois enquanto estiver de pé sem resposta causará o efeito dominó.
Health check
Para saber como anda a saúde das aplicações se faz necessário consultar os “sinais de vida” de cada aplicação. Sabendo da saúde da aplicação podemos, por exemplo, parar de enviar tráfego a ela para que se recupere.
Um health check precisa consultar banco de dados ou acessar algum dado importante.
Geralmente está na rota /health
das aplicações.
Rate Limiting
Tem como função proteger o sistema baseado no que ele foi projetado para suportar.
Por exemplo, para cada tipo de cliente você pode determinar um número de requests por hora, balanceando assim os recursos da sua infraestrutura de acordo com a regra de negócio.
Idealmente é necessário também dar clareza ao usuário de alguns pontos para entender o consumo e o limite
Rate limit total
Rate limit consumido
Horario que o rate limit reseta
Circuit Breaker
Tem como função proteger o sistema fazendo com que as requisições feitas para ele sejam negadas.
Circuito fechado: quando as requisições chegam normalmente
Circuito aberto: quando as requisições não chegam ao sistema
Circuito meio aberto: quando o sistema permite parcialmente requisições para verificar se o sistema aguenta a carga sob as condições do momento.
API Gateway
É o recurso que centraliza todas as requisições que acontecem na aplicação. Pode ser usado também para não permitir que requisições indesejadas cheguem até as aplicações, como por exemplo, usuário não autenticado.
Vai implementar todas as políticas de rate limit, health check e etc.
Service Mesh
De uma forma geral serve para controlar o trafego de rede, recebendo as comunicações entre os serviços e repassando via proxy. Evita que a implementação de proteção dos serviços sejam feitas e replicadas entre todos os serviços. Pode implementar os recursos abaixo e entre outros,
mTLS
Circuit breaker
Retry
Timeout
Fault injection
Comunicação assíncrona
É a capacidade de aguardar uma resolução
Evita a perda de dados
Não necessita do servidor estar de pé na hora do envio da requisição
Servidor pode processar a transação no tempo que ele puder
Retry
Como a palavra diz em inglês, significa retentativa, para cada mensagem que não for processada por algum motivo qualquer, o gerenciador da mensagem assíncrona se responsabiliza por retentar esta mensagem.
Linear backoff
- Significa que o prazo de retentativa é linear, por exemplo, a cada 2 segundos.
Exponential backoff
- Significa que a espera vai escalar exponencialmente conforme o serviço não responde.
Exponential backoff Jitter
- Significa que existe um ruído manipulando o valor do retry exponencial, para evitar que existam retentativas simultâneas.
Garantia de Entrega
Para garantirmos a entrega das mensagens, é muito comum que sejam usadas ferramentas de message broker, entretanto existem várias maneiras de entregar e garantir resiliência nas mensagens, por exemplo, no Kafka temos
Ack 0 que é quando dispara-se a mensagem e não espera a resposta de garantia
Ack 1 onde a mensagem é disparada e se espera por um broker leader confirmar que a mensagem foi recebida
Ack - 1 significa que além de garantir e confirmar que a mensagem foi recebida no leader também vai aguardar a réplica para os outros brokers.
Por esse tópico eu fechei mas ainda prometo voltar para me aprofundar mais :)