A popularidade do estilo arquitetural baseado em camadas advém de sua simplicidade aparente e familiaridade. Há de se considerar também que ele é natural para a maioria das estruturas organizacionais e é “receita fácil” para “mania ágil” (não confundir com agilidade) de “sair programando”, repetindo o mais-do-mesmo.
Arquiteturas baseadas no estilo de camadas são tecnicamente particionadas (em oposição às arquiteturas particionadas por domínio). Ou seja, grupos de componentes, ao invés de serem estabelecidos pelo domínio, são formados por sua função técnica (como apresentação ou negócio)
Conceito fundamental
A ideia essencial desse estilo arquitetural é decompor os componentes de um sistema em uma pilha de camadas. Tradicionalmente, uma camada é autorizada a utilizar apenas a camada imediatamente abaixo. Uma camada inferior funciona como uma espécie de “máquina virtual” para a camada acima.
As camadas “mais baixas” são responsáveis por prover funções mais elementares que servem como “blocos de construção”, nas camadas “mais altas”, para a elaboração de funções mais sofisticadas.
Na sua forma mais restrita, este estilo arquitetural indica que uma determinada camada tenha acesso apenas a camada imediatamente abaixo, sendo que todas as demais ficam ocultas. Dessa forma, habilitam atributos de qualidade desejáveis como manutenabilidade, portabilidade e reuso, logo, promovendo o evolvability.
Como cada camada depende apenas da camada imediatamente abaixo desta, todas as camadas “mais baixas” podem ser substituídas ou “emuladas”, facilitando testes. Quanto mais camadas são adicionadas, maiores são as oportunidades de troca, entretanto, mais comuns são implicações para a performance.
Violações mais frequentes
Ambas violações, quando observadas, geralmente são bem justificadas. Entretanto, acabam comprometendo parte dos benefícios da adoção desse estilo arquitetural e são indícios de que, provavelmente, 1) outro estilo fosse mais adequado ; ou 2) houve exagero na definição de quantas camadas eram necessárias.
Camadas telefone-sem-fio
Um problema recorrente em muitas aplicações LOB (line-of-business) são implementações “ingênuas” do estilo arquitetural baseado em camadas, onde muitas não adicionam valor algum a solução.Não são raras aplicações SPA, que interagem com APIs “pseudo-REST” CRUD, que acionam serviços de aplicação, que apenas traduzem objetos inputmodels em commands com propriedades idênticas, que são direcionado a um mecanismo de mensageria in-memory (como o MediatR em .NET), que aciona handlers, que materializam “entidades anêmicas”, que são, submetidas a persistência em repositórios (devidamente abstraídos em interfaces), que traduzem tais entidades em objetos de persistência, que são, enfim, salvos em um banco de dados.
“Pular” camadas, acessando camadas “mais baixas” diretamente
Com alguma frequência, algumas implementações do estilo arquitetural em camadas “afrouxam” a restrição de que, em qualquer nível, apenas a “camada imediatamente abaixo” pode ser acessada diretamente. A justificativa geralmente tem relação com a performance.
Uma aplicação escrita em .NET ou Java, por exemplo, ao fazer uso direto de um recurso do sistema operacional, torna-se menos “portável”.
“Camadas compartilhadas” ou verticais
Outra violação comum são as “camadas compartilhadas” ou verticais, diretamente acionadas por todas as demais camadas da solução. Geralmente, essas “camadas” fornecem features genéricas e úteis, como para instrumentação ou segurança.
Na prática, essas implementações verticais não são “camadas” de fato. Ou, ainda, em cenários mais complexos, habilitam uma visão arquitetural 2D, com uma dimensão de negócios e outra técnica, o que pode ser interessante. De qualquer forma, é importante destacar a explosão do acoplamento aferente nas camadas verticais.
Camadas comuns em aplicações LOB (line-of-business)
Aplicações de negócios desenvolvidas com arquiteturas baseadas em camadas geralmente apresentam variações de quatro camadas lógicas: 1) apresentação; 2) negócios; 3) persistência; e 4) banco de dados. Algumas vezes, as camadas de persistência e banco de dados são “mescladas” e ampliadas para uma descrição mais genérica, como “infraestrutura”. Outras vezes, a camada de negócios é “fragmentada” em “aplicação” e “domínio”.
Um jeito (nem tão) antigo de fazer software LOB
Até bem pouco tempo, a “análise de sistemas” consistia em descobrir quais relatórios e consultas um software precisaria produzir. Daí, se inferia que dados seriam necessários e que “cadastros” teriam que ser elaborados.Nessa “visão antiga”, o banco de dados é o rei. Aliás, seguindo essa linha, é natural enxergar o banco como lugar ideal para implementar lógica de negócio. Há um bocado de “gente grande” que ainda pensa e desenvolve software desse jeito, procurando apenas formas de evoluir no frontend para criar “telinhas mais bonitas”.
As camadas de uma aplicação são ótimo registro para o architecture haiku.
Conway, outra vez!
As arquiteturas de 4 (as vezes 3) camadas, tão comuns em aplicações LOB, são, também expressão da lei de Conway.
Qualquer empresa que projeta um sistema, inevitavelmente, produz um projeto cuja a estrutura é uma cópia da estrutura de comunicação da organização. – Melvin Conway
Na maioria das organizações é comum encontrar times de desenvolvedores especialistas em frontend (UI e UX), backend, especialistas de negócio e DBAs. Essa “estrutura” da organização se encaixa naturalmente no modelo em camadas adotado em boa parte do mercado, se tornando assim, natural.
A implementação de um estilo arquitetural que não seja o baseado em camadas implica na revisão da organização da empresa, caso essa esteja organizada em times funcionais.
Implicações para o Deploy
Embora o estilo arquitetural em camadas preconize a independência de cada camada, com vistas a melhoria do evolvability, não é difícil perceber que esta “promessa” não se cumpre, com base na forma como ocorrem os deploys das aplicações.
Não raro, as camadas de apresentação, negócios e persistência formam um bloco único de deploy com acoplamento eferente para o banco de dados, que segue gestão independente.
Outras vezes, a camada de apresentação é segmentada no deploy dando autonomia para atualizações e implementações de “perfumarias estéticas” (reproduzindo aqui comportamento desrespeitoso e míope, frequentemente observado no mercado).
Finalmente, há ainda cenários, geralmente em aplicações menores, onde todas as camadas formam um único bloco de deploy.
Camadas abertas e fechadas
Eventualmente, uma camada pode dispensar as features fornecidas por aquela, ou aquelas, imediatamente abaixo. Neste caso, precisa “pular” estas camadas, acessando diretamente aquela que provê aquilo de que necessita.
Idealmente, esses “pulos” precisam ser projetados, indicando as camadas dispensáveis como “abertas”. Enquanto aquelas que não podem ser ignoradas devem ser apontadas como “fechadas”.
Aplicações que fornecem APIs para consumo externo, por exemplo, porém que dispensam o uso na própria camada de apresentação, mantém a camada das APIs abertas.
Outra decisão que “abre” algumas camadas são implementações do padrão fast-lane reader, onde a camada de apresentação faz queries diretamente contra o banco de dados, tentando maximizar performance.
As decisões de quais camadas devem ser abertas, e os porquês, devem ser registradas em ADRs. O monitoramento ativo das ligações entre as camadas é uma excelente base fitness functions.Indicações e contraindicações
O estilo arquitetural em camadas tem fit perfeito para domínios onde seja possível resolver problemas compondo soluções a partir de blocos mais primitivos. Uma camada 0, nesse contexto, forneceria os elementos mais básicos que seriam combinados nas camadas superiores, de maneira similar ao que acontece com os protocolos HTTP -> TCP -> IP.
O estilo em camadas, por outro lado, não tem fit natural com aplicações LOB modernas. Afinal, embora suportem bem evoluções tecnológicas, sofrem para acomodar adaptações necessárias para o negócio. Tendo em vista que qualquer mudança de negócio desencadeia, potencialmente, mudanças em todas as camadas, aponta eveleado changing coupling comprometendo o evolvability.
Para cada jornada, as ferramentas certas
// TODO
Antes de avançar para o próximo capítulo, recomendo algumas reflexões:
- Você já lidou, ou está lidando, com arquiteturas “viciadas” pela implementação do sinkhole anti-pattern.
- Você já lidou com arquiteturas baseadas em camadas “exóticas” em LOB? Como elas eram?
- Você enfrenta problemas de performance em sua aplicação decorrentes de decisões de manter todas as camadas fechadas?