Richards & Ford – Fundamentos da Arquitetura de Software (2024)
Página 37
página como uma característica importante da arquitetura. Para permitir que o sistema mude sem diminuir o desempenho, a arquitetura cria uma função de adequação como um teste que mede o tempo de carregamento de cada página, então faz o teste como parte da integração contínua do projeto. Assim, os arquitetos sempre conhecem o status das partes críticas da arquitetura porque têm um mecanismo de verificação na forma de funções de adequação para cada parte.
Não detalharemos muito as funções de adequação aqui. Porém destacaremos as oportunidades e os exemplos da abordagem onde forem aplicáveis. Note a correlação entre a frequência com que as funções de adequação são executadas e o feedback dado. Você verá que adotar práticas de engenharia ágil, como a integração contínua, o provisionamento de máquina automatizado e práticas semelhantes, facilita criar arquiteturas resilientes, além de mostrar o quanto a arquitetura se torna interligada com as práticas de engenharia.
Operações/DevOps
A interseção recente e mais óbvia entre a arquitetura e os campos afins ocorreu com o advento do DevOps, orientado pelo repensar dos axiomas da arquitetura. Por muitos anos, várias empresas consideraram as operações como uma função separada do desenvolvimento de software; muitas vezes, elas terceirizavam as operações como uma medida para economizar custos. Muitas arquiteturas planejadas durante os anos 1990 e 2000 pressupunham que os arquitetos não podiam controlar as operações e foram criadas de modo defensivo em torno dessa restrição (para ter um bom exemplo, veja Arquitetura Baseada em Espaços, no Capítulo 15).
Contudo, há alguns anos, diversas empresas começaram a experimentar novas formas de arquitetura que combinam muitas questões operacionais com a arquitetura. Por exemplo, nas arquiteturas mais antigas, como SOA baseado em ESB, a arquitetura era planejada para lidar com a escala elástica, complicando muito o processo. Basicamente, os arquitetos eram forçados a planejar de modo defensivo em torno dos limites introduzidos por causa da economia de custos das operações terceirizadas. Desse modo, eles criavam arquiteturas que podiam lidar com a escala, o desempenho, a elasticidade e muitas outras capacidades internamente. O efeito colateral desse design era uma arquitetura muito mais complexa.
Os criadores do estilo microsserviços da arquitetura perceberam que as operações lidam melhor com essas questões operacionais. Criando uma ligação entre arquitetura e operações, os arquitetos conseguem simplificar o design e contar com os profissionais de operações para coisas com as quais lidam melhor. Assim, perceber uma apropriação indevida dos recursos levou a uma complexidade imprevista, e os arquitetos e os profissionais de operações se juntaram para criar microsserviços, cujos detalhes são vistos no Capítulo 17.
Processo
Outro axioma é que a arquitetura de software é, em grande parte, ortogonal no processo de desenvolvimento de software; o modo como você cria o software (processo) tem pouco impacto na arquitetura dele (estrutura). Dessa maneira, embora o processo de desenvolvimento de software que uma equipe usa tenha impacto na estrutura do software (em especial nas práticas da engenharia), historicamente ela tem sido considerada como separada na maior parte. A maioria dos livros sobre arquitetura de software ignora o processo de desenvolvimento de
Página 38
software, fazendo suposições capciosas sobre coisas como previsibilidade. Contudo, o processo pelo qual as pessoas desenvolvem o software tem impacto em muitas facetas da arquitetura de software. Por exemplo, muitas pessoas nas últimas décadas adotaram metodologias de desenvolvimento ágil devido à natureza do software. Os arquitetos de projetos ágeis podem realizar o desenvolvimento iterativo e, portanto, um ciclo de feedback mais rápido para as decisões. Isso, por sua vez, permite que os arquitetos sejam mais agressivos sobre a experimentação e outros conhecimentos que contam com o feedback.
Como observa a citação anterior de Mark, toda arquitetura se torna iterativa; é só uma questão de tempo. Para esse fim, vamos pressupor por completo a linha de base das metodologias ágeis e destacar as exceções onde forem adequadas. Por exemplo, ainda é comum que muitas arquiteturas monolíticas usem processos mais antigos por causa da idade, da política ou de outros fatores atenuantes não relacionados ao software.
Um aspecto crítico da arquitetura em que as metodologias ágeis se destacam é a reestruturação.
Muitas vezes, as equipes acham que precisam migrar sua arquitetura de um padrão para outro.
Por exemplo, uma equipe iniciou com uma arquitetura monolítica porque era fácil e rápida de inicializar, mas agora ela precisa passar para uma arquitetura mais moderna. As metodologias ágeis suportam melhor essas mudanças do que os processos muito planejados devido ao feedback contínuo e ao encorajamento de técnicas como o Padrão de Estrangulamento (Strangler) e a alternância de recursos.
Dados
Uma grande porcentagem de desenvolvimento sério de aplicações inclui o armazenamento externo de dados, muitas vezes na forma de um banco de dados relacional (ou, cada vez mais, o NoSQL). Porém muitos livros sobre arquitetura de software incluem apenas um leve tratamento desse importante aspecto da arquitetura. Códigos e dados têm uma relação simbiótica: um não é útil sem o outro.
Os administradores de bancos de dados costumam trabalhar lado a lado com arquitetos para criar uma arquitetura de dados para sistemas complexos, analisando como as relações e a reutilização afetarão um portfólio de aplicações. Não entraremos nesse nível de especialização neste livro. Ao mesmo tempo, não deixaremos de ver a existência e a dependência do armazenamento externo. Em particular, quando falarmos sobre os aspectos operacionais da arquitetura e do quantum arquitetural (veja “Quanta Arquitetural e Granularidade”, no Capítulo 7), incluiremos questões externas importantes, como os bancos de dados.
Leis da Arquitetura de Software
Embora o escopo da arquitetura de software seja surpreendentemente amplo, existem elementos unificadores. Os autores aprenderam fundamentalmente a Primeira Lei da Arquitetura de Software sempre se encontrando com ela:
Tudo na arquitetura de software é uma concessão.
— Primeira Lei da Arquitetura de Software Nada existe em um espectro bonito e claro para os arquitetos de software. Toda decisão deve levar muitos fatores opostos em conta.
Página 39
Se um arquiteto pensa que descobriu algo que não é uma concessão, muito provavelmente ele ainda não a identificou.
— Corolário 1 Definimos arquitetura de software em termos além da plataforma estrutural, incorporando princípios, características etc. A arquitetura é mais ampla do que apenas a combinação dos elementos estruturais, refletida em nossa Segunda Lei da Arquitetura de Software:
O porquê é mais importante do que o como.
— Segunda Lei da Arquitetura de Software Os autores descobriram a importância dessa perspectiva quando tentaram manter os resultados dos exercícios feitos pelos alunos durante um workshop quando criaram soluções de arquitetura. Como os exercícios eram cronometrados, os únicos artefatos que mantivemos eram os diagramas que representam a topologia, ou seja, capturamos como eles resolviam o problema, mas não por que a equipe fez determinadas escolhas. Um arquiteto pode ver um sistema existente que ele não conhece e determinar como a estrutura da arquitetura funciona, mas fará um esforço para explicar por que certas escolhas foram feitas e não outras.
Neste livro destacamos por que os arquitetos tomam certas decisões junto a concessões.
Também destacamos boas técnicas para capturar importantes decisões em “Registros de Decisão da Arquitetura”, no Capítulo 19.
Página 40
Parte I. Fundamentos
PARTE I Fundamentos Para entender os trade-offs importantes na arquitetura, os desenvolvedores devem compreender alguns conceitos básicos e componentes relacionados à terminologia, à modularidade, ao acoplamento e à conascência.
Página 41
2. Pensamento Arquitetônico
CAPÍTULO 2 Pensamento Arquitetônico Um arquiteto pensa de modo diferente de um desenvolvedor, assim como um meteorologista vê as nuvens de modo diferente de um artista. Isso se chama pensamento arquitetônico. Infelizmente, muitos arquitetos acreditam que o pensamento arquitetônico se resume simplesmente a “pensar na arquitetura”.
Pensamento arquitetônico é muito mais do que isso. É ver as coisas com os olhos da arquitetura ou do ponto de vista arquitetural. Pensar como arquiteto envolve quatro aspectos principais. Primeiro, é entender a diferença entre arquitetura e design, saber como colaborar com as equipes de desenvolvimento para a arquitetura funcionar. Segundo, é ter uma grande variedade de conhecimento técnico e ainda manter certo nível de profundidade técnica, permitindo que o arquiteto veja soluções e possibilidades que os outros não veem. Terceiro, é entender, analisar e reconciliar os trade-offs entre várias soluções e tecnologias. Por fim, é entender a importância das motivações comerciais e como elas se traduzem em preocupações arquitetônicas.
Neste capítulo, exploraremos esses quatro aspectos de pensar como arquiteto e veremos as coisas com os olhos da arquitetura.
Arquitetura Versus Design
A diferença entre arquitetura e design muitas vezes é confusa. Onde a arquitetura termina e começa o design? Quais responsabilidades um arquiteto tem versus um desenvolvedor? Pensar como arquiteto é saber a diferença entre arquitetura e design, ver como os dois se integram para formar soluções para problemas comerciais e técnicos.
Veja a Figura 2-1, que mostra as responsabilidades tradicionais que um arquiteto tem, em comparação com as de um desenvolvedor. Como
Página 42
mostrado no diagrama, um arquiteto é responsável por coisas como analisar os requisitos comerciais para extrair e definir as características da arquitetura (os “atributos”), selecionar quais padrões e estilos da arquitetura se encaixariam no domínio do problema e criar componentes (blocos de construção do sistema). Então os artefatos criados a partir dessas atividades são passados para a equipe de desenvolvimento, responsável por criar diagramas de classes para cada componente, criar telas de interface do usuário e desenvolver e testar o código-fonte.
Figura 2-1: Visão tradicional da arquitetura versus design Existem vários problemas com o modelo de responsabilidade tradicional mostrado na Figura 2-1. Na verdade, essa ilustração mostra exatamente por que a arquitetura raramente funciona. Especificamente, é a seta unidimensional passando por barreiras virtuais e físicas, separando a arquitetura do desenvolvedor, que causa todos os problemas associados à arquitetura. As decisões que o arquiteto toma às vezes não funcionam para as equipes de desenvolvimento, e as decisões que as equipes de desenvolvimento tomam, e que mudam a arquitetura, raramente voltam para o arquiteto. Nesse modelo, o arquiteto está desconectado das equipes de desenvolvimento e, como tal, a arquitetura raramente fornece o que foi criada para fazer originalmente.

Página 43
Para fazer a arquitetura funcionar, as barreiras físicas e virtuais que existem entre arquitetos e desenvolvedores devem ser quebradas, assim formando uma relação bidirecional forte entre arquitetos e equipes de desenvolvimento. O arquiteto e o desenvolvedor devem estar na mesma equipe virtual para isso dar certo, como representado na Figura 2-2. Não só esse modelo facilita uma forte comunicação bidirecional entre a arquitetura e o desenvolvimento, como também permite que o arquiteto forneça mentoria e treinamento para os desenvolvedores na equipe.
Figura 2-2. Fazendo a arquitetura funcionar com a colaboração Diferentemente das antigas abordagens em cascata para a arquitetura de software estática e rígida, a arquitetura dos sistemas atuais muda e evolui a cada iteração ou fase de um projeto. Uma estreita colaboração entre o arquiteto e a equipe de desenvolvimento é essencial para o sucesso de qualquer projeto de software. Assim, em que momento a arquitetura termina e o design se inicia? Isso não acontece. Ambos fazem parte do ciclo de vida dentro de um projeto de software e sempre devem ficar em sincronia para terem êxito.
Amplitude Técnica

Página 44
O escopo dos detalhes técnicos difere entre desenvolvedores e arquitetos.
Diferentemente de um desenvolvedor, que deve ter bastante profundidade técnica para realizar seu trabalho, um arquiteto de software deve ter uma grande amplitude técnica para pensar como arquiteto e ver as coisas de um ponto de vista da arquitetura. Isso é mostrado pela pirâmide de conhecimento na Figura 2-3, que encapsula todo o conhecimento técnico no mundo. Assim, o tipo de informação que um técnico deve valorizar difere nos estágios da carreira.
Figura 2-3. Pirâmide representando todo o conhecimento Como mostrado na Figura 2-3, qualquer indivíduo pode particionar seu conhecimento em três seções: o que você sabe, o que você sabe que não sabe e o que você não sabe que não sabe.
O que você sabe inclui as tecnologias, os frameworks, as linguagens e as ferramentas que um técnico usa diariamente para realizar seu trabalho, como conhecer a linguagem Java como um programador Java. O que você

Página 45
sabe que não sabe inclui o que um técnico sabe um pouco e ouviu falar, mas tem pouca ou nenhuma especialização nela. A maioria dos técnicos ouviu falar sobre Clojure e sabe que é uma linguagem de programação baseada no Lisp, mas eles não conseguem codificar na linguagem. O que você não sabe que não sabe é a maior parte da pirâmide de conhecimento e inclui as inúmeras tecnologias, ferramentas, estruturas e linguagens que seriam a solução perfeita para um problema que o técnico tenta resolver, mas ele nem sabe que essas coisas existem.
O início de carreira de um desenvolvedor foca expandir o topo da pirâmide, para acumular experiência e especialização. É o foco ideal no início, pois os desenvolvedores precisam ter mais perspectiva, conhecimento prático e experiência. Expandir o topo acaba expandindo a seção do meio; conforme os desenvolvedores encontram mais tecnologias e artefatos afins, isso aumenta seu estoque do que você sabe que não sabe.
Na Figura 2-4, expandir o topo da pirâmide é benéfico porque a especialização é valorizada. Contudo, o que você sabe também é a coisa que deve manter; nada é estático no mundo dos softwares. Se um desenvolvedor se torna especialista em Ruby on Rails, essa especialização não durará se ele ignorar o Ruby on Rails por um ou dois anos. As coisas no topo da pirâmide requerem investimento de tempo para que a especialização persista. Por fim, o tamanho do topo da pirâmide de um indivíduo é sua profundidade técnica.
Página 46
Figura 2-4. Os desenvolvedores devem fixar a especialização para mantê-la Porém, a natureza do conhecimento muda quando os desenvolvedores fazem a transição para a função de arquiteto. Uma grande parte do valor de um arquiteto é o amplo conhecimento da tecnologia e como usá-la para resolver problemas específicos. Por exemplo, como arquiteto, é mais vantajoso saber que existem cinco soluções para certo problema do que ter especialização em apenas uma. As partes mais importantes da pirâmide para os arquitetos são o topo e o meio; a amplitude técnica de um arquiteto é representada pela porção da parte central que invade a parte inferior, como mostrado na Figura 2-5.

Página 47
Figura 2-5. O que alguém sabe é a profundidade técnica e o quanto alguém sabe é a amplitude técnica Como arquiteto, amplitude é mais importante do que profundidade. Como os arquitetos devem tomar decisões que combinam capacidades com restrições técnicas, um grande conhecimento de variadas soluções é valioso.
Assim, para um arquiteto, o curso de ação inteligente seria sacrificar uma especialização duramente conquistada e usar esse tempo para ampliar seu portfólio, como mostrado na Figura 2-6. Como visto no diagrama, algumas áreas de especialização permanecerão, provavelmente nas áreas da tecnologia particularmente agradáveis; já outras atrofiam de forma útil.

Página 48
Figura 2-6. Amplitude melhorada e aprofundamento reduzido para a função de arquiteto Nossa pirâmide do conhecimento mostra como a função do arquiteto é fundamentalmente diferente se comparada com um desenvolvedor. Os desenvolvedores passam suas carreiras inteiras aprimorando a especialização, e fazer a transição para a função de arquiteto significa mudar essa perspectiva, o que muitos indivíduos acham difícil. Por sua vez, isso leva a duas disfunções comuns: primeiro, um arquiteto tenta manter a especialização de várias áreas, sem sucesso em nenhuma delas e se martirizando no processo. Segundo, isso se manifesta como uma especialização obsoleta, a sensação errada de que suas informações desatualizadas ainda são inovadoras. Vemos isso com frequência em grandes empresas em que os desenvolvedores que a fundaram e passaram para as funções de liderança ainda tomam decisões de tecnologia usando critérios antigos (veja “Antipadrão do Homem das Cavernas Congelado”).

Página 49
Os arquitetos devem focar a amplitude técnica para que tenham uma aljava maior a partir da qual tiram suas flechas. Os desenvolvedores que fazem a transição para a função de arquiteto talvez precisem mudar o modo como veem a aquisição de conhecimento. Equilibrar seu portfólio de conhecimento em relação à profundidade versus amplitude é algo que todo desenvolvedor deve considerar durante sua carreira.
Antipadrão do Homem das Cavernas Congelado Um antipadrão comportamental comumente observado na natureza, o Antipadrão do Homem das Cavernas Congelado (ou Frozen Caveman Anti-Pattern), descreve um arquiteto que sempre regressa à sua preocupação irracional de cada arquitetura. Por exemplo, um dos colegas de Neal trabalhou em um sistema que apresentava uma arquitetura centralizada.
Todavia, sempre que eles entregavam o design aos arquitetos clientes, a pergunta constante era: “Mas e se perdemos a Itália?” Vários anos antes, um problema de comunicação incomum tinha impedido que as matrizes se comunicassem com suas lojas na Itália, causando uma grande inconveniência. Embora as chances de uma nova ocorrência fossem extremamente pequenas, os arquitetos ficaram obcecados com essa característica arquitetural em particular.
Em geral, esse antipadrão se manifesta nos arquitetos que se deram mal no passado com uma decisão ruim ou ocorrência inesperada. Embora seja importante uma avaliação dos riscos, ela deve ser também realista. Entender a diferença entre um risco técnico genuíno e percebido faz parte do processo de aprendizado contínuo para os arquitetos. Pensar como um arquiteto requer superar essas ideias e experiências do “homem da caverna congelado”, vendo outras soluções e fazendo perguntas mais relevantes.
Analisando os Trade-offs
Pensar como arquiteto é ver os trade-offs em toda solução, técnica ou outra, e analisá-las para determinar qual é a melhor solução. Citando Mark (um de seus autores):
Arquitetura é o que você não consegue pesquisar com o Google.
Tudo na arquitetura é um trade-off, sendo por isso que a famosa resposta para toda pergunta de arquitetura no universo é “depende”. Embora muitas pessoas fiquem cada vez mais chateadas com a resposta, infelizmente é a verdade. Você não pode pesquisar com o Google a resposta para saber se o REST ou a mensageria seria melhor, se os microsserviços são o estilo certo de arquitetura, pois depende. Depende do ambiente de
Página 50

Página 51


Página 52
Página 53
Entendendo as Motivações Comerciais
Página 54
Pensar como arquiteto é entender as motivações comerciais requeridas para o sucesso do sistema e a tradução desses requisitos em características da arquitetura (como escalabilidade, desempenho e disponibilidade). É uma tarefa desafiadora que requer que o arquiteto tenha certo nível de conhecimento do domínio comercial e relações saudáveis e de colaboração com os principais acionistas da empresa. Dedicamos vários capítulos no livro sobre esse tópico específico. No Capítulo 4, definimos diversas características da arquitetura. No Capítulo 5, descrevemos modos de identificar e qualificar as características da arquitetura. E no Capítulo 6, descrevemos como medir cada uma dessas características para assegurar que as necessidades comerciais do sistema sejam atendidas.
Equilibrando Arquitetura e Codificação
Uma das tarefas difíceis que um arquiteto enfrenta é como equilibrar codificação e arquitetura de software. Acreditamos sinceramente que todo arquiteto deve codificar e conseguir manter certo nível de profundidade técnica (veja “Amplitude Técnica”). Embora possa parecer uma tarefa fácil, por vezes é bem difícil de realizar.
A primeira dica para buscar um equilíbrio entre codificação e ser um arquiteto de software é evitar a armadilha do gargalo. Ela ocorre quando o arquiteto assume a propriedade do código no caminho crítico de um projeto (em geral o código do framework subjacente) e se torna um gargalo para a equipe. Isso acontece porque o arquiteto não é desenvolvedor em tempo integral, portanto deve equilibrar entre a função de desenvolvedor (escrever e testar o código-fonte) e de arquiteto (desenhar diagramas, participar de reuniões e, bem, participar de mais reuniões).
Um modo de evitar a armadilha do gargalo como um arquiteto de software eficiente é delegar o caminho crítico e o código do framework para outras pessoas na equipe de desenvolvimento, então concentrar-se em codificar uma parte da funcionalidade de negócios (um serviço ou uma tela) de uma a três iterações à frente. Fazer isso resulta em três coisas positivas.
Primeiro, o arquiteto ganha experiência prática ao escrever um código para produção, não sendo mais um gargalo na equipe. Segundo, o caminho crítico e o código do framework são distribuídos para a equipe de desenvolvimento (onde ela pertence), dando-lhes a propriedade e uma
Página 55
melhor compreensão das partes mais difíceis do sistema. Terceiro, e talvez o mais importante, o arquiteto escreve o mesmo código-fonte relacionado ao negócio que a equipe de desenvolvimento, portanto consegue identificar melhor com a equipe em termos do problema que eles podem ter nos processos, nos procedimentos e no ambiente de desenvolvimento.
Contudo, suponha que o arquiteto não consiga desenvolver o código com a equipe de desenvolvimento. Como um arquiteto de software ainda continua sendo útil e mantém algum nível de profundidade técnica?
Existem quatro modos básicos para um arquiteto ainda continuar útil no trabalho sem ter que “praticar a codificação em casa” (embora recomendemos praticá-la em casa também).
O primeiro modo é frequentemente fazer prova de conceitos, ou POCs.
Essa prática não só requer que o arquiteto escreva o código-fonte, como também ajuda a validar uma decisão de arquitetura levando em conta os detalhes da implementação. Por exemplo, se um arquiteto fica travado tentando tomar uma decisão entre duas soluções de cache, uma maneira eficiente de ajudar a tomar essa decisão é desenvolver um exemplo funcional em cada produto de cache e comparar os resultados. Isso permite que ele veja em primeira mão os detalhes da implementação e quanto esforço é requerido para desenvolver uma solução completa. Também permite que o arquiteto compare melhor as características da arquitetura, como escalabilidade, desempenho ou tolerância geral a falhas das diferentes soluções de cache.
Nosso conselho ao fazer o trabalho da prova de conceito é que, sempre que possível, o arquiteto deve escrever o melhor código para produção que puder. Recomendamos essa prática por dois motivos. Primeiro, com muita frequência, um código da prova de conceito descartável vai para o repositório de código-fonte e se torna a arquitetura de referência ou exemplo orientador para os outros seguirem. A última coisa que um arquiteto desejaria é que seu código descartável e incorreto fosse uma representação de seu trabalho típico. O segundo motivo é que, escrevendo um código da prova de conceito com qualidade de produção, o arquiteto pratica escrever um código de qualidade e bem estruturado, em vez de desenvolver continuamente práticas de codificação ruins.
Outro jeito de um arquiteto conseguir se manter útil é resolver algumas histórias com déficits técnicos ou histórias de arquitetura, liberando a
Página 56
equipe de desenvolvimento para trabalhar nas histórias de usuário funcionalmente críticas. Essas histórias costumam ter baixa prioridade, portanto se um arquiteto não tiver a chance de concluir uma história de déficit técnico ou de arquitetura dentro de certa iteração, não é o fim do mundo e geralmente não afeta o sucesso da iteração.
Do mesmo modo, trabalhar nas correções de erros dentro de uma iteração é outro modo de se manter útil codificando enquanto ajuda também a equipe de desenvolvimento. Embora definitivamente não glamorosa, essa técnica permite ao arquiteto identificar onde os problemas e os pontos fracos podem estar na base de código e possivelmente na arquitetura.
Aproveitando a automação criando ferramentas simples de linha de comando e analisadores para auxiliar a equipe de desenvolvimento em suas tarefas diárias é outra ótima maneira de manter as habilidades de codificação úteis enquanto torna a equipe mais eficiente. Procure as tarefas repetitivas que a equipe de desenvolvimento realiza e automatize o processo. A equipe ficará grata pela automação. Alguns exemplos são validadores de código-fonte automatizados para ajudar a verificar padrões de codificação específicos não encontrados em outros testes de lint, listas de verificação automatizadas e tarefas repetitivas de refatoração de código manual.
A automação pode vir na forma de análise arquitetural e funções de adequação para assegurar a viabilidade e a conformidade da arquitetura.
Por exemplo, um arquiteto pode escrever código Java em ArchUnit na plataforma Java para automatizar a conformidade arquitetural ou escrever funções de adequação personalizadas para assegurar a conformidade arquitetural, conseguindo uma experiência prática. Falamos sobre essas técnicas no Capítulo 6.
Uma técnica final para permanecer útil como arquiteto é fazer revisões de código frequentes. Embora o arquiteto não esteja escrevendo código de fato, pelo menos está envolvido no código-fonte. E mais, fazer revisões de código tem o benefício extra de conseguir assegurar a conformidade com o arquiteto e buscar oportunidades de mentoria e treinamento na equipe.
Página 57
3. Modularidade
Definição
Página 58
linguagens modernas, os desenvolvedores podem definir o comportamento em funções/métodos, classes ou pacotes/namespaces, cada um com visibilidade e regras de escopo diferentes. Outras linguagens complicam ainda mais adicionando construções de programação como o protocolo de metaobjeto para fornecer aos desenvolvedores ainda mais mecanismos de extensão.
Os arquitetos devem estar cientes de como os desenvolvedores empacotam as coisas porque isso resulta em importantes implicações na arquitetura. Por exemplo, se vários pacotes são bem acoplados, reutilizar o código de um deles para o trabalho relacionado fica mais difícil.
Reutilização Modular Antes das Classes Os desenvolvedores anteriores às linguagens orientadas a objetos podem se confundir por que existem muitos esquemas de separação diferentes. Grande parte do motivo tem relação com a compatibilidade anterior, não do código, mas de como os desenvolvedores pensam sobre as coisas. Em março de 1968, Edsger Dijkstra publicou uma carta na revista Communications of the ACM intitulada “Go To Statement Considered Harmful”. Ele criticava o uso comum da declaração GOTO normal nas linguagens de programação na época, permitindo um salto não linear dentro do código, dificultando o raciocínio e a depuração.
Esse documento ajudou a iniciar a era das linguagens de programação estruturadas, exemplificadas por Pascal e C, o que encorajou um pensamento mais profundo sobre como as coisas se encaixam. Rapidamente, os desenvolvedores perceberam que a maioria das linguagens não tinha um bom modo de agrupar coisas afins de maneira lógica. Assim, a era curta das linguagens modulares nasceu, como Modula (a próxima linguagem de Niklaus Wirth, criador do Pascal) e Ada. Essas linguagens tinham a construção de programação como um módulo, muito parecido com o que pensamos sobre pacotes ou namespaces hoje (mas sem as classes).
A era da programação modular durou pouco. As linguagens orientadas a objeto se tornaram populares porque ofereciam novos modos de encapsular e reutilizar o código. E mais, os designers da linguagem perceberam a utilidade dos módulos, retendo-os na forma de pacotes, namespaces etc. Existem muitos recursos de compatibilidade estranhos nas linguagens para dar suporte a esses diferentes paradigmas. Por exemplo, o Java suporta paradigmas modular (via pacotes e inicialização no nível do pacote usando inicializadores estáticos), orientado a objetos e funcional, com cada estilo de programação tendo regras e particularidades de escopo próprias.
Para as discussões sobre arquitetura, usamos modularidade como um termo geral para indicar um grupo de código relacionado: classes, funções ou qualquer agrupamento. Isso não implica uma separação física, apenas lógica; a diferença por vezes é importante. Por exemplo, amontoar um grande número de classes em um aplicação monolítica pode fazer sentido de um ponto de vista da conveniência. Contudo, na hora de reestruturar a arquitetura, o acoplamento encorajado por um particionamento flexível se torna um impedimento para dividir o monolítico em partes. Assim, é bom conversar sobre modularidade como um conceito diferente da separação física forçada ou implicada por determinada plataforma.
Vale a pena notar o conceito geral de namespace, separado da implementação técnica na plataforma .NET. Os desenvolvedores costumam precisar de nomes totalmente qualificados e certeiros para os ativos de software para separar os diferentes ativos (componentes, classes etc.) entre si. O exemplo mais óbvio que as pessoas usam todos os dias é a internet: identificadores únicos e globais ligados a endereços IP. A maioria das linguagens tem algum mecanismo de modularidade que duplica como um namespace organiza as coisas: variáveis, funções e/ou métodos. Por vezes, a estrutura do módulo é refletida fisicamente. Por exemplo, o Java requer que a estrutura do pacote reflita a estrutura do diretório dos arquivos de classe físicos.
Página 59
Medindo a Modularidade
Coesão
Página 60
Página 61


Página 62
Acoplamento

Página 63
Abstração, Instabilidade e Distância da Sequência Principal

Página 64
Na equação, ce representa o acoplamento eferente (ou de saída) e ca representa o acoplamento aferente (ou de entrada).
A métrica instabilidade determina a volatilidade de uma base de código. Uma base que mostra altos graus de instabilidade se rompe com mais facilidade quando alterada por causa do alto acoplamento. Por exemplo, se uma classe chama muitas outras classes para delegar trabalho, a classe que chama mostra alta susceptibilidade de ruptura se um ou mais métodos chamados mudam.
Distância da Sequência Principal
Uma das métricas mais globais que os arquitetos têm para a estrutura da arquitetura é a distância da sequência principal, uma métrica derivada com base na instabilidade e na abstração, mostrada na Equação 3-5.
Na equação, A = abstração e I = instabilidade.
Observe que abstração e instabilidade são frações cujos resultados sempre ficarão entre 0 e 1 (exceto nos casos extremos de abstração que não seriam práticos). Assim, ao fazer o gráfico da relação, vemos a Figura 3-2.


Página 65
Figura 3-2. A sequência principal define a relação ideal entre abstração e instabilidade A métrica distância imagina uma relação ideal entre abstração e instabilidade; as classes que ficam perto da linha idealizada mostram uma combinação saudável dessas duas preocupações concorrentes. Por exemplo, fazer o gráfico de certa classe permite que os desenvolvedores calculem a métrica distância da sequência principal, mostrada na Figura 3-3.

Página 66
Figura 3-3. Distância normalizada da sequência principal de certa classe Na Figura 3-3, os desenvolvedores criam o gráfico da classe candidata, então medem a distância a partir da linha idealizada. Quanto mais perto da linha, mais equilibrada é a classe.
As classes que ficam muito longe, no canto superior direito, entram no que os arquitetos chamam de zona de inutilidade: o código que é abstrato demais fica difícil de usar. Por outro lado, o código que fica no canto inferior esquerdo entra na zona de sofrimento: o código com implementação demais e sem abstração suficiente se torna frágil e difícil de manter, tal como mostrado na Figura 3-4.

Página 67
Figura 3-4. Zonas de inutilidade e de sofrimento Existem ferramentas em muitas plataformas que fornecem essas medidas e que ajudam os arquitetos ao analisarem as bases de código por causa da falta de familiaridade, migração ou avaliação do déficit técnico.
Limites das Métricas Embora o setor tenha poucas métricas no nível do código que forneçam informações valiosas para as bases de código, nossas ferramentas são extremamente diretas em comparação com as ferramentas de análise de outras disciplinas da engenharia. Até as métricas derivadas diretamente da estrutura de código requerem interpretação. Por exemplo, a complexidade ciclomática (veja “Complexidade Ciclomática”) mede a complexidade nas bases de código, mas não consegue diferenciar a complexidade essencial (porque o problema subjacente é complexo) e a complexidade acidental (o código é mais complexo do que deveria ser). Praticamente todas as métricas no nível do código requerem interpretação, mas ainda é útil estabelecer linhas de base para métricas críticas, como a complexidade ciclomática, para que os arquitetos possam avaliar qual tipo é exibido. Explicamos como configurar tais testes em “Governança e Funções de Adequação”, Capítulo 6.
Observe que o livro de Edward Yourdon e Larry Constantine antes mencionado (Structured Design: Fundamentals of a Discipline of Computer Program and Systems Design) é anterior à popularidade das linguagens orientadas a objetos, focando as construções de programação estruturada, como funções (não métodos). Também definiu outros tipos de acoplamento que não cobrimos aqui porque foram superados pela conascência.

Página 68
Conascência
Página 69
Múltiplos componentes devem concordar com certo algoritmo.
Um caso comum para esse tipo de conascência ocorre quando um desenvolvedor define um algoritmo de hash de segurança que deve ser executado no servidor e no cliente, produzindo resultados idênticos para autenticar o usuário. É óbvio que isso representa uma alta forma de acoplamento; se um algoritmo mudar algum detalhe, o handshake não funcionará mais.
Conascência dinâmica O outro tipo que Page-Jones definiu foi a conascência dinâmica, que analisa as chamadas durante a execução. Veja a seguir uma descrição dos diferentes tipos de conascência dinâmica:
Conascência de Execução (CoE) A ordem da execução dos múltiplos componentes é importante.
Considere este código:
Ele não funcionará corretamente porque certas propriedades devem ser definidas na ordem.
Conascência de Tempo (CoT) O tempo de execução dos múltiplos componentes é importante.
O caso comum desse tipo de conascência é uma condição race causada por dois threads em execução ao mesmo tempo, afetando o resultado da operação em conjunto.
Conascência de Valores (CoV) Ocorre quando diversos valores se relacionam e devem mudar juntos.
Considere o caso em que um desenvolvedor definiu um retângulo como quatro pontos, representando os vértices. Para manter a integridade da estrutura de dados, o desenvolvedor não pode mudar aleatoriamente um dos pontos sem considerar o impacto nos outros.
O caso mais comum e problemático envolve transações, em especial nos sistemas distribuídos. Quando um arquiteto planeja um sistema com bancos de dados separados, embora precise atualizar um único valor em todos os bancos de dados, todos os valores devem mudar juntos ou não mudar.
Conascência de Identidade (CoI) Ocorre quando vários componentes devem referenciar a mesma entidade.
O exemplo comum desse tipo de conascência envolve dois componentes independentes que devem compartilhar e atualizar uma estrutura de dados comum, como uma fila distribuída.
Será mais difícil para os arquitetos determinar a conascência dinâmica porque não temos ferramentas para analisar as chamadas durante a execução de modo tão eficiente como quando

Página 70
analisamos o grafo de chamadas.
Propriedades da conascência A conascência é uma ferramenta de análise para arquitetos e desenvolvedores, e algumas propriedades dela ajudam os desenvolvedores a usarem-na com sabedoria. Veja a seguir uma descrição de cada propriedade da conascência:
Força Os arquitetos determinam a força da conascência pela facilidade com a qual um desenvolvedor consegue refatorar esse tipo de acoplamento; diferentes tipos de conascência são claramente mais desejáveis, como mostrado na Figura 3-5. Arquitetos e desenvolvedores podem melhorar as características do acoplamento de sua base de código refatorando os melhores tipos de conascência.
Os arquitetos devem preferir a conascência estática à dinâmica porque podem determiná-la com uma simples análise do código-fonte, e ferramentas modernas tornam fácil melhorar a conascência estática. Por exemplo, considere o caso da conascência de significado, em que os desenvolvedores podem melhorar refatorando na conascência de nome criando uma constante nomeada, em vez de um valor mágico.
Figura 3-5. A força na conascência fornece um bom guia de refatoração Localização A localização da conascência mede a proximidade dos módulos entre si na base de código. O código proximal (no mesmo módulo), em geral, tem mais formas e formas mais altas de conascência do que um código separado (em módulos ou bases de código

Página 71
Unificando as Métricas de Acoplamento e Conascência
separadas), ou seja, as formas da conascência que indicam um baixo acoplamento quando separadas são boas quando mais próximas. Por exemplo, se duas classes no mesmo componente tiverem conascência de significado, isso será menos perigoso para a base do código do que se dois componentes tiverem a mesma forma de conascência.
Os desenvolvedores devem considerar a força e a localização juntas. As formas mais fortes de conascência encontradas no mesmo módulo representam menos “cheiro de código ruim” do que a mesma conascência afastada.
Grau O grau da conascência se relaciona ao tamanho de seu impacto. Tem impacto em poucas ou muitas classes? Graus menores de conascência prejudicam menos as bases de código, isto é, ter uma alta conascência dinâmica não é terrível se você tem apenas alguns módulos. Contudo, as bases de código tendem a aumentar, tornando maior um pequeno problema.
Page-Jones apresenta três diretrizes para usar a conascência e melhorar a modularidade dos sistemas:
- 1. Minimize a conascência em geral dividindo o sistema em elementos encapsulados
- 2. Minimize qualquer conascência restante que cruze os limites do encapsulamento
- 3. Maximize a conascência dentro dos limites do encapsulamento
Jim Weirich, famoso inovador da arquitetura de software, popularizou de novo o conceito de conascência e dá dois ótimos conselhos:
Regra do Grau: converta as formas fortes de conascência em formas mais fracas Regra da Localização: conforme a distância entre os elementos de software aumenta, use formas mais fracas de conascência Unificando as Métricas de Acoplamento e Conascência Até então, vimos acoplamento e conascência, medidas de diferentes épocas e com objetivos variados. Porém, do ponto de vista do arquiteto, essas duas visões se sobrepõem. O que Page-Jones identifica como conascência estática representa graus de acoplamento de entrada ou saída. A programação estruturada apenas se interessa pela entrada ou pela saída, já a conascência vê como as coisas são acopladas.
Figura 3-6. Unificando acoplamento e conascência

Página 72
Para ajudar a visualizar a sobreposição nos conceitos, considere a Figura 3-6. Os conceitos de acoplamento da programação estruturada aparecem à esquerda, já as características da conascência ficam à direita. O que a programação estruturada chamou de acoplamento de dados (chamadas do método), a conascência aconselha sobre como esse acoplamento deve se manifestar. A programação estruturada realmente não lidou com as áreas cobertas pela conascência dinâmica; encapsulamos esse conceito rapidamente em “Quanta Arquitetural e Granularidade”.
Problemas com a conascência dos anos 1990 Existem vários problemas para os arquitetos ao aplicarem essas métricas úteis para analisar e projetar sistemas. Primeiro, essas medidas veem os detalhes em um nível baixo do código, focando a qualidade e a limpeza do código, em vez da estrutura necessariamente arquitetural.
Os arquitetos tendem a cuidar mais de como os módulos estão acoplados, não do grau de acoplamento. Por exemplo, um arquiteto cuida da comunicação síncrona versus assíncrona e não se importa muito com como ela é implementada.
O segundo problema com a conascência está no fato de que ela realmente não lida com uma decisão fundamental que muitos arquitetos modernos devem tomar: comunicação síncrona ou assíncrona nas arquiteturas distribuídas como microsserviços? Fazendo referência à Primeira Lei da Arquitetura de Software, tudo é um trade-off. Após discutirmos sobre o escopo das características da arquitetura no Capítulo 7, apresentaremos novos modos de pensar sobre a conascência moderna.
De Módulos a Componentes
Usamos o termo módulo como um nome genérico para um grupo de código afim. Contudo, a maioria das plataformas tem suporte para alguma forma de componente, um dos principais blocos de construção para os arquitetos de software. O conceito e a análise correspondente da separação lógica ou física existem desde o início da ciência da computação. Contudo, com todo texto e raciocínio sobre componentes e separação, desenvolvedores e arquitetos ainda lutam para conseguirem bons resultados.
Examinaremos como derivar os componentes dos domínios de problema no Capítulo 8, mas devemos primeiro debater sobre outro aspecto fundamental da arquitetura de software: as características da arquitetura e seu escopo.
Página 73
4. Definição das Características da Arquitetura
CAPÍTULO 4 Definição das Características da Arquitetura Uma empresa decide resolver certo problema com um software, então monta uma lista de requisitos para o sistema. Existem muitas técnicas para o exercício de reunir os requisitos, em geral definidas pelo processo de desenvolvimento do software usado pela equipe. Mas o arquiteto deve considerar muitos outros fatores ao planejar uma solução de software, como mostrado na Figura 4-1.
Figura 4-1. Uma solução de software consiste nos requisitos do domínio e nas características da arquitetura Os arquitetos podem colaborar definindo os requisitos do domínio ou do negócio, mas uma responsabilidade principal envolve definir, descobrir e analisar todas as coisas que o software deve fazer que não estão diretamente relacionadas à funcionalidade do domínio: as características da arquitetura.
O que diferencia a arquitetura de software da codificação e do design? Muitas coisas, inclusive a função que os arquitetos têm ao definir as características da arquitetura, os aspectos importantes do sistema independentes do domínio do problema. Muitas organizações descrevem esses recursos do software com vários termos, inclusive requisitos não funcionais, mas não gostamos dele porque é depreciativo. Os arquitetos criaram o termo para diferenciar as características da arquitetura dos requisitos funcionais, mas nomear algo como não funcional tem um impacto negativo do ponto de vista da linguagem: como as equipes são convencidas a prestar muita atenção em algo “não funcional”? Outro termo popular é atributos da qualidade, que não gostamos porque implica uma avaliação da qualidade posterior, em vez do design.
Preferimos características da arquitetura porque descreve as preocupações críticas com o sucesso da arquitetura, portanto o sistema inteiro, sem descontar a importância.
Uma característica da arquitetura atende a três critérios:
Especifica uma consideração de design fora do domínio Influencia algum aspecto estrutural de design É essencial ou importante para o sucesso da aplicação Essas partes interligadas de nossa definição são mostradas na Figura 4-2.

Página 74
Figura 4-2. Os recursos diferenciais das características da arquitetura A definição na Figura 4-2 consiste nos três componentes listados, além de alguns modificadores:
Especifica uma consideração de design fora do domínio Ao planejar uma aplicação, os requisitos especificam o que ele deve fazer; as características da arquitetura especificam os critérios operacionais e de design para o sucesso, como implementar os requisitos e por que certas escolhas foram feitas. Por exemplo, uma característica comum e importante da arquitetura especifica certo nível de desempenho para a aplicação, que normalmente não aparece em um documento de requisitos. Ainda mais pertinente: nenhum documento de requisito determina “impedir o déficit técnico”, mas é uma consideração de design comum para arquitetos e desenvolvedores. Explicamos bem essa distinção entre as características explícita e implícita em “Extraindo as Características da Arquitetura das Preocupações do Domínio”, Capítulo 5.
Influencia um aspecto estrutural do design A principal razão para os arquitetos tentarem descrever as características da arquitetura nos projetos envolve as considerações do design: a característica da arquitetura requer uma consideração estrutural especial para ter sucesso? Por exemplo, segurança é uma preocupação real em todo projeto e todos os sistemas devem seguir uma linha de precauções durante o design e a codificação. Contudo, chega ao nível da característica da arquitetura quando o arquiteto precisa projetar algo especial. Veja dois casos relacionados ao pagamento em um sistema de exemplo:
Processador de pagamentos terceirizado Se um ponto de integração lida com os detalhes do pagamento, então a arquitetura não deve requerer considerações estruturais especiais. O design deve incorporar

Página 75
uma limpeza padrão de segurança, como criptografia e hash, mas não requerer uma estrutura especial.
Processamento de pagamentos na aplicação Se a aplicação sendo projetado deve lidar com o processamento de pagamentos, talvez a arquitetura planeje um módulo, um componente ou um serviço específico para essa finalidade, isolando estruturalmente as questões críticas da segurança.
Agora, a característica da arquitetura tem um impacto na arquitetura e no design.
Claro, até esses dois critérios não são suficientes em muitos casos para determinar isso:
incidentes de segurança anteriores, a natureza da integração com terceiros e muitos outros critérios podem estar presentes nessa decisão. E mais, mostra algumas considerações que os arquitetos devem fazer ao determinar como projetar certas capacidades.
Essencial ou importante para o sucesso da aplicação As aplicações podem dar suporte a uma quantidade enorme de características da arquitetura… mas não deveriam. O suporte de cada característica da arquitetura adiciona complexidade ao design. Assim, um trabalho crítico dos arquitetos é escolher menos características da arquitetura, ao invés do máximo possível.
Subdividimos mais as características da arquitetura em implícitas versus explícitas. As implícitas raramente aparecem nos requisitos, embora sejam necessárias para o sucesso do projeto. Por exemplo, disponibilidade, confiabilidade e segurança embasam todas as aplicações, embora raramente sejam especificadas nos documentos do design. Os arquitetos devem usar seu conhecimento do domínio do problema para revelar essas características da arquitetura durante a fase de análise. Por exemplo, uma trading pode não precisar especificar uma baixa latência em todo sistema, embora os arquitetos nesse domínio do problema saibam como isso é essencial. As características explícitas da arquitetura aparecem nos documentos de requisitos ou em outras instruções específicas.
Na Figura 4-2, é intencional a escolha do triângulo: cada elemento da definição dá suporte aos outros, que por sua vez dá suporte ao design geral do sistema. O divisor de águas criado pelo triângulo mostra o fato de que essas características da arquitetura costumam interagir entre si, levando ao uso generalizado do termo trade-off entre os arquitetos.
Características da Arquitetura Listadas (em Parte
) As características da arquitetura existem em um amplo espectro do sistema de software, variando desde as características do código de baixo nível, como modularidade, até questões operacionais sofisticadas, como escalabilidade e elasticidade. Não existe um padrão universal real, apesar das tentativas de codificar alguns no passado. Pelo contrário, cada organização cria sua própria interpretação desses termos. E mais, como o ecossistema de softwares muda muito rápido, novos conceitos, termos, medidas e verificações sempre aparecem, dando novas oportunidades para definições das características da arquitetura.
Apesar do volume e da escala, os arquitetos normalmente separam essas característica em grandes categorias. As seções a seguir descrevem algumas, com alguns exemplos.
JIM – Assistente
Jim IA: pode errar. Não digite dados pessoais. Suas mensagens são processadas por um serviço externo.
.gif)