Padrões arquiteturais: arquitetura de software descomplicada

Padrões arquiteturais: arquitetura de software descomplicada
Camila Pessôa
Camila Pessôa

Compartilhe

Introdução

Talvez você já tenha escutado a frase ou mesmo pensado: “Tenho uma ideia para um aplicativo”. Tudo soa como algo muito fácil de realizar à primeira vista, porém, durante o estudo e a prática em desenvolvimento de software, percebemos que alguns processos são indispensáveis antes de escrever a primeira linha de código.

Transformar uma ideia do zero em software não é uma tarefa tão simples assim, porém, muitas pessoas desenvolvedoras já passaram por essas questões antes e pensaram em formas de otimizar esses processos ou facilitar a representação de soluções em softwares.

Pensando nisso, este artigo vai apresentar alguns dos padrões de arquitetura de software já consolidados no mercado para que você tenha um direcionamento nos seus estudos. Vamos lá?

Banner da promoção da black friday, com os dizeres: A Black Friday Alura está chegando. Faça parte da Lista VIP, receba o maior desconto do ano em primeira mão e garanta bônus exclusivos. Quero ser VIP

O que é arquitetura de software

Para entendermos os padrões arquiteturais, primeiro é necessário compreendermos o que é arquitetura de software.

As apropriações de nomenclaturas do mundo real são comuns no universo de tecnologia, e não poderia ser diferente com o termo arquitetura. No mundo físico, profissionais de arquitetura constroem projetos e desenhos de casas, prédios, entre outras estruturas, baseando-se em um contexto que envolve diversas variáveis e situações.

A definição de arquitetura de software, de acordo com a ISO/IEC/IEEE 42010:2022, é a seguinte:

“Arquitetura de software é a estrutura fundamental ou o esqueleto de um sistema de software, que define seus componentes, suas relações e seus princípios de projeto e evolução.”

Vamos entender melhor? Arquitetura de software pode ser entendida como um conjunto de normas, princípios e técnicas para construção de software. De certa forma, assemelha-se à ideia de arquitetura que conhecemos e que delimita os padrões de edificações para casas em ambientes pequenos, frios, quentes etc., mas será que é isso mesmo?

Uma imagem futurista e tecnológica mostrando as mãos de uma pessoa desenhando em um holograma as camadas que representam um software e se assemelha a um edifício complexo e detalhado, com elementos gráficos holográficos flutuando ao redor, indicando um processo de design arquitetônico avançado ou planejamento urbano.

Fonte: imagem gerada pela IA generativa Bing

Nesse sentido, essa analogia funciona quando pensamos que a arquitetura de software também possui padrões direcionados para cada modelo de negócio. No entanto, como Martin Fowler e Robert C. Martin já afirmaram, conceituar arquitetura de software é uma tarefa complexa, pois sua dinâmica é outra. Os softwares não envolvem elementos físicos, como tijolos ou cimento, porém, sua “materialidade” se estabelece a partir de componentes, módulos etc., e sua estrutura é formada com código dentro de código.

Tudo isso pode gerar algumas confusões durante nosso aprendizado e, neste momento, você pode ter chegado à seguinte conclusão: “Então SOLID é arquitetura de software?!”. Na verdade não é bem assim.

O acrônimo SOLID é a reunião de cinco princípios aplicados à programação orientada a objetos, ou seja, isoladamente o SOLID reúne um conjunto de boas práticas para a escrita de código, para o design de software, mas não carrega uma arquitetura para um projeto. Isso não quer dizer que você deverá separar os princípios de design de software da arquitetura de software, pois você pode, e deve, aplicar os princípios de design na arquitetura escolhida para o projeto, por exemplo, você pode aplicar SOLID, KISS, DRY etc. no padrão de arquitetura orientada a serviços (SOA).

Além de princípios, os conceitos de abordagens de design de software também podem se confundir com arquitetura de software. Vamos entender melhor?

O DDD (Domain Driven Design, em tradução livre, desenvolvimento orientado a domínio) não é considerado isoladamente arquitetura de software, pois não faz parte de seu escopo a criação de uma estrutura para organização de componentes, namespaces ou formas de divisão de classes e interfaces. No entanto, é possível aplicar o DDD para entender os domínios do modelo de negócio e, a partir disso, escolher a arquitetura que se encaixa no projeto, como a arquitetura hexagonal, para desenvolver o software.

Diferenciar o que não é arquitetura de software se torna mais fácil do que buscar uma definição fechada. Sabemos que não se limita a design do software, ao código em si, bibliotecas, frameworks, hardware, metodologias ágeis ou princípios de desenvolvimento. No entanto, é interessante notar que todos esses elementos e escolhas são determinantes para definir uma boa arquitetura.

Arquitetura de software funciona como um “tudo em todo o lugar ao mesmo tempo” organizado a fim de evitar o caos. E por isso é essencial destrinchar o modelo de negócio para representá-lo em software.

Aqui chegamos em outro ponto crucial da arquitetura de software, que é aquele momento em que nos perguntamos “Vou ter que pensar em uma estrutura do zero para cada modelo de negócio?”. Já imaginou ter que informar todas as vezes como o software está organizado quando alguém entra na equipe? Precisar realizar uma busca minuciosa por um componente para executar sua manutenção… e não encontrá-lo? Ou ter falhas de segurança e não conseguir escalar uma aplicação?

Gif do filme Tudo em todo lugar ao mesmo tempo (2022) em que as versões alternativas da personagem Evelyn Qan Wang aparecem gritando uma de cada vez.

Fonte: cineset

Calma! Várias pessoas desenvolvedoras já passaram por isso antes e pensaram em soluções. É nesse contexto que entram em cena os padrões de arquitetura!

Padrões em arquitetura de software

Os padrões de arquitetura de software são peças fundamentais para concretizar toda a abstração, e a escolha adequada ao modelo de negócio é essencial, pois evita retrabalho e alto custo para o cliente e a equipe.

Além disso, podemos trabalhar com padrões de arquitetura diferentes no mesmo software, seja para atender determinados requisitos do negócio, facilitar a manutenção ou aproveitar vantagens de diferentes tecnologias.

Um padrão arquitetural é uma solução já estabelecida para desenvolvimento de softwares, sendo um modelo reutilizável para problemas já conhecidos. Esses padrões já foram estudados, testados em projetos reais e passaram por melhorias, ou seja, são soluções difundidas e aceitas no mercado. Confira algumas vantagens no uso de padrões arquiteturais:

  • Maior flexibilidade e escalabilidade de software
  • Facilidade de manutenção e evolução
  • Segurança
  • Melhor desempenho das aplicações
  • Redução de custos e riscos

Além de todos esses benefícios, a utilização de padrões de arquitetura facilita o diálogo entre equipes e pessoas por se tratar de soluções conhecidas no mercado. Pensando nisso, vamos conhecer alguns dos principais padrões de arquitetura de software?

Arquitetura client-server (cliente-servidor)

Vou contar uma breve história. A ideia de pensar em softwares a partir de camadas não é algo recente e essa perspectiva teve maior visibilidade nos anos de 1990 com a chegada dos sistemas cliente-servidores. Mas o que são esses sistemas e como é sua arquitetura?

Segundo Martin Fowler, no livro Padrões de arquitetura de aplicações corporativas, os sistemas eram separados duas camadas:

  1. Cliente: responsável por manter a interface com o usuário e um ou outro código da aplicação. Por exemplo, Delphi e VB disponibilizavam componentes visuais para trabalhar com banco de dados.
  2. Servidor: normalmente um banco de dados relacional, como o SQL.

Esse modelo funcionava bem para realizar atualizações mais simples em dados e se precisasse apenas exibir as informações. Porém, se houvesse a necessidade de implementação de lógica de domínio mais complexa, com validações, regras de negócio, verificações e até cálculos a manipulação do código, ficava mais difícil. Esse problema acontecia em muitos casos porque as pessoas desenvolvedoras escreviam a lógica no cliente, favorecendo a duplicação de código e outros problemas, como a criação de muitas telas para alterações simples em processos de buscas.

Outra possibilidade, porém menos usual, era manter a lógica de domínio embutida no banco de dados, no formato de stored procedures (em tradução livre, procedimentos armazenados). No entanto, o código permanecia limitado e engessado. Confira um exemplo de sistema que provê planilhas através de rede interna de uma empresa, no diagrama a seguir:

Diagrama de fluxo, representando verticalmente a comunicação entre cliente, rede e servidor de documentos com banco de dados. Entre cliente e rede uma seta com ponta dupla apontando para ambos, e entre rede e servidor de documentos com banco de dados também. Ao lado das setas está escrito “Requisição e resposta”.

Simultaneamente a ascensão da arquitetura cliente-servidor, o paradigma orientado a objetos também crescia, e a própria comunidade trazia respostas para as questões da lógica de domínio, entre elas, uma solução era abstrair a complexidade da lógica de domínio para uma camada estruturada.

Embora a separação em três camadas aparentemente solucionasse os problemas de manutenção das regras de negócio, muitos sistemas eram simples, e as ferramentas utilizadas para construir sistemas cliente-servidor não funcionam bem com o modelo em três camadas. Devemos lembrar também que os problemas simples eram bem resolvidos com a arquitetura cliente-servidor.

O cenário muda com a web, pois as pessoas desejavam usar navegadores para instalação de aplicações cliente-servidor. Hoje sabemos que essa não é uma solução adequada, pois se todas as regras de negócio estiverem em um cliente, o que acontece? Além de uma manutenção difícil, qualquer mudança na lógica de negócio irá obrigar uma refatoração da interface web…

Imagine se uma mudança no formato de buscas exija a modificação da interface da página web?

Ator Pedro Pascal começa gargalhando e termina como se estivesse chorando copiosamente com as mãos cobrindo o rosto.

Fonte: gif em Reddit

Essa prática vai, no mínimo, se refletir em custos para o desenvolvimento do software, pois demanda mais pessoas e tempo para implementação de novas funcionalidades. No entanto, isso não significa que a arquitetura cliente-servidor é ruim ou não deva ser utilizada, é preciso entender a lógica de negócio e avaliar a aplicação da arquitetura. Sobretudo quando pensamos em sistemas em two-tier e layer, pois são conceitos muito semelhantes.

Fowler descreve que sistemas cliente-servidor são entendidos como two-tier system, porque há uma separação física: o cliente é um desktop, e o servidor é o próprio servidor.

Por outro lado, o autor descreve que a ideia de layer é uma separação funcional e enfatiza que uma camada de lógica de domínio, por exemplo, pode ser executada tanto em um computador desktop quanto no servidor de banco de dados. Isso significa que você pode ter dois dispositivos (nós), mas ainda assim há três camadas distintas. Mesmo ao usar um banco de dados local, todas as três camadas podem ser executadas em um único laptop, mantendo a separação funcional entre elas.

Essa perspectiva casa com o que Sommerville, no livro Engenharia de software, descreve como padrão client-server para sistemas distribuídos:

Em uma arquitetura cliente-servidor, a funcionalidade do sistema está organizada em serviços — cada serviço é prestado por um servidor. Os clientes são os usuários desses serviços e acessam os servidores para fazer uso deles.

Sommerville continua informando que esse padrão é usado quando há a necessidade de compartilhamento de dados em um banco de dados a partir de acessos em locais distintos, novamente, é a ideia de separação de servidores para cada serviço, para cada lógica de domínio.

Arquitetura em camadas (Layers)

Em uma organização do sistema em camadas, cada uma delas tem uma responsabilidade e funcionalidade específicas. Reforçamos que esse modelo é diferente do padrão MVC, pois é possível modificar suas camadas de forma independente, por exemplo, podemos adicionar ou alterar uma View existente sem fazer alterações nos dados contidos na Model.

Por outro lado, no padrão de arquitetura em camadas, as funcionalidades de uma camada dependem dos recursos e serviços disponibilizados pela camada abaixo dela. São os níveis que fornecem serviços. Vamos entender com um exemplo de uma arquitetura em camadas para um software genérico a partir do diagrama a seguir:

Diagrama de estrutura de software, com quatro caixas conectadas por setas azuis apontando para baixo. As caixas contêm termos técnicos relacionados ao desenvolvimento e operação de sistemas: “Interface de usuário”, “Gerenciamento de interface do usuário. Autenticação e autorização”, “Lógica de negócio principal/Funcionalidade de aplicação. Recursos de sistema” e “Apoio de sistema (SO, banco de dados etc.)”.

Fonte: elaborado com base em Engenharia de software de Ian Sommerville

Na representação, a camada de interface do usuário, que está no topo, é a de apresentação e está ligada à camada abaixo, que por sua vez gerencia o processo de autenticação e autorização entre o usuário e o sistema. Abaixo da camada de gerenciamento está a que abriga a lógica de negócio principal e os recursos do sistema, e a camada inferior é responsável pelo apoio ao sistema, que pode ser representada pelo armazenamento de dados, sistema operacional, banco de dados, dentre outros.

Dessa forma, temos uma representação que segue de um nível mais alto até o mais baixo, não em grau de importância, mas em relação à arquitetura de um sistema. A quantidade de camadas não é um número ou um modelo fechado, pois podem ser podem ser divididas em mais camadas caso exista a necessidade.

Sommerville, na obra Engenharia de software pontua que esse modelo normalmente é utilizado em diferentes situações:

  • Quando desejamos construir um recurso novo a partir de um sistema que já existe.
  • Quando o desenvolvimento é feito por equipes diferentes, em que cada uma é responsável por uma camada de funcionalidade.
  • Quando o software tem um requisito de proteção multinível.

Mas como isso funciona na prática?

Vamos imaginar que você precisa desenvolver um formulário para inscrição na Imersão Dev da Alura. Quais elementos vamos precisar? O formulário é um recurso novo associado ao sistema da Alura. Dessa forma, podemos desenhar nossa arquitetura em camadas, confira a imagem a seguir:

Diagrama de fluxo de um sistema de inscrição online, representando a estrutura e o fluxo de dados desde o formulário de inscrição, passando pela interface, servidor web até o banco de dados. Há uma seta com ponta dupla entre interface, servidor web e banco de dados. O diagrama é apresentado em uma escala que vai do alto nível (interface) ao baixo nível (banco de dados). O formulário de inscrição da Imersão Dev é enviado para a interface. A interface se comunica com o servidor web. O servidor web se conecta ao banco de dados. O diagrama usa cores neon verdes e azuis para representar os componentes do sistema e setas para indicar o fluxo de dados. O nome “alura” aparece no canto inferior direito em letras brancas.

O diagrama apresenta as camadas do mais alto para o mais baixo. Temos três camadas, que são:

  1. Interface com front-end para o formulário de inscrição.
  2. Um servidor com uma API para tratar os dados das pessoas inscritas.
  3. Um banco de dados para armazenamento das informações.

A partir dessa separação e da comunicação entre as camadas, entendemos que se trata de camadas fechadas, pois a comunicação acontece de um nível para o abaixo dele e vice-versa. Por exemplo, a página front-end não conversa diretamente com o banco de dados, pois é necessário que a requisição seja tratada pela API do servidor para que os dados sejam enviados ao banco.

Devemos enfatizar que a arquitetura do exemplo mantém todas as camadas fechadas mas nem sempre será desse jeito. Permitir a comunicação entre camadas de níveis diferentes, assim como a quantidade delas, vai depender das necessidades do seu projeto de software.

Você pode conhecer mais no vídeo do Matheus Castiglioni sobre estilo de arquitetura em camadas.

Arquitetura Model-View-Controller (MVC)

Talvez o padrão MVC seja um dos primeiros a ser conhecido no aprendizado em desenvolvimento web, pois o padrão é bastante flexível e tem uma alta escalabilidade e reusabilidade.

A arquitetura MVC faz a separação da apresentação e a interação dos dados do sistema, que é estruturado em três componentes lógicos: model (modelo), view (visualização, visão ou vista) e controller (controlador). Esses componentes interagem entre si da seguinte maneira:

  • Model é responsável por estabelecer as regras de negócio, interagir com o sistema de dados e fazer as operações associadas a esses dados.
  • View define e gerencia como os dados são apresentados ao usuário.
  • Controller é a camada intermediária entre model e view, interage com o usuário (por meio de teclas, cliques do mouse, requisições etc.) e é responsável por responder de acordo.

O padrão MVC pode ser representado pelo diagrama a seguir:

Diagrama de fluxo do padrão de arquitetura MVC (Model-View-Controller) em um fundo preto com detalhes em verde e branco. O diagrama explica como o usuário interage com a view, que faz uma requisição ao controller, que solicita dados à model, que por sua vez, faz uma requisição de dados ao banco de dados. Os dados são então retornados à model, passados para o controller e renderizados na view para o usuário. O diagrama usa cores neon verdes e azuis para representar os componentes do sistema e setas para indicar o fluxo de informações. O nome “alura” aparece no canto inferior direito em letras brancas. O diagrama é um exemplo de como um padrão de arquitetura pode ser usado para organizar e modularizar um sistema informático.

Referência: Interview Bit

Segundo Martin Fowler, em Padrões de arquitetura corporativa, temos duas separações chave dentro do padrão MVC. Como o diagrama acima representa, é imprescindível separar a view (apresentação) do model (modelo) e o controller (controle) da view.

Arquitetura orientada a serviços (SOA)

Neste padrão, o sistema é dividido em serviços independentes, sendo cada serviço responsável por uma funcionalidade específica. Em seguida, há o desenvolvimento de interfaces bem definidas para que o diálogo entre os serviços ocorra sem precisar depender de uma linguagem ou plataforma.

A ideia é que um software preste um serviço para outro serviço. Mas o que são os serviços?

Um serviço é uma função independente e bem definida que representa uma unidade de funcionalidade de um negócio. Entre as características da arquitetura orientada a serviços, cada serviço pode trocar informações com outro serviço. Um serviço também é stateless, ou seja, é sem estado e não depende do estado de outro serviço.

Vamos entender com um exemplo prático?

Imagine que sua equipe foi contratada para desenvolver um sistema para uma clínica médica. Nesse sistema, a recepção poderá buscar pacientes e marcar consultas através do software instalado localmente; os pacientes podem marcar consultas pelo aplicativo mobile e pelo portal web; e os médicos podem solicitar exames por meio do portal ou da aplicação desktop. A partir dos requisitos, confira o diagrama abaixo de uma possível solução para o desenvolvimento do sistema:

Diagrama de fluxo de um sistema de gerenciamento de consultas médicas. O diagrama é apresentado em cores neon contra um fundo preto, destacando os componentes e conexões do sistema. O diagrama mostra como três tipos de consumidores do serviço (aplicação web, aplicação mobile e aplicação desktop) se comunicam com um Enterprise Service Bus (ESB), que coordena três serviços: buscar pacientes, marcar consultas e solicitar exames. Esses serviços acessam dois bancos de dados para armazenar e recuperar as informações necessárias. O diagrama usa cores neon verdes e azuis para representar os elementos do sistema e setas para indicar o fluxo de dados. O nome “alura” aparece no canto inferior direito em letras brancas. O diagrama é um exemplo de como um sistema informático pode ser usado para gerenciar serviços relacionados à saúde.

Fonte: baseado em What is service oriented architecture

No diagrama, notamos que em vez de separar o sistema em três aplicações diferentes, com lógicas de negócio distintas para cada aplicação, podemos criar uma camada de serviços para cada funcionalidade e trabalhar em interfaces diferentes que consomem os serviços. Esta é uma forma de centralizar os serviços e regras de negócio a fim de economizar tempo, infraestrutura e evitar redundância de código.

Com a arquitetura SOA, sua equipe desenvolveu um sistema com base em serviços que podem ser utilizados por outras aplicações. Entre os serviços e a interface há uma camada intermediária chamada de Enterprise Service Bus (ESB, barramento de serviços corporativos, em tradução livre). E como o ESB funciona?

Segundo artigo da AWS, o barramento funciona assim:

  1. O barramento recebe mensagem de entrada ou saída de um endpoint.
  2. Determina o endereço dos endpoints de destino verificando as regras da política de negócios.
  3. Processa a mensagem e a envia para o endpoint de destino.

O barramento de serviço corporativo é peça fundamental para a integração entre os componentes de software que representam as funcionalidades do sistema. Embora a arquitetura SOA seja interessante para diversos casos, sua aplicação e uso de EBS se tornam limitados em situações que envolvem a integração complexa de sistemas herdados, por isso é necessário o estudo de novas soluções como arquitetura em Microsserviços e análise da necessidade de aplicação.

Pipes and filters (PF ou duto e filtro)

A arquitetura de pipes and filters (duto e filtro, em tradução para o português) divide o sistema em componentes independentes, chamados de filtros. Cada filtro é responsável por uma operação específica no fluxo de um dado em tempo de execução. Primeiro, o dado entra por um canal, em seguida ocorre sua a transformação, e os dados processados são enviados para um canal de saída.

Confira um exemplo para entender melhor. Sua equipe agora ficou responsável por desenvolver um sistema de análise de dados para bloquear e-mails de spam. Vamos pensar na arquitetura do nosso sistema com base no diagrama a seguir:

Um diagrama de fluxo detalhado do processo de classificação de e-mails para identificar spam. O diagrama apresenta várias etapas, começando com a “Entrada de email” e terminando com a “Saída final com a classificação”. As etapas intermediárias incluem “Pré-processamento de Texto”, “Filtro de Tokenização”, “Remoção de Stopwords”, “Filtro de Stemming” e “Filtro de extração de características”. O diagrama é concluído com o “Classificador de Spam” que leva à saída final. O diagrama usa cores neon verdes e azuis sobre um fundo preto com linhas diagonais finas. O nome “alura” aparece no canto inferior direito em letras brancas. O diagrama é um exemplo de como um algoritmo de aprendizado de máquina pode ser usado para analisar e classificar e-mails.

O diagrama apresenta os seguintes elementos:

  • A entrada do sistema é um e-mail.
  • O primeiro filtro realiza o pré-processamento do texto do email, removendo caracteres indesejados e realizando outras tarefas de limpeza.
  • No segundo filtro, o texto é tokenizado em palavras.
  • O terceiro filtro remove as chamadas "stopwords" (palavras vazias ou palavras de parada, em tradução livre), que no nosso caso são palavras comuns que geralmente não contribuem para a identificação de spam.
  • O filtro de stemming reduz as palavras às suas formas radicais.
  • O conjunto de características extraídas é alimentado para um classificador de spam.
  • O classificador de spam decide se o email é spam ou não com base nas características fornecidas.
  • O resultado final, sua saída, é a classificação do e-mail como spam ou não.

Cada filtro é um componente independente que pode ser modificado ou substituído sem afetar os outros filtros. Essa modularidade facilita a manutenção e a evolução do sistema de filtragem de spam. Nesse sentido, o padrão PF se torna uma opção para aplicações que precisam processar dados de forma eficiente e escalável, porém sua manutenção pode se tornar complexa.

A nomenclatura pipe and filter é originária do sistema original Shell do Unix. O Shell é um tipo de software chamado de interpretador, cujo modelo possibilita vincular os processos utilizando pipes, que realizam o fluxo no qual um texto é passado de um processo para o outro. Após a entrada de um dado, para que ocorra sua transformação, os filters são aplicados para de fato filtrar os dados que podem ser processados no fluxo do programa.

Entendendo brevemente sobre o funcionamento do modelo de arquitetura pipe and filter, notamos que sua aplicação é bastante útil para o processamento e transformações sequenciais de dados, porque apresenta um fluxo de fácil compreensão e possibilita a reutilização da transformação por meio de filters.

Você pode se aprofundar no funcionamento do Shell lendo o artigo Pipes and filters, em inglês.

Arquitetura monolítica

É um único bloco criado de forma vertical com todos os componentes, funcionalidades e estruturas implementadas em conjunto, isso significa que nesse bloco as funcionalidades podem rodar em um único processo, ou se for modularizado, todas as camadas do software são interdependentes. Cada parte do software modificada pode interferir no comportamento do todo.

De acordo com a análise de Sam Newman, em Criando microsserviços, podemos agrupar os sistemas monolíticos que surgem frequentemente em três tipos: sistema monolítico com um único processo, sistema monolítico modular e sistema monolítico distribuído.

Uma aplicação web pode ser definida como um monolito de acordo com o seu pacote de publicação para a produção. Dessa forma, se você tem um único pacote, sua aplicação é monolítica. Mas como é um diagrama de uma arquitetura monolítica?

Confira o diagrama abaixo, que apresenta a arquitetura genérica de um software de uma lanchonete para pedidos online.

O diagrama ilustra os três componentes principais do sistema: a interface do usuário, que é a parte que interage com os usuários finais; a lógica de negócios, que é a parte que implementa as regras e os processos do sistema; e o sistema de gerenciamento de banco de dados, que é a parte que gerencia o armazenamento e a recuperação dos dados. Esses componentes estão conectados a um banco de dados central, que é representado por uma estrutura esquemática com ícones de tabelas, consultas e relatórios. O diagrama usa cores neon verdes e azuis sobre um fundo preto com linhas diagonais finas. O nome “alura” aparece no canto inferior direito em letras brancas. O diagrama é um exemplo de como um sistema monolítico pode ser organizado e visualizado.

No sistema de pedidos online, a pessoa usuária utiliza a interface para acessar os serviços de menu, pedidos e realizar o pagamento. Há uma camada responsável pela gestão dos dados e o banco de dados para armazenamento.

Nomeamos de sistema monolítico modular aquele que apresenta uma característica divisão em módulos de sua camada de serviços, como percebemos sua divisão dos pedidos, menu e pagamento dentro da camada de regra de negócio. Os sistemas modulares possibilitam certo paralelismo nos trabalhos,

Mas isso tudo precisa ser pensado de acordo com a lógica de negócio. Por exemplo, imagine que em nosso sistema, precisamos permitir que uma pessoa faça um pedido e um pagamento de algum item que não está no cardápio. Podemos então executar o processo de pedido e pagamento sem executar o acesso ao módulo de menu.

Essa divisão não anula a concepção de arquitetura monolítica, mas tem o objetivo de organizar o código para que cada serviço represente uma unidade funcional, além de possibilitar seu desenvolvimento com certa independência, garantindo maior legibilidade e facilidade de manutenção se compararmos com um sistema monolítico com um único processo.

Um sistema monolítico com um único processo tem toda sua base de código executada dentro de um único processo. As funcionalidades do sistemas, devido à sua integração, compartilham recursos e o mesmo espaço de endereçamento de memória em um contexto de único processo em execução.

Sam Newman descreve também o sistema monolítico distribuído. O autor afirma que a definição se parece muito com a arquitetura SOA, porém, frequentemente não envolve as vantagens da arquitetura orientada a serviços. Na prática, um monolito distribuído normalmente surge quando não há foco em entender o problema a ser solucionado, resultando em arquitetura altamente acoplada, em que alterações teoricamente pequenas abrangem um escopo muito maior e causam falhas em diversos pontos do sistema.

No entanto, se houver um planejamento inicial e estudo da lógica de negócio, é possível trabalhar com monólitos modularizados e não permitir um acoplamento total de todos os componentes.

Embora tenha desvantagens como utilizar uma única tecnologia, ser pouco flexível para escalabilidade e apresentar o famigerado único ponto de falha – quando ocorre o impedimento para executar o processo total se um dos componentes não funcionar (por exemplo, não conseguir fazer o pagamento de um lanche porque o sistema de pedidos falhou) – as vantagens são muito interessantes para determinados negócios como:

  • Menor complexidade de implementação.
  • Deployment de uma unidade, um pacote em produção.
  • Desenvolvimento rápido, pois permite a execução de uma aplicação por uma equipe menor, a viabilização de um MVP (produto mínimo viável) ou mesmo uma prova de conceito com mais velocidade.

As vantagens e desvantagens técnicas dependem do escopo e da proposta do projeto. O uso de uma stack única pode ser encarado como uma vantagem se a aplicação não demandar de compartilhamento de recursos ou alto desacoplamento, pois dessa forma a manutenção e o diálogo entre a equipe provavelmente será mais fluída. No entanto, dependendo do problema a ser solucionado, pode ser entendido como pouco flexível para o desenvolvimento de novas funcionalidades. Já imaginou desenvolver novas funcionalidades em um monolito de sistema de pagamento em Cobol?

É por conta de alguns dos fatores citados que sistemas monolíticos são entendidos como legados e que a arquitetura monolítica é vista como ultrapassada por algumas pessoas na comunidade de tecnologia. Todavia, sabemos que nem tudo são microsserviços e que o sucesso ou fracasso da implementação da arquitetura vai depender do entendimento do problema a ser resolvido.

A arquitetura monolítica é muito útil e vale a pena estudá-la. Há cases de sucesso de sua aplicação em grandes empresas, como o caso da Prime Video da Amazon, que migrou por compreender que o monolito funciona de forma mais adequada ao seu modelo de negócio, rendendo uma economia de 90% de seus custos de manutenção.

Stack Overflow, Shopify e Github também aplicam a arquitetura monolítica em seu modelo de negócio. Dessas empresas, a única que está migrando para microsserviços é o Github, vamos conhecer sobre essa arquitetura a seguir?

Você pode conferir o case do Stack Overflow no podcast Alura Cases.

Arquitetura baseada em microsserviços

Os microsserviços podem ser considerados uma variante da arquitetura orientada a serviços e antagonista aos monólitos, pois encontramos serviços ainda menores e mais independentes, com linguagens e plataformas distintas.

Vamos retomar o exemplo do software de lanchonete para compreendermos na prática. A lanchonete cresceu, o cliente pretende expandir os negócios online, e o início dessa expansão vai ocorrer por meio da implementação de um serviço de análise de dados. Pensando nisso, a equipe decidiu migrar o monolito para arquitetura baseada em microsserviços para acompanhar o crescimento do modelo de negócio. O diagrama ficou da seguinte forma:

Diagrama de fluxo de usuário para um aplicativo de lanchonete. O diagrama mostra que a interface do usuário se conecta ao menu, aos pedidos e ao pagamento, que são armazenados em bancos de dados separados, e como os dados de pagamento são usados para análise de dados. O diagrama é feito em neon azul sobre um fundo preto e tem o logotipo da Alura no canto inferior direito.

Agora os serviços foram decompostos em serviços menores, os chamados microsserviços, trazendo mais flexibilidade para a divisão do trabalho na equipe e a implementação de novos serviços. Além da migração para microsserviços, o segundo passo foi trabalhar no microsserviço de análise de dados que foi solicitado pela rede de lanchonetes, para possibilitar a avaliação de métricas de venda, pedidos e repensar no cardápio da empresa.

A arquitetura em microsserviços permite o uso de diferentes tecnologias e linguagens. Tomando o nosso novo sistema como exemplo, podemos integrar o microsserviço de pagamento com a linguagem Java e a análise de dados com Python, algo que não é possível em um monolito.

Outro ponto importante do negócio é que a rede de lanchonetes atende 24 horas por dia durante toda a semana. Com os microsserviços é mais fácil redimensionar os recursos de infraestrutura para os produtos de acordo com os picos de acesso.

Microsserviços são aplicações distribuídas, compostas por diversas aplicações menores para criar sistemas complexos. Cada serviço atua de forma independente e sua modelagem tem como base um domínio de negócios. É possível encapsular uma funcionalidade através de um serviço e deixá-la disponível para outros por meio de, por exemplo, APIs REST ou serviços de mensageria.

Os microsserviços facilitam a escalabilidade e a mobilidade do sistema. Em contrapartida, as exigências para sua implementação são maiores, pois os custos e a complexidade de implantação e manutenção são altos. A pergunta a se fazer nesses casos é: “A complexidade da arquitetura de microsserviços é justificada?”.

Dessa forma, a aplicação da arquitetura baseada em microsserviços deve ser analisada com cuidado devido à sua complexidade e considerando, sobretudo, a necessidade de sua implementação de acordo com o domínio do negócio.

Para se aprofundar, leia um artigo que descreve o caso da migração de monolitos para microsserviços pela Netflix.

Arquitetura hexagonal

O padrão de arquitetura hexagonal, ou ports and adapters (portas e adaptadores, em tradução literal), trata separadamente a lógica de negócio e a lógica de implementação. A arquitetura hexagonal é bastante útil pois desacopla a aplicação do ambiente através dos conceitos de portas e adaptadores, facilitando a manutenção e integração com outros componentes da aplicação.

As portas são as interfaces que permitem que a lógica de de negócios se comunique com o mundo externo, e os adaptadores são responsáveis por implementar as portas. Nesse sentido, sua aplicação é vantajosa porque permite a implementação também de outras arquiteturas com certa flexibilidade e, por isso, o core (a essência) da aplicação deve estar bem definido.

A lógica de negócio é fundamental para a criação das funcionalidades do sistema, e o Domain-Driven-Design ajuda a entender os domínios e atuar na construção da arquitetura hexagonal com eficiência.

A arquitetura se divide em camadas autocontidas e isoladas do mundo externo. O domínio da aplicação é a peça que rege o desenvolvimento dos componentes. A premissa da arquitetura hexagonal é proteger a camada de domínio no centro e permitir sua conexão com o mundo externo através de uma interface padrão, apenas substituindo o adaptador que utilizar, ou seja, se você precisar integrar seu sistema com uma biblioteca, infraestrutura etc.

A arquitetura hexagonal tem o objetivo de solucionar um dos principais problemas no desenvolvimento de software: o acoplamento. Essa arquitetura traz uma perspectiva diferente sobre a ordem das camadas de uma aplicação, em que normalmente temos a primeira camada como a interface e a última como um banco de dados para armazenamento. A ideia é entender front-end e back-end como camadas externas ao seu domínio!

A aplicação da arquitetura hexagonal ocorre através de princípios como a inversão de dependência.

Vamos entender seu funcionamento com um diagrama de arquitetura Hexagonal para um Sistema de Bibliotecas:

A imagem mostra um diagrama de arquitetura de software em um fundo preto, com linhas e texto em cores neon. O diagrama é composto por uma série de retângulos e setas que indicam o fluxo de informações entre diferentes componentes do sistema, como “Interface Web”, “REST”, “GraphQL”, “gRPC”, até o “Banco Dados”. Os componentes estão contidos dentro de um hexágono grande, representando a estrutura geral do sistema. A palavra “alura” é visível no canto inferior direito da imagem.

Fonte: elaborado com base em Marco Tulio Valente, Engenharia de software moderna

No diagrama, entendemos como os usuários interagem com o sistema através de três diferentes interfaces: web, mobile e um sistema externo. Independentemente da forma de acesso, essa interação é sempre facilitada por adaptadores. Os adaptadores, por sua vez, conectam-se a uma porta de entrada, que especifica os métodos para realizar diversas operações (buscar itens no catálogo da biblioteca, fazer empréstimos, cadastrar usuários etc.). Esses métodos são concretamente implementados pelas classes que representam os domínios no sistema em questão e podem incluir entidades como Livro, Usuário, Bibliotecário, Empréstimo, Reserva, entre outros.

Para armazenar informações importantes, o sistema utiliza uma porta de saída, equipada com métodos para salvar e recuperar dados relacionados a livros, empréstimos etc. Conectado a essa porta, há um adaptador responsável por executar as operações em um banco de dados relacional.

É possível que um sistema tenha várias portas de entrada e saída, sempre localizadas no hexágono interior, próximo às classes de domínio. Em uma porta específica, seja de entrada ou saída, é possível conectar um ou mais adaptadores, que ficam posicionados no hexágono mais externo. Essa organização facilita a interação dos usuários com o sistema de maneira eficiente e estruturada por ser bastante flexível. Se for necessário modificar o banco de dados para um não relacional ou para outro banco relacional, isso não vai interferir nas classes de domínio, da mesma forma se desejasse trabalhar as interfaces todas via API REST, por exemplo. Muito interessante, não é?

Você pode aprofundar seus conhecimentos lendo a Monografia com um roteiro para a utilização da arquitetura hexagonal no projeto dirigido pelo domínio e assistindo ao Alura+ sobre arquitetura hexagonal.

Qual a melhor arquitetura de software?

Já entendemos que há vários tipos de padrões de arquitetura de software, e também há outros, como arquitetura em nuvem, arquitetura distribuída, arquitetura em cluster, arquitetura de gerenciamento de configuração, arquitetura de gerenciamento de desempenho, arquitetura de gerenciamento de segurança, arquitetura orientada a eventos, publish-subscribe (Pub/Sub), clean architecture, peer-to-peer. Embora existam muitos modelos, como podemos escolher um padrão?

Escolher a solução que está no hype e perfeitamente executada em um contexto específico vai funcionar com certeza para o meu problema? Não sabemos (provavelmente não)! Como afirmamos no início do artigo, é necessário compreender o problema para começar a pensar em uma solução.

Dessa forma, a escolha da melhor arquitetura de software começa pela compreensão do problema que será transformado em solução. Utilizar os modelos e padrões ajudam muito a desenvolver softwares com qualidade, mas para isso é preciso conhecer o seu modelo de negócio, as limitações e vantagens de cada padrão. Em seguida, é preciso ter em mente que a arquitetura de software não é um formato fechado, não é engessado e permite ter a liberdade para tomar as melhores decisões em seu código. O segredo é estudar e praticar bastante!

A imagem mostra um diagrama gráfico que representa componentes da “Arquitetura de software”. O termo está centralizado em um círculo azul e verde, com linhas pontilhadas conectando-o a cinco retângulos que contêm os seguintes termos: “Design de Código”, “Padrões de Arquitetura”, “Padrões de Projeto”, “Metodologias Ágeis” e “Design de Software”. O fundo é escuro, e o logo da “alura” aparece no canto inferior direito da imagem.

Conclusão

Arquitetura de software não é 0 e 1, é algo vivo que pode crescer e evoluir. Na prática exige estudo contínuo e, embora seja um assunto complexo para delimitar na teoria, não é algo inacessível. Você é completamente capaz de realizar a aplicação de padrões e sua principal habilidade é entender bem o problema para desenhar e arquitetar uma solução com base em padrões e boas práticas, afinal escolhas inadequadas geram retrabalho e custo maior no desenvolvimento de software.

Diante da vasta gama de padrões e abordagens em arquitetura de software apresentados, é evidente que a escolha adequada desempenha um papel crucial no desenvolvimento de sistemas eficientes e sustentáveis. Além disso, todos os padrões apresentam vantagens e desvantagens, e a jornada de transformar uma ideia em software funcional exige, além de habilidades técnicas, a compreensão profunda dos requisitos do negócio e das nuances do domínio em questão.

Ao compreendermos a distinção entre princípios de design, como SOLID, e a arquitetura de software, destacamos a importância de integrar esses princípios no contexto de um padrão arquitetural. Afinal, não se trata apenas de escrever código eficiente, mas de construir uma estrutura robusta que atenda às demandas específicas de cada modelo de negócio.

Os padrões de arquitetura apresentados, como arquitetura em camadas, arquitetura orientada a serviços (SOA), MVC, client-server, pipes and filters, monolítica, microsserviços e hexagonal, oferecem soluções distintas para diferentes desafios. A escolha entre eles deve ser cuidadosamente ponderada, considerando não apenas a tendência do momento, mas as exigências do projeto, a escalabilidade desejada, a manutenção futura e a natureza do domínio.

A utilização de padrões de arquitetura proporciona benefícios tangíveis, como flexibilidade, escalabilidade e segurança, além de simplificar a comunicação entre equipes e acelerar o processo de desenvolvimento. Conforme afirmado por Robert C. Martin, o objetivo da arquitetura de software é minimizar os recursos humanos necessários, refletindo a busca contínua por eficiência e qualidade.

Em última análise, a escolha de um padrão de arquitetura de software não deve ser uma adesão cega à moda atual, mas uma decisão estratégica baseada na compreensão profunda dos requisitos do negócio. Em um cenário em constante evolução, onde a tecnologia e as demandas do mercado se transformam rapidamente, a flexibilidade e a adaptabilidade das escolhas arquiteturais desempenham um papel crucial no sucesso em longo prazo de qualquer empreendimento de desenvolvimento de software.

Referências

Camila Pessôa
Camila Pessôa

Oi oi, sou a Camila ! Ingressei na área de tecnologia por meio da robótica educacional e comecei os estudos em programação com desenvolvimento web e foco Back-end com Node.js. Adoro ler, assistir séries/filmes, animes, jogar e passear ao ar livre com minha filhota.Tenho tenho grande paixão por educação e tecnologia, pois acredito que essa combinação é transformadora! :)

Veja outros artigos sobre Programação