Muito tem se falado sobre frameworks e sobre a grande promessa de trazer ganhos de produtividade, qualidade e performance. Porém, quantos de nós já iniciamos projetos usufruindo de frameworks com o objetivo de alcançar melhores resultados de maneira rápida e eficiente, mas ao final caímos nos mesmos problemas que tínhamos antes do uso destes?
Prazos estourando enquanto o mercado muda drasticamente suas regras, mudanças no escopo que precisam ser feitas para minimizar os impactos, enquanto isso, ferramentas escolhidas vão se tornando depreciadas e os testes não garantem o funcionamento ou até mesmo nem existem, chegando ao ponto de um sistema nunca ir para produção porque se tornou defasado antes mesmo de algum dia ter ficado estável. Um cenário muito comum na indústria de desenvolvimento de software.
Qual o melhor framework?
Quando estamos por iniciar um novo projeto, seja ele de uma aplicação web, mobile ou até mesmo em linha de comando, uma das primeiras perguntas que se passam na cabeça do desenvolvedor é: “qual framework vou usar?”.
A escolha de um framework sempre vai variar de acordo com o escopo do projeto que vamos desenvolver, como por exemplo, a plataforma em que esse sistema irá rodar, o suporte desse framework, a mão de obra envolvida, o tempo e o dinheiro que temos para investir.
Porém, o que percebemos é que esses são parâmetros que têm maior importância quando estamos discutindo a viabilidade do projeto com executivos e pessoas de negócio. Quando essa discussão chega a um time de desenvolvedores que irá trabalhar no projeto, parâmetros como a plataforma, o suporte e o investimento perdem seu valor perante à um novo requisito: o fanatismo.
Observando as discussões mais recentes dentro de comunidades, vamos encontrar uma trincheira de guerra com desenvolvedores vestindo fardas com as logo marcas de seus frameworks e ferramental favoritos. A maioria das discussões não segue um embasamento orientado ao escopo do projeto, senão, a religiosidade em cima de uma ferramenta que agrada aos seus olhos.
Entretanto, escolher um framework que melhor atende aos requisitos do projeto nunca vai ser uma tarefa fácil e sempre terá um percentual de chance de falhas no futuro, pois, o escopo quase sempre muda e essas mudanças sempre vão exigir adaptações à medida que o produto evolui.
E é trabalho do arquiteto e/ou líder de desenvolvimento garantir que esse sistema está preparado para receber tais mudanças com o menor esforço possível; principalmente, se estamos falando de produtos em startups, que precisam validar hipóteses em um curto período de tempo.
Frameworks são ferramentas extremamente poderosas, desde que usadas com sabedoria e responsabilidade. E para aproveitar ao máximo os benefícios que um framework pode trazer para o seu projeto, precisamos, antes de mais nada, separar as responsabilidades e entender onde o código do seu produto termina e onde começa o trabalho que o framework deve fazer.
Código Agnóstico a Framework
Código agnóstico à framework é um mindset que vem sendo bastante discutido em comunidades de desenvolvimento e arquitetura de software. E para se atingir esse mindset, primeiro precisamos entender profundamente os conceitos por trás dele, para assim, começar a enxergar os benefícios por trás dessa abordagem ao desenhar novos sistemas.
Software Design Principles
Princípios de Design de Software não são uma novidade; eles estão em quase todos os livros de arquitetura e engenharia de software, e são também a base para diversos padrões utilizados na indústria. Porém mesmo não sendo uma coisa nova, muitos arquitetos e líderes de desenvolvimento ainda se esquecem, ou se recusam a se apoiar nestes princípios, de maneira e novos sistemas acabam perdendo em qualidade, manutenibilidade e escalabilidade. E essa negligência é o primeiro ponto de falha que nos leva à escrever código acoplado e específico para framework.
Separation of Concerns ou SoC (em português: separação de conceitos) é o primeiro e, sem dúvida, o princípio mais importante quando falamos de código agnóstico à frameworks, pois ele delimita as responsabilidades de cada parte do seu software – e isso inclui o código do seu produto (que contém suas regras de negócio) e o framework que você escolheu para se apoiar no desenvolvimento.
Dentro deste conceito, temos duas premissas básicas: a máxima coesão e o menor acoplamento possível. O que significa que o seu código deve conter somente o que ele precisa para atender aos requisitos do negócio, mas ao mesmo tempo ter o mínimo (se possível nenhum) acoplamento com o meio externo a aquele requisito, que neste cenário, seria o framework que você adotou.
Entendendo bem os limites de onde começa e termina o seu software, baseado nas funcionalidades oferecidas pelo framework que você escolheu para desenvolver seu projeto, começamos a aprofundar mais no desenho técnico, agora pensando de maneira mais granular, levando em consideração os requisitos do negócio.
Então, utilizando-se do mesmo princípio de separação de conceitos, mas agora introduzindo uma nova abordagem, o Domain Driven Design, podemos começar a modularizar e definir os seus domínios.
Domain Driven Design
Domain Driven Design ou DDD (em português: Desenho Orientado a Domínio ou Projeto Orientado a Domínio) é uma abordagem, considerada por alguns como uma metodologia, para modelagem de sistemas onde a maior preocupação deixa de ser o código que vai ser escrito, passando a focar no problema a ser resolvido, também conhecido como domínio.
Ganhando entendimento sobre o negócio, junto ao que chamamos de Domain Experts, que são os principais stakeholders e possíveis usuários do software, vamos começar a encontrar os domínios deste negócio. Eles irão aparecer à medida que as discussões forem ganhando detalhamento e profundidade.
Para exemplificar, trago aqui um problema comum: um e-commerce. Este é negócio bastante complexo que tem como problema base uma pessoa A vender algo para uma pessoa B.
Apesar de, a princípio, parecer simples por ser um único problema, ao começarmos a análise das necessidades envolvidas de ambas as partes, podemos identificar alguns dos domínios, onde um vendedor A precisa montar um Catálogo, precisa receber os Pagamentos pelos produtos e ele precisa de uma solução de Logística para enviar os pedidos.
Enquanto isso, um comprador B precisa conseguir realizar seus Pedidos e muitas vezes ele vai querer Cupons de Desconto para economizar. Portanto somente em uma breve sentença de detalhamento do fluxo padrão de um e-commerce, podemos identificar pelo menos cinco domínios de nosso negócio.
Component-based Architectures
Identificados os domínios, agora podemos começar a pensar na melhor maneira de modularizar, ou quebrar nossos sistemas em componentes de única responsabilidade dentro do nosso sistema. Essa componentização poderá ser feita baseada não somente nos domínios, mas também em domínios de suporte, como por exemplo, serviços de comunicação, persistência, fila, mensageria, etc.
Após definidas as responsabilidades dos componentes que compõem esse sistema, precisamos definir os contratos de intercomunicação entre os domínios do sistema e isso poderá ser feito através de APIs, expondo para fora do componente as funcionalidades que forem necessárias e que outros componentes dependam através serviços delimitados e bem definidos.
Por fim, após definidos e modelados os contratos de intercomunicação, podemos expor os serviços necessários para integrar o framework ao código de seu domínio. Dessa forma, caso você necessite de trocar de framework, você precisará reescrever apenas estes serviços.
Trade-offs
Fazer código agnóstico à framework não é uma tarefa fácil. Você precisa conhecer muito bem o seu domínio e o framework que está escolhendo para desenvolver sua solução, para entender onde inicia e termina as responsabilidades da sua aplicação.
Porém, se apoiando em alguns conceitos básicos de padrões de projeto e padrões arquiteturais esse processo pode ficar menos doloroso, além de trazer uma série de vantagens à longo prazo.
Arquiteturas de software baseadas em componentização trazem muitos benefícios, dentre eles o reaproveitamento de código, a extensibilidade, escalabilidade à nível de requisitos de domínio, facilidade e garantia de funcionamento através de testes objetivos, e a redução de side-effects, pois o seus componentes dependem minimamente uns dos outros.
Por fim, encapsular toda a complexidade de seu componente e expor suas APIs através de serviços garantem o mínimo acoplamento possível. Além de delimitar os contextos do seu domínio, tornando muito mais fácil o trabalho colaborativo.
Outro ponto é que, ao colocar o código de integração de seu framework em serviços, isso permite que você se mova rapidamente quando perceber que aquela ferramenta já não te serve mais.
***
Artigo publicado na revista iMasters, edição #28: https://issuu.com/imasters/docs/imasters_28_v5_issuu