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 :)