Há muito tempo estou tendo dificuldade para criar Arquiteturas de Softwares, quanto mais eu trabalho numa Arquitetura mais difícil fica continuar trabalhando nela, pois ela cresce e se torna mais complexa, fica mais difícil entendê-la além do que fica mais difícil adicionar novas funcionalidades nela.
Dividir Arquitetura em Partes e definir como elas interagem entre si:
Minha ideia é simples e me parece correta: dividir a Arquitetura de todo o sistema em Partes (Módulos), que por sua vez serão divididas em Sub-Partes (Sub-Módulos) e assim por diante; cada Parte tem uma responsabilidade, e, eu defino como essas Partes interagem entre si (defino os Contratos entre elas) para que elas - trabalhando juntas - entreguem todas as funcionalidades do Software.
Então eu poderia começar dividindo a Arquitetura nas partes A, B, e C, e cada parte seria responsável por alguma coisa. Também posso começar a estabelecer os Contratos entre essas partes, como dizer que A envia um ObjetoX para B e que B deverá fazer determinada coisa ao receber esse ObjetoX, tendo que ler esse ObjetoX para saber como exatamente fará esta coisa; e que B para fazer seu trabalho, terá que solicitar que C faça determinada coisa, que varia um pouco dependendo do conteúdo do ObjetoX recebido por B.
Esses Contratos entre as Partes podem ser tão bem específicados ao ponto de definirmos os parâmetros de entrada e de retorno, definindo as assinaturas dos métodos para saber exatamente qual método cada Parte chamará na outra Parte, o que deverá passar pelos parâmetros e o que receberá de retorno.
Como eu gostaria que fosse:
Até aí parece tudo muito bom, se eu estivesse trabalhando numa equipe poderia até pensar que dá pra deixar cada desenvolvedor responsável por implementar uma Parte, e, contanto que cada desenvolvedor faça uma implementação cumprindo os Contratos já estabelecidos, no final poderemos unir todas as Partes (que se “encaixarão” perfeitamente umas nas outras por estarem seguindo os devidos Contratos) e teremos um Software pronto funcionando. Assim, esses desenvolvedores poderiam trabalhar ao mesmo tempo e nem precisariam se comunicar, não precisariam saber como os outros estão implementando suas Partes, porque os Contratos garantirão a compatibilidade final das Partes.
Como de fato é:
Mas não é isso que acontece! Na verdade passa bem longe disso:
O que acontece é que, quando se começa a implementar uma Parte, ou mesmo dividí-la em Sub-Partes, logo percebe-se que um ou mais dos Contratos já estabelecidos não são adequados! Talvez a Parte precise receber mais Dados, ou talvez esteja recebendo Dados que ela não precisará usar, ou talvez ela precise Notificar outras Partes de alguma coisa que pode acontecer enquanto ela é executada (talvez ela tenha que lançar exceptions não-previstas para outras Partes, ou, talvez ela precise que outra Parte seja um Listener de eventos que ocorrem dentro dela), talvez descubra-se que a Parte não conseguirá satisfazer todas as solicitações que o Contrato dela diz que ela satisfará, ou ainda, talvez queira-se implementar uma nova funcionalidade nesta Parte e o Contrato dela não oferece meios de utilizar esta nova funcionalidade.
De qualquer modo, é necessário renegociar os Contratos. E o maior problema é que isso ocorre durante todo o projeto da Arquitetura, conforme você vai definindo ela, criando as Sub-Partes, ou mesmo quando já está codificando, você acaba descobrindo que um ou mais dos Contratos já feitos precisa ser alterado.
O Retrabalho (quase?) sem fim:
Mas o pior problema é Propagação de Alterações: Alterar um Contrato significa que as Partes envolvidas nesse Contrato precisarão ser alteradas, afim de passarem a atender a nova versão do Contrato. Entretanto, ao alterar uma Parte, você pode descobrir que precisará alterar o mesmo Contrato novamente e/ou alterar outros Contratos, que levarão a alteração de outras Partes, que levarão a alteração de outros Contratos, e assim por diante, de modo que uma Alteração na Arquitetura provoca Alterações em cascata, tipo “efeito dominó”, e o resultado de tudo isso é um imenso retrabalho e o descarte de muito trabalho que já tinha sido feito.
Então, no fim, eu tenho um “processo de desenvolvimento” onde tenho que ficar refazendo um monte de coisa que já estabeleci na Arquitetura porque acabei descobrindo que é inadequado, inviável, ou mesmo porque me dei conta de que há uma forma significativamente melhor de fazer aquilo; isso leva a propagações de alterações constantes, de modo que a Arquitetura é instável (está sempre mudando) até ela ficar pronta.
Como a Arquitetura está sempre mudando, não dá pra mim trabalhar nela “Parte por Parte”, porque trabalhar numa Parte causará mudanças que se propagarão pelas outras, forçando-me a ter que ficar indo de Parte em Parte fazendo alterações nelas para que se adequem às mudanças. Como eu não posso trabalhar na Arquitetura “Parte por Parte”, é como se estivesse trabalhando nela “Toda de uma Vez”, o que torna o trabalho muito mais difícil, pois:
- Se a Arquitetura é instável, qualquer documentação ou diagramas também serão, aumentando muito o retrabalho (tem que ficar refazendo documentação e diagramas);
- Ter que editar a Arquitetura “Toda de uma Vez” me força a ter que ter na mente o funcionamento de “Toda a Arquitetura” ao mesmo tempo, não posso simplesmente me concentrar em cada Parte e esquecer do resto.
- Se eu estivesse trabalhando em Equipe, os desenvolvedores teriam que estar sempre renegociando os Contratos e refazendo suas Partes para atender as novas versões dos Contratos.
O que vocês me dizem?
- Então, estou fazendo algo errado?
- Vocês também fazem assim e passam por esses mesmos problemas?
- Existe uma forma melhor de se fazer a Arquitetura do Software?
- Qual é o “Passo-a-Passo” de vcs para fazer a “Arquitetura do Software”?
Qualquer ajuda, comentário, ou experiência é muito bem vinda