DEV Community

System Design - Uma introdução

Neste post, vou abordar os fundamentos do System Design, o que seria esse campo de estudos e desenvolvimento e o que isso tem a ver com desenvolvimento de software.

Inicialmente, minha intenção é lançar as linhas gerais acerca desse tema e iniciar uma série sobre System Design em Português do Brasil, tendo em vistas que ainda hoje há uma lacuna na nossa literatura sobre esse tema tão fundamental para a engenharia de software.

Estou muito influenciado pela série do Matheus Fidelis sobre System Design e espero contribuir para melhorar o acesso à esses conhecimentos.

Por quê System Design é importante?

Para aplicações que operam em larga escala, ou seja, que atendem uma demanda de milhões de usuários simultâneos, System Design enquanto uma disciplina da engenharia de software nos fornece uma perspectiva de alto nível de abstração acerca dos componentes fundamentais de uma aplicação, sua interação e interdepedência. Em outras palavras, o System Design, nos ajuda a definir a arquitetura de um sistema, desmembrando-o em componentes fundamentai.

O que é System Design?

System Design é o processo de definir os elementos de um sistema, bem como suas interações e relacionamentos, a fim de satisfazer um conjunto de requisitos especificos, sendo eles requisitos funcionais, não-funcionais e extendidos.

Fornecendo uma perspectiva de abstração de um dado sistema, dividindo-o em componentes menores e projetando cada componente, com uma responsabilidade especifica, para trabalhar em conjunto de forma eficaz para alcançar o objetivo geral do sistema.

Esse processo geralmente inclui analisar o sistema atual (se houver) e determinar quaisquer deficiências, criar um plano detalhado para o novo sistema e testar o design para garantir que atenda aos requisitos. É um processo iterativo que pode envolver várias rodadas de design, teste e refinamento.

Na engenharia de software, System Design é uma fase no processo de desenvolvimento de software que se concentra no design de alto nível de abstração de um sistema de software, incluindo a arquitetura e os componentes.

Também é um dos aspectos importantes do processo de entrevista para engenheiros de software, em especial em big techs. A maioria das empresas tem uma rodada de entrevista dedicada ao System Design, onde pedem aos candidatos para projetar um sistema para uma determinada situação-problema.

Espera-se que os candidatos elaborem um design detalhado do sistema, incluindo a arquitetura, os componentes e suas interações. Eles também são esperados para discutir os trade-offs envolvidos em seu design e as alternativas que consideraram para otimizar o funcionamento de cada componente.

Precisamos conhecer sobre escalabilidade (horizontal e vertical), cache, balanceamento de carga, particionamento de dados, diversos tipos de bancos de dados, protocolos de rede, fragmentação de banco de dados e muito mais. Da mesma forma, precisamos considerar vários compromissos como: latência vs. throughput, desempenho vs. escalabilidade, consistência vs. disponibilidade, etc. No geral, o system design é um tópico de discussão aberto. Por isso, a maioria das principais empresas de tecnologia prefere ter uma ou duas rodadas de entrevistas de system design.

Sendo assim, algo fundamental para iniciar os estudos nesse tema é nos familiarizarmos com os conceitos essenciais utilizados em System Design.

Disponibilidade / Availability

Disponibilidade significa: O sistema deve permanecer sempre operacional e retornar a resposta de uma solicitação do cliente. Em outras palavras, sempre que qualquer usuário quiser usar o serviço, o sistema deve estar disponível e satisfazer a solicitação do usuário.

A disponibilidade pode ser quantificada medindo a porcentagem de tempo em que o sistema permanece operacional dentro de uma janela de tempo específica. Normalmente, definimos a disponibilidade de um sistema em termos de números de 9s (conforme mostrado na tabela abaixo).

Tabela de Indisponibildidade

Quando se está projetando um sistema, como um _marketplace _ou um sistema de meios de pagamento, por exemplo, é fundamental ter em mente os possíveis cenários de indisponibilidade do sistema. Caso uma aplicação desse tipo fique indisponível por vários dias, isso pode significar uma perda em rendimentos na casa dos milhões de reais.

Para projetar um sistema altamente disponível, precisamos considerar vários fatores, como redundância, balanceamento de carga (load balancing), mecanismos de _failover _e estratégias de recuperação de danos. Nosso objetivo deve ser minimizar o tempo de inatividade e garantir que o sistema esteja sempre acessível e funcionando conforme o previsto.

Taxa de transferência / Throughput

A taxa de transferência é uma das principais métricas para medir o desempenho de um sistema. É a quantidade de dados ou o número de solicitações que podem ser processadas em um determinado período de tempo. Em outras palavras, usamos o rendimento para medir a capacidade do sistema para lidar com múltiplas solicitações ou fluxos de dados simultaneamente.

A alta taxa de transferência é importante quando lidamos com um grande volume de dados ou um grande número de solicitações simultâneas. Uma excelente maneira de obter alto rendimento é: Dividir as solicitações e distribuí-las para várias máquinas.

Latência / Latency

A latência é outra métrica importante relacionada ao desempenho de um sistema. É uma medida do tempo de duração para produzir o resultado. Em outras palavras, é o tempo que uma solicitação leva para receber uma resposta de um servidor. Isso inclui o tempo que uma solicitação leva para percorrer a rede, processando a solicitação, consultas ao banco de dados e outras operações antes de gerar a resposta. Baixa latência significa: tempos de resposta rápidos, maior desempenho do sistema e uma melhor experiência de usuário! Para o caso de um jogo online, por exmeplo, é mandatório que se busque atingir a mínima latência possível.

Protocolos de Rede / Network Protocols

Quase todos os sistemas dependem de redes. Atua como uma plataforma de comunicação entre usuários e servidores ou diferentes servidores. Por outro lado, os protocolos são as regras que regem a forma como os servidores ou máquinas se comunicam na rede. Alguns dos protocolos de rede comuns são HTTP, TCP/IP, etc.

Protocolos de Rede

Balanceamento de Carga / Load Balancing

Load balancers são máquinas que distribuem a carga entre vários servidores. Para escalar o sistema, podemos adicionar mais e mais servidores e portanto, deve haver uma maneira de direcionar as requisições para esses servidores de maneira que não haja uma sobrecarga em um único servidor ou em alguns servidores do sistema. Em outras palavras, os load balancers são usados para evitar um ponto único de falha ou a indisponibiliddade do sistema pela sobrecarga em um ou alguns servidores. Load balancers distribuem o tráfego, previnem falhas no serviço e contribuem para a confiabilidade do sistema, atuando como gerenciadores de tráfego e a alta disponibilidade do sistema.

Proxies

Um proxy é um mediador, presente entre o cliente e o servidor. Quando um cliente envia uma requisição, ela passa pelo proxy e então, este redireciona a requisição para o alcança o servidor. Proxies são de dois tipos: Proxy Direto (Forward Proxy) e Proxy Reverso (Reverse Proxy). O proxy direto atua como uma máscara para os clientes e oculta a identidade do cliente para o servidor. Da mesma forma, o proxy reverso atua como uma máscara para os servidores e oculta a identidade do servidor da resposta.

Bancos de Dados

Qualquer sistema está associado à dados que precisam ser armazenados para posterior recuperação e manipulação. Uma maneira de classificar bancos de dados é com base em sua estrutura, que pode ser tanto relacional quanto não-relacional. Bancos de dados relacionais, como MySQL e PostgreSQL, impõem relações rigorosas entre os dados e têm uma organização altamente estruturada. Bancos de dados não-relacionais, como Cassandra e Redis, possuem estruturas flexíveis e podem armazenar tanto dados estruturados quanto não-estruturados. Eles são frequentemente usados em sistemas altamente distribuídos e que requerem alta velocidade.

Para aumentar a escalabilidade, uma alternativa pode ser a partição de banco de dados, que é a maneira de dividir o banco de dados em partes menores para aumentar o desempenho do sistema. É outro conceito crítico usado para melhorar a latência e a taxa de transferência, para que mais e mais requisições possam ser processadas. Aqui está a lista de alguns bancos de dados populares com base na Pesquisa de Stack Overflow Developer Survey 2023:

Stack Overflow Developer Survey 2023

ACID vs BASE

Bancos de dados relacionais e não relacionais garantem tipos diferentes de organização e manipulação de dados. Bancos de dados relacionais, em geral, estão associados à propriedades ACID, enquanto bancos de dados não relacionais estão associados à conformidade BASE. Os bancos de dados ACID priorizam a consistência em detrimento da disponibilidade: toda a transação falhará se ocorrer um erro em qualquer etapa dela. Em contraste, os bancos de dados BASE priorizam a disponibilidade em vez da consistência. Em vez de falhar na transação, os usuários podem acessar dados inconsistentes temporariamente. A consistência de dados é alcançada, mas não imediatamente.

ACID significa atomicidade, consistência, isolamento e durabilidade.
  • Atomicidade: A atomicidade garante que todas as etapas em uma única transação de banco de dados sejam totalmente concluídas ou revertidas ao estado original. Por exemplo, em um sistema de reservas, ambas as tarefas — reservar assentos e atualizar os detalhes do cliente — devem ser concluídas em uma única transação. Você não pode ter assentos reservados para um perfil de cliente incompleto. Nenhuma alteração será feita nos dados se alguma parte da transação falhar.

  • Consistência: A consistência garante que os dados atendam às restrições de integridade e às regras de negócios predefinidas. Mesmo que vários usuários realizem operações semelhantes simultaneamente, os dados permanecem consistentes para todos. Por exemplo, a consistência garante que, ao transferir fundos de uma conta para outra, o saldo total antes e depois da transação permaneça o mesmo. Se a Conta A tiver USD 200 e a Conta B tiver USD 400, o saldo total será de USD 600. Depois que A transfere USD 100 para B, A tem USD 100 e B tem USD 500. O saldo total ainda é de USD 600.

  • Isolamento: O isolamento garante que uma nova transação, acessando um registro específico, espere até que a transação anterior termine antes de iniciar a operação. Ele garante que as transações simultâneas não interfiram entre si, mantendo a ilusão de que estão sendo executadas em série. Por outro exemplo, é um sistema de gerenciamento de inventário multiusuário. Se um usuário atualizar a quantidade de um produto, outro usuário acessando as mesmas informações do produto terá uma visão consistente e isolada dos dados, não afetada pela atualização em andamento até que ela seja confirmada.

  • Durabilidade: A durabilidade garante que o banco de dados mantenha todos os registros confirmados, mesmo se o sistema apresentar falhas. Ele garante que, quando as transações ACID são confirmadas, todas as alterações são permanentes e não são afetadas por falhas subsequentes do sistema. Por exemplo, em uma aplicação de mensagens, quando um usuário envia uma mensagem e recebe uma confirmação de entrega bem-sucedida, a propriedade de durabilidade garante que a mensagem nunca seja perdida. Isso permanece verdadeiro mesmo se a aplicação ou o servidor encontrar uma falha.

BASE significa basicamente disponível, estado flexível e, eventualmente, consistente. A sigla destaca que BASE é o oposto de ACID, como seus equivalentes químicos.
  • Basicamente disponível: Basicamente disponível é a acessibilidade simultânea do banco de dados pelos usuários em todos os momentos. Um usuário não precisa esperar que outros concluam a transação antes de atualizar o registro. Por exemplo, durante um aumento repentino no tráfego em uma plataforma de comércio eletrônico, o sistema pode priorizar a veiculação de listagens de produtos e a aceitação de pedidos. Mesmo que haja um pequeno atraso na atualização das quantidades de estoque, os usuários continuam fazendo o check-out dos itens.

  • Estado flexível: O estado flexível se refere à noção de que os dados podem ter estados transitórios ou temporários que podem mudar com o tempo, mesmo sem acionadores ou entradas externas. Ele descreve o estado de transição do registro quando várias aplicações o atualizam simultaneamente. O valor do registro é finalmente finalizado somente após a conclusão de todas as transações. Por exemplo, se os usuários editarem uma publicação nas redes sociais, a alteração pode não ser visível para outros usuários imediatamente. No entanto, mais tarde, a postagem é atualizada sozinha (refletindo a alteração mais antiga), mesmo que nenhum usuário a tenha acionado.

  • Leitura eventualmente consistente: Eventualmente consistente significa que o registro alcançará consistência quando todas as atualizações simultâneas forem concluídas. Nesse ponto, as aplicações que consultam o registro verão o mesmo valor. Por exemplo, considere um sistema distribuído de edição de documentos em que vários usuários possam editar um documento simultaneamente. Se o Usuário A e o Usuário B editarem a mesma seção do documento simultaneamente, suas cópias locais poderão diferir temporariamente até que as alterações sejam propagadas e sincronizadas. No entanto, com o tempo, o sistema garante uma consistência eventual ao propagar e mesclar as alterações feitas por diferentes usuários.

SQL vs NoSQL

Ao projetar qualquer sistema, é necessário ter clareza sobre o tipo de armazenamento de acordo com os requisitos do sistema. Se o sistema for distribuído e escalabilidade for essencial, então os bancos de dados NoSQL pode ser a melhor escolha. Bancos de dados NoSQL também são preferidos quando a quantidade de dados é enorme.

Simultaneamente, bancos de dados SQL são favoráveis quando a estrutura de dados é mais importante. Geralmente, é preferido quando as consultas são complexas e os bancos de dados exigem menos atualizações. No entanto, sempre há um compromisso ao escolher entre bancos de dados NoSQL e SQL. Às vezes, de acordo com os requisitos do negócio, uma arquitetura Poliglota compreendendo tanto bancos de dados SQL quanto NoSQL é usada para garantir o desempenho da aplicação.

Escalabilidade em sistemas distribuídos

À medida que os serviços crescem e o número de solicitações aumenta, o sistema pode ficar lento e o desempenho pode ser afetado. O escalar é a melhor maneira de lidar com esse problema, o que envolve aumentar a capacidade do sistema para lidar com mais requisições. Existem duas maneiras principais de escalar: escalonamento horizontal e escalonamento vertical.

O escalonamento horizontal envolve adicionar mais servidores para distribuir as solicitações. Isso permite que o sistema lide com mais tráfego distribuindo-o entre vários servidores. Por outro lado, o escalonamento vertical aumenta a capacidade de uma única máquina ao atualizá-la com mais recursos, como CPU, memória e armazenamento. Isso permite que o sistema lide com mais tráfego aumentando a capacidade de uma única máquina.

Referências

Top comments (0)