Este capítulo possui uma versão mais recente:

Todos os problemas em ciência da computação podem ser resolvidos por outro nível de indireção, exceto o problema de muitos níveis de indireção.
David J. Wheeler
O alto acoplamento é, provavelmente, a principal característica de sistemas difíceis de evoluir e, até mesmo, de manter. Por esse motivo, as decisões de design, não apenas as relacionadas à arquitetura, devem mitigar as chances de que ele aconteça.
0
Você concorda?x

Uma das muitas causas para o surgimento do acoplamento, sob a perspectiva arquitetural, são o choque de duas forças. De um lado, há um incentivo constante para o desenvolvimento de integrações, cada vez mais amplas, em tempos cada vez menores. De outro, há o desejo de garantir governança e manter as eficiências dos times que ficam, evidentemente, prejudicadas em função do aumento da complexidade e da dificuldade para a “garantia da qualidade”, por causa das integrações, levando a políticas de controle, muitas vezes, insustentáveis. Esse “aperta-solta”  colabora para a erosão arquitetural.
0
Você concorda com essa afirmação? Há algo a ponderar aqui?x

Sandboxing Cycle

O combate ao acoplamento é uma das principais motivações para padrões de design e práticas de engenharia populares – como inversão de controle, decomposição em microsserviços, comunicação assíncrona, CQRS, event-driven, distribuição em camadas, testes e muito mais. Aliás, boa parte dos “problemas” percebidos na implementação desses padrões e práticas decorre da “ignorância” desse propósito.
0
Você concorda?x

Acoplamento aferente e eferente

O acoplamento “nasce” de diversas formas, mas assume apenas duas naturezas genéricas distintas: aferente e eferente.

O acoplamento aferente de um artefato – seja ele um componente, classe, função, etc – surge quando este é referenciado por outro. Quando, por exemplo, o código de uma classe “A” faz referência ao código de uma classe “B”, diz-se que “B” tem um incremento em seu acoplamento aferente. Por outro lado, o acoplamento eferente de um artefato surge quando este demanda (ou depende) de outro.

Artefatos com acoplamento eferente mais alto “quebram” com mais frequência, pois, além de sua qualidade interna, são fortemente impactados pela qualidade interna de suas dependências.

Artefatos com acoplamento aferente alto são potenciais “pontos de falha” graves. Afinal de contas, problemas em sua qualidade interna afetam todos os que dele dependem.

Um indicador derivado das métricas de acoplamentos aferente e eferente é a instabilidade. Trata-se da proporção, em um determinado artefato, de acoplamento eferente em relação ao acoplamento total.

Quanto maior a instabilidade ponderada de um conjunto de artefatos, em um determinado módulo, maiores são as chances de “quebra”.

O “problema” das dependências externas

Em todo sistema relevante, há sempre um bocado de dependências para componentes externos. As dependências estabelecidas em componentes com maior acoplamento aferente têm, pelo menos em teoria, maior criticidade. Logo, demandam mais cuidados e gestão para mudança (atualização). Entretanto, esse “aumento do cuidado”, se implementado de maneira ingênua, tende ao aumento perigoso de “burocracia ruim”.
1
Você concorda?x

Quanto maior complexidade aferente uma dependência externa possuir, maior, também é a criticidade para sua manutenção e, também, maior a tentação para aumentar o “controle” sobre seu desenvolvimento, inclinando times, muitas vezes, a “reinventar a roda” ou, ainda mais impactante, trazer fornecedores para “dentro de casa”. Em grandes organizações, isso consiste em dificuldade para organização de times e, em casos extremos, tornam inquestionáveis a relevância da gestão de código envolvendo open sourcing ou inner sourcing.
1
Você concorda?x

Em grandes organizações há um “misto de emoções” com relação a “abertura” das dependências externas. Há quem considere arriscado em demasia “confiar” em padrões e tecnologias “fechadas”. Daí, aderem a política de somente utilizar tecnologias e padrões “abertos”. No outro extremo, há quem suspeite, muitas vezes por ignorância, da gestão do roadmap e das motivações das iniciativas mais abertas, optando por “marcas” no lugar de evidências (cover my ass strategy).

O “problema” do banco de dados monolítico

O banco de dados é componente importante na maioria das aplicações.

Tradicionalmente, grandes aplicações concentram as informações de todos os seus módulos em um mesmo banco de dados. Uma das justificativas é que isso simplifica a integração entre estes módulos. O “problema”, entretanto, é que junto com a potencial simplicidade de integração há o crescimento do acoplamento aferente. Afinal, falhas no banco de dados são cada vez mais impactantes na medida em que mais “módulos” compartilham dele.

A eventualidade de decomposição de um sistema em serviços, ou microsserviços, preservando o banco de dados monolítico potencializa o problema.

Há, no mercado, soluções de integração que envelopam o banco de dados, expondo informações a partir de um “serviço de dados”. Importante, entretanto, observar que, tais soluções, se implementadas de maneira ingênua, apenas transferem o acoplamento aferente do banco para o “serviço”, aumentando as chances de “vendor locking“.
0
Você concorda?x

CQRS: Segregação combatendo o acoplamento

Muitas aplicações LOB (Line-Of-Business) são projetadas para agrupar componentes conforme sua função primária. Não raro, a estratégia adotada classifica componentes em apresentaçãonegócio (juntando aplicação e domínio) e infraestrutura.

Nessa visão os componentes de apresentação, que “interagem” com o usuário, integram ao sistema recebendo dados, através de viewmodels, e enviando dados, com inputmodels. Aliás, essa distinção forte entre inputmodels e viewmodels, embora aumente a complexidade (pelo incremento da dimensionalidade), é, muitas vezes, preferível a uma única DTO por aliviar o acoplamento aferente (de um objeto único), simplificando mudanças e, potencialmente, aumentando o evolvability.
1
Você concorda?x

Cabe a camada de aplicação “traduzir” os inputmodels em comandos que, na prática, são solicitações para modificações de estado (mudança de dados percebíveis no longo prazo). Aliás, essa “especialização”, permite também a distinção, quando necessário, de métricas de performance importantes para operações de longa duração, habilitando ênfase de response time para a aplicação e de throughput para o domínio, através da inclusão de um componente para mensageria.

Métricas de performance e componentização

Certifique-se de adotar métricas apropriadas de performance para cada natureza de operação.


Se uma operação demandar, ao mesmo tempo, controle tanto de response time quanto throughput, há claros indícios de revisão arquitetural.

Tal medida, aliás, reduz o acoplamento aferente do domínio, tranferindo-o para o mecanismo de mensageria que, geralmente, tem menos demanda para mudanças e é ser mais “estável” (protocolo AMQP não recebe alterações desde 2012).

O “pulo do gato” está em identificar que o “domínio” pode ser pesado demais para a composição de viewmodels e pode acabar sendo “penalizado” pelo crescimento do acoplamento aferente decorrente da “instabilidade” das consultas, comum em aplicações LOB. Eventualmente, não seria má ideia segregar modelos para aliviar tal acoplamento.
1
Você concorda com essa afirmação? Há algo a ponderar aqui?x

 

Esta segregação do negócio em modelos distintos para comandos e consultas (Command Query Responsibility Segregation – CQRS) literalmente “divide” o acoplamento aferente da camada de negócios em duas partes, aumentando o custo potencial de desenvolvimento, porém, geralmente, reduzindo o custo de manutenção, porém, aumentando o acoplamento aferente para o banco de dados.

Em cenários extremos, o próprio banco de dados pode ser “segregado” para funções de gravação e consulta (algo bem comum, se pensar na utilização de mecanismos de caching) com vistas a reduzir o acoplamento aferente desse componente, com impacto positivo na escala, mas negativo no custo.

Caching, mensageria e a escalabilidade

Mensageria e caching são chaves para sistemas escaláveis. Porém, aumentam a complexidade da solução pelo incremento da dimensionalidade.

Combatendo acoplamento com abstrações

Um dos principais recursos para a redução o acoplamento aferente, pelo menos do ponto de vista estático, é a introdução de abstrações. Ou seja, a substituição das dependências de implementações concretas por abstratas, como interfaces e/ou contratos.

A introdução de abstrações  – como delegates, high order functions, interfaces, classes abstratas e contratos de serviço – transfere o acoplamento das implementações concretas para estas abstrações. Geralmente, isso causa alívio de complexidade e viabiliza o isolamento mais efetivo das unidades, facilitando, entre outras coisas a escrita de testes automatizados. Entretanto, quando abstrações são criadas em demasia, cresce a dificuldade dos desenvolvedores para entender como os componentes se conectam no contexto dinâmico (durante a execução), aumentando o volume de carga cognitiva necessária para o trabalho e, consequentemente, custos de desenvolvimento.
0
Você concorda?x

As abstrações tendem a se tornar “difíceis de mudar” na medida em que o software vai sofrendo adaptações, com a criação de mais implementações concretas.

Entretanto, bem mais comum (e bem menos controlada) é a proliferação de artefatos “consumidores”, operando através de inversão de controle ou mecanismos de descoberta.

Essa “proliferação descontrolada”, aliás, é a justificativa para que aprimoramentos sejam explicitados em abstrações novas, de maneira a não quebrar contratos mais antigos e potencialmente não evidentes.

O volume de abstração também é uma métrica arquitetural interessante. De maneira simples, trata-se da proporção, em um determinado escopo, da quantidade de abstrações com relação a quantidade de implementações concretas.

 

O “lado ruim” da testabilidade

O “efeito colateral”, geralmente positivo, da testabilidade de um sistema é a redução do acoplamento aferente estático, pela introdução de abstrações, necessárias para a escrita de testes de unidade. O problema é que a introdução de abstrações implica em aumento da complexidade dinâmica que precisará ser compensada pela proteção contra regressões que os testes, em teoria, vão apoiar. 
0
Você concorda?x

Artefatos instáveis, ainda em desenvolvimento, podem demandar atualizações nas abstrações que os representam que, como sabemos, tem complexidade aferente sempre maior. Por isso, com frequência é justificável postergar a inclusão de abstrações, mesmo sob pena de reduzir a testabilidade.
0
Você concorda?x

O “lado ruim” de IRepository

Uma prática comum em implementações baseadas em Domain-driven Design é a separação entre contrato (interface pública) para repositórios, geralmente colocados no mesmo pacote destinado aos objetos que formam o modelo do domínio, das implementações concretas. A justificativa mais comum é dar “liberdade” para a eventualidade de trocar a tecnologia de persistência. Outra justificativa, é facilitar a escrita de testes automatizados.

Na prática, o que se tem verificado é que raramente sistemas precisam, realmente, suportar mais do que uma tecnologia de persistência. No fim, o que se constata são replicações rasas das interfaces públicas fornecidas pelas bibliotecas que fazem “conexão” com os bancos ou, pior, nivelamento por baixo com base nas características mais genéricas dentre duas alternativas possíveis. (Por exemplo, RavenDB e MongoDB são dois bancos de dados NoSQL de documentos. RavenDB suporta transações. Mongo, não! Uma interface “abstrata” teria que descartar essa feature do RavenDB).
2
Você concorda com essa afirmação? Há algo a ponderar aqui?x

Quanto a “testabilidade”, boa parte das bases de código concentra seus usos de repositórios em serviços de aplicação que, por sua vez, tem baixo acoplamento afaerente e elevado acoplamento eferente. Ou seja, menos demandantes de testes de unidade. Dessa forma, as famigeradas “interfaces” acabam servindo apenas para viabilizar testes que agregam pouco (ou nenhum) valor.
1
Você concorda com essa afirmação? Há algo a ponderar aqui?x

O “lado ruim” das APIs públicas

APIs públicas tem acoplamento aferente dinâmico difícil de medir. Entretanto, como se sabe, quanto mais bem sucedidas, geralmente maior será tal acoplamento e, consequentemente, maiores serão as dificuldades para evoluir. Essa é a razão principal para o versionamento eficiente dos contratos públicos, com políticas claras para descontinuidades.

Um “antídoto” para o problema das APIs públicas com acoplamento aferente muito grande é a segmentação das APIs em externas e internas. As APIs externas serão aquelas que irão atender publicamente demandas bem delimitadas, e internas, irão apenas expor funcionalidades e capabilities. Tal solução, entretanto, tem como custo o aumento da dimensionalidade e, em consequência da complexidade. Em sistemas maiores, quase sempre é necessário desenvolver algum mecanismo de mediação.
0
Você concorda com essa afirmação? Há algo a ponderar aqui?x

O “lado ruim” das arquiteturas event-driven

Outro “antídoto” comum para a complexidade aferente é adoção de comunicação através de eventos. Aliás, essa é uma “bomba” contra o acoplamento que, como tal, acaba tendo efeitos colaterais nem sempre desejáveis.

Sistemas baseados em eventos demandam o planejamento de coreografias, regulando o comportamento dinâmico, tremendamente difíceis de entender sem o suporte de documentação eficiente.

Choreography

A alternativa é a introdução de componentes especificamente dedicados a orquestração, porém, estes, acabam comprometidos por terem alto acoplamento aferente.

Orchestration

 

Determinando o volume ideal de abstrações

Como já foi dito, transferir acoplamento aferente de algumas implementações concretas para abstrações pode simplificar a manutenabilidade dos sistemas, aprimorando o evolvability. Entretanto, abstrações em demasia, ou para artefatos errados, tornam sistemas mais difíceis de entender e caros para manter.
0
Você concorda?x

O volume ideal de abstrações tem relação direta com a instabilidade observada. Quando maior a instabilidade de um artefato, menor a necessidade de uma abstração que alivie seu acoplamento aferente. Por outro lado, artefatos com baixa instabilidade “gritam” por abstrações.

No gráfico indicado acima, artefatos que se posicionem no canto superior-direito se encaixam na zone of uselessness, ou seja, código tão abstrato que se converte em algo difícil de manter. No outro extremo, artefatos que se posicionem no canto inferior esquerdo estão na zone of pain, ou seja, acoplamento aferente extremamente elevado sem o “alívio” de abstrações que habilitem testabilidade e novas implementações.

A medida de desequilíbrio entre a instabilidade e o volume de abstrações de um software pode ser determinada pela distância da condição atual e a proporção adequada.

Modularidade

Um módulo é, por definição, um agrupamento de unidades independentes que pode ser utilizado para a composição de estruturas mais complexas.

Módulos estabelecem limites para “acoplamento mais seguro”, afinal, estão dentro de um mesmo “espaço de manutenção”. Dessa forma, o acoplamento de artefatos dentro de um mesmo módulo é sempre preferível aos externos.
0
Você concorda?x

No dia-a-dia, os desenvolvedores têm diferentes recursos para expressar modularidade. Começando pelo nível básico, em métodos e funções, até níveis mais altos, como classes, pacotes ou namespaces e assemblies – todas essas opções com diferentes práticas e políticas para regras de escopo e visibilidade. Quanto mais frágeis as “fronteiras” de cada módulo, maiores são as dificuldades para o evolvability.


Módulos muito grandes são, por associação, mais complexos, logo, demandantes de maior esforço cognitivo para o entendimento. Por conta disso, precisam ser mantidos por times com mais integrantes, com custo mais alto de gestão. Módulos muito pequenos geralmente são menos coesos e com mais demanda para acoplamento eferente, por isso, muito mais instáveis.
1
Você concorda?x

O tamanho certo para os módulos de um sistema de software extrapolam características técnicas e tem relação direta com o nível de maturidade das organizações que os mantém.

A coesão do módulo é determinada pela clara ligação entre os artefatos que o constituem. Quanto menor a coesão, maiores as chances de erosão.

A modularização é um recurso fundamental para a gestão do acoplamento.

O “lado ruim” de microsserviços

A fragmentação de microsserviços é, em essência, uma estratégia para definição da modularidade de um sistema de software, mas não é a única!

Sob a perspectiva da gestão do acoplamento, microsserviços agrupam uma série de artefatos – incluindo código, banco de dados, mecanismos de replicação, etc – localizando o acoplamento entre eles, tentando preservar a coesão. Além disso, têm suas funcionalidades expostas de maneira controlada e bem versionada.

O “problema” é que atingir o nível necessário de coesão para um microsserviço é difícil. Não raro, implementações ingênuas desta estratégia de modularidade gera artefatos (microsserviços, no caso), com índices gigantescos de acoplamento eferente, tornando-os instáveis. Além disso, também operam em regime de coreografia, tornando o entendimento do comportamento dinâmico mais difícil.
0
Você concorda?x

Algumas verdades inconvenientes

Um sistema não pode ser composto apenas por componentes estáveis. Partes do software que precisam ser adaptadas com mais frequência devem ser aquelas com menor acoplamento aferente e maior acoplamento eferente, logo, com menos abstrações.

Algumas implicações da análise do acoplamento são:

  • O frontend das aplicações é, geralmente, o conjunto de componentes que mais demanda adaptações, por isso, geralmente, não é bom local para soluções mais sofisticadas e abstrações inteligentes.
    0
    Você concorda?x
  • APIs externas, embora menos demandantes de adaptações, também precisam ser instáveis para a maioria dos domínios, por isso não devem ser projetadas para ter alto acoplamento aferente. Assim, quase sempre é preferível, por exemplo, desenvolver uma API externa para atender desktop e outra para dispositivos móveis, movendo a parte “estável” para uma camada de APIs internas.
  • Não “fechar” com algumas tecnologias – como bancos de dados, por exemplo – introduz a necessidade de abstrações que, naturalmente, tem complexidade aferente mais alta e acabam dificultando o evolvability.
    0
    Por favor, deixe seu feedback aquix
  • Abstrações em demasia são demonstrações explícitas de ingenuidade inteligente.
    0
    Você concorda?x

Para toda jornada é necessário ter o nível adequado de “bagagem”

Acoplamento é inevitável mas, sem os cuidados adequados, pode atingir níveis insustentáveis. Criar “pontos de acoplamento” é fácil e, depois disso, remover é difícil.

Arquiteturas de software de qualidade são, acima de tudo, aquelas que evoluem mitigando necessidades para o acoplamento. Além disso, é parte das práticas de arquitetura de software monitorar e estabelecer estratégias para lidar com o acoplamento quando ele surgir. Entretanto, não ignoremos o fato que toda escolha implica em uma renúncia. Sempre haverão trade-offs. Nada é bom para tudo! Reduzir o acoplamento aferente implica, muitas vezes, em introduzir abstrações, o que aumenta a complexidade pela dimensionalidade e pela irreversibilidade.

 

// TODO

Antes de avançar para o próximo capítulo, recomendo as seguintes reflexões:

  • A estratégia de modularidade do software em que você está trabalhando colabora para a mitigação de causas para acoplamento?
  • Os testes automatizados estão “exigindo” abstrações de maneira razoável?
  • Que artefatos de sua solução tem alto nível de acoplamento aferente?
  • Os artefatos com acoplamento aferente tem níveis adequados de testes automatizados?
  • As abstrações com acoplamento aferente mais elevado tem estratégia clara de versionamento?

Referências bibliográficas

MARTIN, Robert. Clean Architecture: a craftsman’s guide to software structure and design. Portland, Or: Pearson Education, 2017. (Robert C. Martin Series).

MUNROE, Randall. Dependency Disponível em: https://xkcd.com/2347/. Acesso em: 03 mai. 2021.

MUNROE, Randall. Open Source Disponível em: https://xkcd.com/225/. Acesso em: 03 mai. 2021.

MUNROE, Randall. Sandboxing Cycle Disponível em: https://xkcd.com/2044/. Acesso em: 03 mai. 2021.

MUNROE, Randall. Shopping Team Disponível em: https://xkcd.com/309/. Acesso em: 04 mai. 2021.

RICHARDS, Mark; FORD, Neal. Fundamentals of Software Architecture: an engineering approach. Sebastopol, Ca: O’Reilly Media, Inc, 2020.

STACK OVERFLOW. Orchestration or Coreography. Disponível em: https://stackoverflow.com/questions/4127241/orchestration-vs-choreography. Acesso em: 2 maio 2021.

Compartilhe este capítulo:

Compartilhe:

Comentários

Participe da construção deste capítulo deixando seu comentário:

Inscrever-se
Notify of
guest
8 Comentários
Oldest
Newest Most Voted
Feedbacks interativos
Ver todos os comentários
Vilson
Vilson
3 anos atrás
Feedback no conteúdo deste capítulo Em todo sistema relevante, há sempre um bocado de dependências para componentes externos. As dependências estabelecidas em componentes com maior…" Ler mais »

acho que o texto a partir de “..maior criticidade” está confuso, me parece que remete ao componente que referencia, quando em verdade deveria remeter ao componente referenciado (aquele com dependências aferentes) Mas, o componente referenciado (aferente) não possui conhecimento dos componentes que o referenciam (eferentes), logo não há como inferir sobre a necessidade de “cuidado” em sua atualização. Por outro lado, o aumento de dependências externa é crítico pois torna o sistema sujeito a falhas de origem externa, falhas para as quais não há remédio outro além da eliminação da dependência.

vilson
vilson
3 anos atrás
Feedback no conteúdo deste capítulo Quanto maior complexidade aferente uma dependência externa possuir, maior, também é a criticidade para sua manutenção e, também, maior a…" Ler mais »

Certa vez fui chamado a avaliar os custos de duas possíveis soluções, 10 x 1. Concluí que ambas as soluções eram compatíveis com as necessidades do cliente e portanto a solução de custo 10 poderia ser descartada. Então fui surpreendido pelo cliente. Ele não estava interessado em custo, mas sim na garantia dada pelo fornecedor da solução, visto que em caso de falha o prejuízo poderia ser centenas ou milhares de vezes maior do que o custo da solução em avaliação.

Vilson
Vilson
3 anos atrás
Feedback no conteúdo deste capítulo Nessa visão os componentes de apresentação, que "interagem" com o usuário, integram ao sistema recebendo dados, através de viewmodels, e…" Ler mais »

A sigla “DTO” aparece aqui pela primeira vez no documento, acredito ser interessante sua descrição.
Entendo que a separação em inputModels e viewModels adiciona flexibilidade ao passo que encerra na aplicação as responsabilidades de produzir a informação, deixando para a apresentação apenas a formatação/apresentação. Nesse sentido, talvez seja interessante que a aplicação seja uma composição input + output.

Vilson
Vilson
3 anos atrás
Feedback no conteúdo deste capítulo O "pulo do gato" está em identificar que o "domínio" pode ser pesado demais para a composição de viewmodels e pode…" Ler mais »

Bom surgiu a subdivisão, uma pequena menção na seção anterior seria interessante.

Marcio
Marcio
3 anos atrás
Feedback no conteúdo deste capítulo Na prática, o que se tem verificado é que raramente sistemas precisam, realmente, suportar mais do que uma tecnologia de…" Ler mais »

Creio que desde a versão 4.0, MongoDB suporta transação multidocumento, cumprindo os requisitos ACID.

Marcio
Marcio
3 anos atrás
Feedback no conteúdo deste capítulo Na prática, o que se tem verificado é que raramente sistemas precisam, realmente, suportar mais do que uma tecnologia de…" Ler mais »

Notoriamente, isso viola o “L” do S.O.L.I.D (princípio de Liskov), mas pode ser um pouco aliviado (mas não evitado) através do padrão Unit Of work

Marcio
Marcio
3 anos atrás
Feedback no conteúdo deste capítulo Quanto a "testabilidade", boa parte das bases de código concentra seus usos de repositórios em serviços de aplicação que, por…" Ler mais »

Aferente

Marcio
Marcio
3 anos atrás
Feedback no conteúdo deste capítulo Módulos muito grandes são, por associação, mais complexos, logo, demandantes de maior esforço cognitivo para o entendimento. Por conta disso,…" Ler mais »

Módulos pequenos tendem a ter menos responsabilidades. Não seria o caso de serem mais coesos então?

Fundador e CEO da EximiaCo, atua como tech trusted advisor ajudando diversas empresas a gerar mais resultados através da tecnologia.

Mentoria

para arquitetos de software

Imersão, em grupo, supervisionada por Elemar Júnior, onde serão discutidos tópicos avançados de arquitetura de software, extraídos de cenários reais, com ênfase em systems design.

Consultoria e Assessoria em

Arquitetura de Software

EximiaCo oferece a alocação de um Arquiteto de Software em sua empresa para orientar seu time no uso das melhores práticas de arquitetura para projetar a evolução consistente de suas aplicações.

ElemarJúnior

Fundador e CEO da EximiaCo, atua como tech trusted advisor ajudando diversas empresas a gerar mais resultados através da tecnologia.

+55 51 99942-0609 |  contato@eximia.co

+55 51 99942-0609  contato@eximia.co

8
0
Quero saber a sua opinião, deixe seu comentáriox
()
x