Estrutura para evolução
Uma conceção evolutiva é a chave para a inovação contínua
Todas as aplicações bem sucedidas mudam ao longo do tempo, quer seja para corrigir erros, adicionar novas funcionalidades, incorporar novas tecnologias ou tornar os esquemas existentes mais dimensionáveis e resilientes. Se todas as partes de uma aplicação estiverem fortemente conjugadas, torna-se muito difícil introduzir as alterações no sistema. A alteração de uma parte da aplicação pode interromper outra parte ou fazer com que as alterações oscilem por toda a base de código.
Este problema não se limita às aplicações monolíticas. Uma aplicação pode estar decomposta em serviços, mas continuar a apresentar o tipo de acoplamento forte que faz com que o sistema fique rígido e frágil. Mas, quando os serviços são concebidos para evoluir, as equipas podem inovar e fornecer continuamente novas funcionalidades.
Os microsserviços estão se tornando uma maneira popular de alcançar um design evolutivo, porque abordam muitas das considerações listadas aqui.
Recomendações
Imponha coesão elevada e acoplamento alargado. Um serviço é coeso se fornecer funcionalidades que estão interligadas logicamente. Os serviços são relativamente acoplados se for possível alterar um serviço sem alterar o outro. Alta coesão geralmente significa que as mudanças em uma função exigirão mudanças em outras funções relacionadas, onde todas as funções relacionadas residem em um serviço. Se chegar à conclusão que atualizar um serviço requer atualizações coordenadas de outros serviços, poderá ser um sinal de que os serviços não são coesos. Um dos objetivos do design orientado por domínio (DDD) é identificar esses limites.
Encapsule o conhecimento do domínio. Quando um cliente consome um serviço, a responsabilidade de impor as regras de negócio do domínio não deve recair sobre o cliente. Em vez disso, o serviço deve encapsular todo o conhecimento do domínio que seja da respetiva responsabilidade. Caso contrário, todos os clientes têm de impor as regras de negócio e acaba por ficar com dados de conhecimento do domínio distribuídos por diferentes partes da aplicação.
Utilize mensagens assíncronas. As mensagens assíncronas são uma forma de separar o produtor de mensagens do consumidor. O produtor não depende do consumidor que responde à mensagem ou efetua qualquer ação específica. Com uma arquitetura de pub/sub, o produtor nem sequer sabe quem está a consumir a mensagem. Os novos serviços podem facilmente consumir mensagens sem modificações para o produtor.
Não incorpore dados de conhecimento do domínio num gateway. Os gateways podem ser úteis numa arquitetura de microsserviços, para ações como o encaminhamento de pedidos, tradução de protocolos, balanceamento de carga ou autenticação. No entanto, o gateway deve restringir-se a este tipo de funcionalidade de infraestrutura. Não deve implementar qualquer conhecimento de domínio, para evitar tornar-se uma dependência pesada.
Exponha interfaces abertas. Evite criar camadas de tradução personalizadas que ficam entre serviços. Em vez disso, um serviço deve expor uma API com um contrato de API bem definido. A API deve ter versão, para que possa evoluir a API, mantendo a retrocompatibilidade. Dessa forma, pode atualizar um serviço sem coordenar as atualizações para todos os serviços a montante que dependem dele. Os serviços destinados ao público devem expor uma API RESTful através de HTTP. Os serviços de back-end poderão utilizar um protocolo de mensagens de estilo RPC por motivos de desempenho.
Conceba e teste os contratos de serviço. Quando os serviços expõem APIs bem definidas, pode desenvolver e testar essas APIs. Dessa forma, pode desenvolver e testar um serviço individual sem lançar todos os serviços dependentes. (Obviamente, teria na mesma de efetuar testes de integração e de carga nos serviços reais.)
Use funções de fitness. As funções de aptidão medem o resultado para ver se está mais perto ou mais longe de uma solução ideal. As funções de aptidão ajudam a proteger as características arquitetónicas à medida que as mudanças ocorrem ao longo do tempo. A função de aptidão é qualquer mecanismo que fornece uma avaliação objetiva da integridade das características da arquitetura. A avaliação pode incluir uma variedade de mecanismos, como métricas, testes de unidade, engenharia de caos e assim por diante. Por exemplo, o arquiteto pode identificar o tempo de carregamento da página como uma característica importante. Posteriormente, a carga de trabalho deve ter uma função de adequação para testar o tempo de carregamento da página e executar o teste como parte da integração contínua.
Abstraia a infraestrutura da lógica do domínio. Não permita que a lógica do domínio se misture com funcionalidades relacionadas com a infraestrutura, como as mensagens ou a persistência. Caso contrário, as alterações da lógica do domínio irão requerer atualizações das camadas de infraestrutura e vice-versa.
Passe as questões transversais para um serviço separado. Por exemplo, se vários serviços tiverem de autenticar pedidos, poderia mover esta funcionalidade para o seu próprio serviço. Em seguida, você pode evoluir o serviço de autenticação — por exemplo, adicionando um novo fluxo de autenticação — sem tocar em nenhum dos serviços que o usam.
Implemente os serviços de modo independente. Quando a equipa de DevOps consegue implementar um único serviço independentemente dos outros serviços na aplicação, as atualizações podem acontecer mais rapidamente e em segurança. As correções de erros e novas funcionalidades podem ser implementadas a uma cadência mais regular. Conceba a aplicação e o processo de lançamento para que suportem atualizações independentes.