A forte dependência entre classes (ou componentes) é o principal vilão de uma boa arquitetura de software! Evidentemente, não existe uma arquitetura completamente desacoplada, pois desta forma os objetos não poderíam trocar mensagens uns com os outros. A idéia é minimizar a existência destas dependências, e inserir no modelo apenas dependências bem planejadas. Particularmente, costumo chamar estas dependências bem planejadas de dependências saudáveis, afinal de contas, trazem muitos benefícios para o sistema como um todo.
No post Princípios Fundamentais do Processo Unificado, apresento o conceito Processo Centrado em Arquitetura. Resumidamente, o processo de desenvolvimento é guiado pela evolução da arquitetura, e o arquiteto preocupa-se em estabelecer um alto grau de modularidade para facilitar a expansão e a manutenção do sistema. Com módulos bem planejados, e dependências saudáveis entre eles, minimizamos três propriedades internas indesejáveis: Rigidez, Fragilidade, e Imobilidade. Vale a pena dar um pulo naquele post para entender cada uma destas três propriedades.
OK, vamos ao que realmente interessa: Como modelar apenas dependências saudáveis? Resposta bate-pronto: Utilizando interfaces para a inversão de dependências! Interface é uma definição parcial de algum conceito do domínio tratado por sua arquitetura. Certamente é a maior aliada do arquiteto de software! Vamos apelar para a UML para entender como inverter dependências.
O esquema acima poderia ser parte integrante da arquitetura de um comércio eletrônico, onde pedidos são armezenados na base de dados. É natural pensar que um objeto da classe Pedido envie mensagens para um objeto da classe MSSQL. Entretando, a dependência existente a partir de Pedido para MSSQL é indesejável. Algumas consequências podem ser observadas:
- O negócio passa a depender da tecnologia. Como sabemos, a tecnologia está em constante evolução, e cada mudança tecnológica potencialmente provoca uma mudança nas classes de negócio, como a classe Pedido neste exemplo. Regras de negócio tendem a ser mais estáveis ao longo do tempo.
- A arquitetura assume que, invariavelmente, o armazenamento dos dados será realizado por um banco de dados. É importante lembrarmos que o armezenamento baseado no sistema de arquivos vem ganhando algum destaque, e mostra bom desempenho.
- Caso a classe de acesso aos dados tenha sido desenvolvida para trabalhar com um banco de dados específico (MSSQL neste exemplo), então a classe de negócio deverá ser constantemente modificada para cobrir eventuais novos provedores de dados (MySQL por exemplo).
- Cria um acoplamento que dificulta testes unitários.
Como alternativa, poderíamos fazer com que a classe Pedido dependesse de uma interface que teria por objetivo prímário desacoplar o negócio das questões de armezenamento e recuperação dos dados.
Repare que, neste momento, a dependência é invertida, e todos os pontos negativos citados são removidos da arquitetura! Porém, o objeto de negócio ainda precisa enviar a mensagem para o objeto que manipula a base de dados. Como realizar isso nesta nova arquitetura? Simples! Interfaces não existem em tempo de execução, apenas objetos. Desta forma, o lugar da interface é ocupado pelo objeto de alguma classe que realiza esta interface. Podemos ter uma classe para MSSQL, outra para MySQL, uma terceira para FileSystem, e assim por diante.
Visualizar a inversão de dependência é ainda mais fácil quando tratamos com componentes, e não classes, como segue.
No próximo diagrama a dependência é invertida com o advento da interface.
A notação caixa-preta torna tudo ainda mais evidente, como segue:
E, novamente, a inversão da dependência:
É isso aí pessoal, espero ter colaborado com mais essa! Até a próxima, abraços!