Arquitetura de aplicativos: práticas recomendadas para deixar seus aplicativos seguro para mudanças futuras

Como arquitetos de software, é nossa responsabilidade imaginar e projetar sistemas capazes de suportar os modelos de negócios implacáveis ​​desta era. Nesse sentido, é fundamental desenvolver maneiras de evoluir nossa arquitetura de aplicativo para combinar conceitos e processos de negócios corretamente. Caso contrário, a arquitetura não será estruturalmente sólida e teremos que lidar com uma terrível “arquitetura espaguete”.

Nesta postagem do blog, vou compartilhar algumas das melhores práticas que você deve seguir para construir uma arquitetura de aplicativo estruturada e escalonável, evitando transformar seus sistemas em uma tigela de espaguete. Este artigo é baseado em um TechTalk recente no mesmo tópico, Web and Mobile Architecture with Architecture Dashboard . Para uma discussão mais detalhada, convido você a dar uma olhada.

Vamos começar!


Por que a arquitetura de aplicativos é tão importante?

A arquitetura do aplicativo inclui todos os módulos e componentes do software, sistemas internos e externos e as interações entre eles que constituem um aplicativo. Uma arquitetura de aplicativo bem estruturada garante que seus aplicativos possam escalar conforme as demandas de negócios, atendendo aos requisitos de negócios e usuários pretendidos, ao mesmo tempo em que garante que todos os conceitos sejam isolados corretamente e com dependências sólidas entre si.

Suponha que você ignore o lado da arquitetura dos aplicativos ao alterar e adicionar novos requisitos ao seu projeto de software. Você acabará ficando com uma arquitetura espaguete, um labirinto de sincronização e dependências não gerenciáveis ​​entre diferentes partes de seu aplicativo.

O problema de uma arquitetura espaguete é que ela resulta em vários problemas, sendo os principais:

Fraca abstração de serviço:  não isolar e abstrair corretamente os serviços em torno dos principais conceitos de negócios espalha as regras de negócios em diferentes sistemas, tornando a reutilização de código muito pouco estruturada e até impossível;

Dependências não gerenciáveis: Quando os componentes não estão isolados uns dos outros corretamente, a atualização ou substituição de um sistema tem um efeito de bola de neve, o que significa que as alterações em uma parte afetam todas as suas dependências;

Sistemas legados inflexíveis e lentos: a adaptação rápida de um sistema legado às mudanças nos negócios torna-se difícil. Se o sistema for complexo e inflexível, as mudanças podem demorar muito. E se a tecnologia for obsoleta, o acúmulo de informações essenciais e dependências de sistemas ao longo do tempo pode impedir qualquer substituição.


Então, como você evita isso?

Melhores práticas para construir uma arquitetura de aplicativo escalonável
A chave para construir uma arquitetura de aplicativo escalonável e confiável é basear sua arquitetura em princípios fortemente definidos e bases bem estabelecidas. Dessa forma, você pode suportar um crescimento rápido e escalabilidade massiva, evitando pesadelos de implantação, custos mais altos de manutenção de código e acompanhando as necessidades de negócios.

Para fazer isso, você precisa começar com um projeto de arquitetura, que o ajudará a:

Gerar consenso entre todos os jogadores
Planejamento de suporte
Facilite a mudança
Gerenciar complexidade
Reduza os riscos
Minimize o débito técnico - que é o jogo final de qualquer arquitetura bem projetada.
Então, como você cria um projeto de arquitetura à prova de futuro? Deixe-me apresentar a você o Architecture Canvas, um framework para apoiar e acelerar o projeto de arquitetura que seguimos na OutSystems.



Apresentando o Architecture Canvas

O Architecture Canvas é uma estrutura multicamadas que promove a abstração de serviços e componentes reutilizáveis, preservando ciclos de vida independentes e minimizando o impacto das mudanças, tornando sua arquitetura mais fácil de manter e evoluir.

É assim que parece:

Três camadas da tela de arquitetura


Camada de base: nesta camada, você implementa todos os requisitos não funcionais reutilizáveis, como serviços para se conectar a sistemas externos ou para estender sua estrutura usando bibliotecas de padrões de IU reutilizáveis ​​e temas, por exemplo. Todos os tipos de requisitos não funcionais.

Camada central: no topo da camada básica, você implementa seus serviços de negócios principais, incluindo serviços em torno de conceitos de negócios, regras de negócios, entidades de negócios, transações de negócios e widgets de negócios. Esses serviços devem ser agnósticos no sistema e baseados nos serviços básicos para abstrair qualquer detalhe de integração de que você possa precisar. É nessas duas camadas inferiores que você isola todos os serviços ou componentes reutilizáveis.

Camada do usuário final: a camada superior é onde você oferece suporte às interações dos usuários por meio de interfaces de usuário e processos usando os serviços básicos e básicos para oferecer suporte à jornada do usuário. Observe que um módulo nesta camada nunca deve fornecer serviços a outros módulos para garantir a independência total do ciclo de vida.


Validando a Estrutura de Sua Arquitetura

Para garantir que sua arquitetura seja sólida e que você não termine com uma arquitetura de monólito ou espaguete, você deve seguir um conjunto de diretrizes e recomendações. As regras a seguir referem-se a referências fortes entre módulos ou aplicativos (por exemplo, ações ou blocos), portanto, desconsidere referências soltas como Ações de serviço ou Destinos de tela.


1. Sem referências ascendentes nas 3 camadas:

Dada a camada estruturada, é evidente que não queremos que os serviços de base agnósticos de negócios dependam de conceitos de negócios centrais ou que qualquer serviço reutilizável dependa de interfaces de usuário final. Além disso, as referências ascendentes tendem a criar um cluster onde quaisquer dois módulos direta ou indiretamente vinculados têm uma dependência circular. Dê uma olhada no exemplo abaixo:


O módulo B pode alcançar o módulo A indiretamente e o módulo A também pode alcançar o módulo B indiretamente. Portanto, temos um cluster de módulos interdependentes. E se você tiver outro módulo de usuário final (EU2) que está consumindo legitimamente o serviço principal B, ele se tornará dependente de todo o cluster. Conseqüentemente, não apenas seu tempo de execução recebe uma grande pegada desnecessária, mas também é afetado por alterações feitas em módulos dos quais ele nem deveria estar ciente.


2. Nenhuma referência lateral entre os usuários finais:

Os módulos de usuário final nunca devem fornecer serviços reutilizáveis ​​para garantir que sejam isolados corretamente, permitindo que os usuários finais tenham diferentes ciclos de vida. Veja o exemplo abaixo:


Se o usuário final 1 consumir o usuário final 2, não só ele não pode ser independente de EU2, mas também não pode ser independente da hierarquia restante abaixo dele.



3. Evite referências circulares entre os módulos principais e fundamentais:

Se você seguir as duas regras anteriores, não precisará se preocupar com os ciclos entre os módulos do usuário final. Um ciclo é sempre indesejável, pois traz um impacto esperado sobre como gerenciar o código. Um ciclo entre os módulos indica que os conceitos não são abstraídos corretamente.

No exemplo abaixo, uma das duas coisas deve acontecer: 1) A e B estão tão fortemente conectados que devem fazer parte do mesmo módulo (por exemplo, Pedido e Linha de Pedido) ou 2) uma das dependências deve ser quebrada por colocar a lógica no lugar certo, de acordo com a relação esperada entre os conceitos. Por exemplo, os contratos dependem dos clientes, mas os clientes devem poder existir sem qualquer referência aos contratos.

Evite referências circulares entre os módulos principais e fundamentais



4. Recomendações extras:

Os módulos principais não devem ter telas front-end: se você estiver implementando um serviço, talvez queira adicionar algumas telas para fazer testes de unidade. Mas, como desenvolvedor, depois de testar seu código, você deve se livrar das telas de teste. Se, por algum motivo, essas telas de teste forem úteis para suportar qualquer tipo de teste de regressão ou teste de BDD , elas devem ser movidas para um módulo de teste do usuário final. Um dos principais perigos de manter telas de teste em seus módulos principais é que elas tendem a ser telas anônimas para fins de teste e, se terminarem em produção, abrem uma violação de segurança.


Todas as entidades devem ser expostas como somente leitura: esta recomendação garante que você não está permitindo que os consumidores usem ações grosseiras para criar, atualizar ou excluir registros no banco de dados diretamente, porque o serviço principal deve abstrair as transações de negócios, cuidando de validações, normalizações e efeitos colaterais, como auditoria ou integração com outro sistema. Implemente todas as suas transações comerciais como ações públicas, para expor aos consumidores serviços seguros e corretamente abstraídos.


Evite a lógica de negócios na camada de base: às vezes, as pessoas tendem a implementar regras de negócios nesta camada, mas deve ser agnóstico de negócios garantir que ela possa ser reutilizada em qualquer domínio de aplicativo.


Não adicione entidades de negócios centrais na camada de base: novamente, para serem agnósticos de negócios, os módulos de base não devem ter entidades relacionadas aos negócios. Eles podem, no entanto, ter entidades não comerciais para oferecer suporte a alguns requisitos não funcionais de seus aplicativos. Por exemplo, se você precisa criar um serviço genérico para auditar todas as suas transações, você pode criar uma entidade de auditoria. A auditoria em si não é assunto da sua empresa; o negócio da sua empresa é vender um produto ou provisionar um novo cliente ou alterar um contrato.


Composição do aplicativo usando a tela de arquitetura

Antes de passar pela composição da aplicação, uma rápida divulgação: neste contexto, “aplicação” não tem o mesmo significado que costumamos dar em um contexto de negócios.

Na OutSystems, usamos o termo "aplicativo" para se referir ao conjunto de módulos definidos no Service Studio - ambiente de desenvolvimento OutSystems - que constituem a unidade de implantação mínima para LifeTime - console OutSystems para gerenciar todos os seus ambientes, aplicativos, usuários de TI, segurança, e o ciclo de vida do aplicativo. Dito isso, o que você promove do Desenvolvimento à Qualidade e da Qualidade à Produção, não são módulos únicos, mas aplicações.

Para identificar a qual camada o aplicativo corresponde, você deve procurar a camada superior dos módulos dentro do aplicativo, ou seja, se a camada superior for um módulo de usuário final, por exemplo, então este é um aplicativo de usuário final.



Composição do aplicativo usando a tela de arquitetura
Agora, com os aplicativos instalados, você deve seguir um conjunto de regras para garantir uma arquitetura correta.

Regra nº 1: Comece com as diretrizes da tela de arquitetura para módulos
Coloque seus módulos em camadas corretamente seguindo as diretrizes definidas acima.

Regra nº 2: Isole serviços comuns
Quando os módulos são colocados corretamente no lugar, é hora de ir atrás dos aplicativos. Use os mesmos princípios aqui. Portanto, se você tiver um módulo em “aplicativo de usuário final 2” consumindo um módulo em “aplicativo de usuário final 1”, deve isolar o aplicativo de núcleo comum para evitar dependências. Dessa forma, você oferece suporte a ambos os aplicativos. No minuto em que você deseja compartilhar algo, é necessário isolar os serviços comuns em aplicativos comuns.

Regra nº 3: Não misture proprietários
Ter mais de um proprietário em um aplicativo resulta em um gerenciamento de implantação complexo, pois a responsabilidade pelo que foi alterado torna-se incerta. Promover a propriedade é a chave. Se for impossível concentrar a propriedade de um aplicativo, considere dividi-lo de forma que a propriedade seja claramente definida.


Regra nº 4: Não misture patrocinadores
Assim como os proprietários, os patrocinadores têm demandas e bases diferentes. Vejamos um exemplo: imagine um portal que permite a simulação de diferentes ramos de negócios de seguros. Se todas as linhas de negócios estiverem sob a mesma aplicação, qualquer alteração feita em uma (por exemplo, no automático) não pode ser independente da outra. Portanto, a linha de negócios mais lenta ditará o ciclo de lançamento.

Ao criar aplicativos separados por linha de negócios, cada um pode determinar o ritmo de cada entrega. Uma vez que isso esteja claro, você deve identificar e isolar tudo o que é compartilhado entre os diferentes patrocinadores e o que deve ser administrado em conjunto.
 
Gostou do artigo e quer saber mais sobre tecnologia? Então siga os nossos perfis no Facebook, Twitter e LinkedIn !