Definição: Acoplamento
Duas ou mais partes de um sistema de software estão acopladas quando a mudança em uma delas implica mudanças nas outras.
SOLID Principles (Uncle Bob) Nesta palestra, “Uncle” Bob compartilha como a aplicação de princípios SOLID, na arquitetura, podem ajudar a mitigar os riscos associados ao acoplamento. |
O acoplamento é uma das principais causas para dificuldades em evoluir software. Logo, maior fonte de custo. Dívidas técnicas associadas com acoplamento costumam ser as que cobram “juros” mais altos. |
Tipos de acoplamento
All things are poison, and nothing is without poison; the dosage alone makes it so a thing is not a poison. Paracelsus |
Em termos práticos, o acoplamento “amarra” iniciativas e frentes de trabalho aumentando os custos de gestão por demandar atividades de sincronização e coordenação.
Há muitas formas de classificar o acoplamento. Entretanto uma das mais aceitas indica que acoplamento pode ser:
- Operacional – quando um componente não consegue “funcionar” quando outro componente não está disponível;
- Desenvolvimento – quando mudanças em um componente implica em mudanças coordenadas em outro;
- Semântico – quando dois componentes atribuem e dependem significados comuns para determinados conceitos (por exemplo, com a implementação de núcleos compartilhados em DDD)
- Funcional – quando dois componentes precisam trabalhar juntos por compartilhar uma “responsabilidade”
- Incidental – quando dois componentes estão acoplados sem nenhuma “boa” razão (contratos de APIs)
Os tipos de acoplamento indicados acima não são excludentes.
Sempre que possível, evite a definição de núcleos compartilhados. |
Uncoupling - Michael Nygard Nesta palestra, Michael Nygard compartilha conceitos e insights sobre acoplamento, principalmente considerando sua inevitabilidade. |
Há também a classificação entre acoplamento estático – aquele que pode ser identificado a partir de análise estática – e acoplamento dinâmico – que é percebido em tempo de execução.
A busca pela melhoria contínua na arquitetura
A redução de custos no desenvolvimento de software, ao longo do tempo, pode ser obtida pela busca disciplinada e mitigação das fontes de acoplamento.
As práticas de arquitetura de software devem, continuamente:
- Procurar e explicitar acoplamentos latentes;
- Analisar os custos decorrentes dos acoplamentos identificados;
- Analisar trade-offs para determinar formas alternativas de acoplamento, mais baratas
- Projetar e acompanhar a implementação de “antídotos” para o acoplamento.
Software Architecture: The Hard Parts Como o acoplamento fundamenta as decisões e práticas de arquitetura? Este é o tema central desse livro, mais do que recomendado, |
Acoplamento aferente e eferente
O acoplamento “nasce” de diversas formas, mas assume apenas duas naturezas genéricas distintas: aferente (incoming) e eferente (outcoming).
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 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.
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ção, negócio (juntando aplicação e domínio) e infraestrutura.
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).
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.
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 “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.
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.
A alternativa é a introdução de componentes especificamente dedicados a orquestração, porém, estes, acabam comprometidos por terem alto acoplamento aferente.
Determinando o volume ideal de abstrações
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.
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.0Você 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.0Você concorda?x
- 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.2Você concorda?x
- Abstrações em demasia são demonstrações explícitas de ingenuidade inteligente.0Você 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?
Considerar trocar o gráfico acima por um gráfico mais intuitivo onde as letras X Z Y são substituídas por camadas comumente utilizadas no dia a dia. E.g. Frontend, Serviço, Domínio, etc.