Uma proposta de arquitetura simples, sofisticada e elegante para projetos de software, by tio Bob.
https://medium.com/@diego_moura/arquitetura-limpa-big-picture-fc176e5b194c
Prédios têm uma estrutura física óbvia, em pedra ou concreto, com arcos altos ou largos, grande ou pequena, magnífica ou mundana. Essas estruturas têm poucas escolhas além de respeitar os limites impostos pela gravidade e pelos seus materiais. Por outro lado — exceto no sentido de seriedade — o software tem pouco tempo para a gravidade. E do que o software é feito? Diferente dos prédios, que podem ser feitos de tijolos, concreto, madeira, aço e vidro, o software é feito de software. — Robert C. Martin (Uncle Bob) – Clean Architecture
Overview
Geralmente, quando ouvimos a palavra arquitetura no contexto do software, é natural imaginarmos algumas coisas como por exemplo, caixinhas conecatadas à outras caixinhas através de arestas que podem representar a direção da dependência entre elas ou, simplesmente dizer que determinada caixinha conhece outra, onde o relacionamento entre essas caixinhas poderão descrever um comportamento, um caso de uso, ou uma simples regra de negócio. Existem muitas formas de expressar a arquitetura de um software, ou pelo menos parte dela, seja através de diagramas, mapas mentais, modelos relacionais, etc. No entanto, independente da representação ou esboço pensado, o principal objetivo da arquitetura é “organizar a casa”, e ajudar os desenvolvedores a mantê-la “limpa”.
Para isso Uncle Bob ou tio Bob, nos presenteou com um excelente conteúdo – o livro Clean Architecture. Que iremos tratar e discutir alguns conceitos centrais nesse artigo.
O valor da Arquitetura
Ao iniciar um novo projeto, muitas vezes o desenvolvedor pressionado pelo seu gestor, ou por outras unidades (produto, comercial, etc), tende a fazer e propagar uma estimativa baseada em uma análise leviana em relação ao escopo/prazo do projeto, de modo a “atender” as expectativas comerciais, e pra isso ele acaba não considerando ou deixando de dar o devido valor e importância da arquitetura no início do desenvolvimento do software.
O que muitas vezes cai no desconhecimento dos gestores ou mesmo do próprio desenvolvedor, é que um projeto iniciado sem uma boa arquitetura, pode até funcionar muito bem, mas certamente será mais difícil de mantê-lo e escala-lo quando necessário.
O custo de um projeto iniciado sem uma boa arquitetura, que o permita crescer adequadamente quando as novas necessidades de implementações não previstas no escopo aparecerem, será muito maior do que um software pensado e desenvolvido dentro de uma arquitetura que o suporte.
A arquitetura deve se preocupar em garantir que o desenvolvimento e manutenção de um software use menos recursos humanos quanto possível. Deixando as opções abertas para novas implementações pelo maior tempo possível, assim como a arquitetura também deve se preocupar com testabilidade, manutenibilidade e deployment, para em casos onde houver a necessidade de manutenções, que essa tarefa não seja um parto na vida do desenvolvedor, e nem se torne eterna.
Design e Arquitetura
Quando refletimos um dado projeto, é normal transcendermos dois conceitos que em suma representam a mesma coisa, mas para visões diferentes do software. Me refiro aos os termos – Design e Arquitetura.
Comumente o termo Arquitetura é utilizado em contextos de mais alto nível. Como assim? Imagine o projeto de uma casa, a arquitetura nesse contexto irá se preocupar com as características estruturais, como a forma da casa, a distribuição dos cômodos, as elevações, os limites entre cada cômodo e etc. E ainda em relação a casa, o design se preocuparia com detalhes de mais baixo nível, por exemplo com a distribuição da rede elétrica, onde ficarão as tomadas em cada quarto, os interruptores, quais lâmpadas eles controlam, quais são os detalhes da cozinha, onde ficarão os armários, fogão, geladeira e etc.
Logo, a arquitetura se preocupa com componentes de nível mais alto ou componentes estruturais, e como eles irão se comunicar entre si (as camadas do software). E o design se preocupa em definir como esses componentes serão organizados e implementados no nível do código.
OBS: Neste artigo não irei me aprofundar nos conceitos relacionados a design em si. Falarei sobre esse assunto em um novo post que farei em breve a respeito dos princípios S.O.L.I.D também propostos por Robert C. Martin (Uncle Bob).
Componentes
Que diabos são esses tais componentes? Pense em componentes como sendo a menor parte implantável de um software. Em Java os componentes são os arquivos JARs, em DotNet são as DLLs, em linguagens interpretadas, são agregações de código fonte. Os componentes podem ser organizados em um ou vários arquivos, podem ser utilizados juntos ou separados, e carregados estaticamente ou dinamicamente.
Logo podemos concluir que a arquitetura é composta por componentes, e irá dizer como esses componentes serão distribuídos, organizados, implantados, carregados e como eles irão comunicar entre si.
Emfim… Arquitetura Limpa
Nos últimos anos, temos visto a emancipação de diversos “novos” padrões no que diz respeito a boas práticas de desenvolvimento de software, e isso leva em consideração a necessidade de se pensar uma boa arquitetura para o seu software. Claro que há casos onde abordagens como essa (Clean Architecture) ou outras arquiteturas derivadas do modelo em camadas como DDD, Arquitetura Hexagonal, DCI, BCE, etc… Não se aplicarão. Tudo dependerá de uma análise prévia em relação ao escopo do projeto que está sendo solicitado pelo cliente ou PO, pois dependendo do tamanho do escopo, ou complexidade, o arquiteto poderá definir estrategicamente qual o melhor padrão arquitetural para o sistema, sem que sua escolha seja um overkill ao utilizar arquiteturas mais complexas que exijam diversos níveis de abstrações, e camadas desnecessárias para o sistema solicitado. Fazer isso somente pelo fato de ser uma hype, ou “estar na moda”, te colocará em um perigo eminente. Não caia nessa armadilha! Fazer bem feito, exige conhecimento prévio de como fazer da forma certa. Reflita.
Mas voltando a falar sobre Arquitetura Limpa – Uncle Bob, propôs um modelo que basicamente lida com a separação de responsabilidades, divididas em camadas (o número de camadas ou abstrações, irão variar conforme a sua necessidade) afim de garantir que o sistema em questão seja: independente de framework, testável, independente de UI, independente de banco de dados, e independente de qualquer recurso externo. Logo, a arquitetura deve ser centrada nos casos de uso, e informar os desenvolvedores sobre o sistema e suas regras de negócio; Não sobre o framework ou banco de dados ali presente. Estes são apenas detalhes periféricos, e não devem estar acoplados à aplicação.
A Regra da Dependência
A principal regra no que diz respeito a Clean Achitecture é a Regra da Dependência. Em suma ela define que as dependências de código fonte devem sempre apontar apenas para dentro do círculo, sentido às políticas de alto nível (casos de uso e entidades — se você ainda não compreende bem os conceitos relacionados à politicas de alto e baixo nível, casos de uso, regras de negócios cruciais e específicas da aplicação, e entidades, sugiro que leia meu artigo Arquitetura de Software — Por onde começar? ). Quanto mais a dentro do círculo o elemento estiver, maior será o nível de abstração das políticas de negócio. Os círculos mais externos, consistem de implementações concretas de baixo nível.
Então, tendo como base essa definição, o vetor de dependências deverá sempre apontar para dentro do círculo, assim como as camadas de mais alto nível (Quanto mais no interior do círculo, maior o nível) não deverão conhecer as camadas de mais baixo nível (As camadas mais no exterior do círculo).
Uncle bob definiu 4 camadas bases — (Frameworks and Drivers, Adapters Interfaces, Use Cases, Entities) para o padrão proposto, no entanto não há nenhuma regra em relação a isso. O número de camadas poderá aumentar de acordo com a sua necessidade, desde que a Regra da dependência não seja violada.
Frameworks and Drivers
Esta camada é a camada mais externa da sua aplicação, nela estará contida implementações relacionadas ao framework utilizado, associações com banco de dados, setups de drivers ou recursos externos, etc. Todos esses detalhes deverão permanecer nessa camada mais externa, para que não causem mal algum à aplicação, e permaneçam desacoplados e distantes da camada de domínio.
Adaptadores de Interfaces
Os adaptadores de interface são responsáveis por implementarem a conversão dos dados para o formato mais adequado para as camadas de nível mais alto, na entrada, e para o formato mais conveniente, para as camadas de nível mais baixo, na saída. É nesta camada que existirão os presenters, views e controllers, que conterão a lógica de preparação dos dados através de estruturas simples de dados (DTO’s — Data Transfer Object) para retorna-los ao cliente, no formato desejado pela aplicação consumidora. Assim como para entregar a camada de caso de uso um DTO que faça sentido para esta camada, para que não seja, por exemplo um objeto da web sendo transferido para a camada de nível mais alto, visto que objetos da web carregam consigo propriedades e metódos restritos à camada de Frameworks & Drivers.
Casos de Uso
É nesta camada que iremos implementar as nossas regras de negócio específicas da aplicação. Nesta camada deverá conter a implementação de todos os casos de uso do sistema, e esta é única camada que conhece a camada mais interna da nossa aplicação, às Entidades, logo, é na camada de casos de uso que acontece a dança das entidades, onde através das entidades, o fluxo de controle deverá garantir que as regras cruciais de negócios, sejam alcançadas para os casos de uso.
Entidades
As entidades reúnem as regras cruciais de negócios. Em suma uma entidade pode ser entendida como uma classe ou objeto, que contem um conjunto de propriedades e funções que juntos, representarão as regras cruciais de negócios, ou simplesmente, classes que representam coisas que geram valor direto para a empresa. As entidades devem estar disponíveis para serem consumidas por toda aplicação. E não deve ser impactada quando houver mudanças operacionais (Quando uma interface ou caso de uso mudar por alguma razão).
Boundaries
Agora que temos visão das camadas e quais as responsabilidades de cada uma delas, precisamos entender como iremos comunica-las afim de não violar a Regra da Dependência. E a resposta para isso é: Respeitando os limites! Logo, o fluxo de controle deve ser pensado de forma a respeitar os limites imposto pela arquitetura, então, a camada de framework & drivers, não deve conhecer a camada de casos de uso, assim como á camada de casos de uso não deve conhecer nada sobre os adaptadores de interface.
Em suma, em casos onde a camada de casos de uso tivesse que executar alguma ação contra o banco de dados por exemplo, seria necessário realizar a inversão de controle (IoC — Inversion of Control), e para isso existem algumas formas prosseguir, uma delas e talvez a mais simples, é aplicando o Princípio da Inversão de Dependência (D.I.P — Dependency Inversion Principle), onde seria recebido no construtor da classe de nível mais alto, uma instância de um objeto de nível mais baixo, e o mesmo interagiria com a camada de nível mais baixo através dessa instância, para realizar algum procedimento ou ação, invertendo assim o controle de fluxo.
Outra técnica seria utilizando containers de injeção de dependência (DI Container), que em suma carregam as abstrações das dependências de código fonte ao executar à aplicação, em uma estrutura de grafo, respeitando o formato pelo qual dizemos a ele como determinada classe ou função deve ser instanciada, e a partir daí, ele se encarregará de injetar as dependências via método construtor, ou argumentos de funções, sempre que solicitado.
Conclusão
Como vimos em relação à arquitetura, precisamos antes de partir para implementação, analisar e aferir em relação ao projeto demandado, abstrair as regras de negócio, casos de uso, e entidades de domínio, para só depois definir qual o melhor padrão arquitetural, e como devemos iniciar o nosso software dentro de uma arquitetura condizente, que nos permita escalar e termos resiliência quando o software demandar mudanças ou potencial de escala.
Lembre-se sempre de manter as opções abertas ao iniciar um novo software, isolando o banco de dados, a web, o framerwork, e outros detalhes periféricos, para que não causem nenhum mal ao seu sistema. Eles não devem influenciar em nada quanto às regras de negócio, e políticas de alto nível.
Muitas vezes o que precisamos é bem mais simples do que queremos acreditar ser. E em casos como esse, não perca tempo com uma arquitetura em camadas com grandes níveis de abstração. Sempre reflita o sistema com bastante prudência ao planeja-lo, para não tornar seu projeto um boilerplate de maluco —Lembre-se, não é porque todos lhe disseram que é bom, que irá servir para você. Isto pode ser um overkill, se você não se conter amiguinho(a).
Bom é isso aí! Espero ter contribuido de alguma forma.
#happyCoding