Arquitetura de Software e nuances "ricas" no mundo do desenvolvimento

Arquitetura de Software e nuances "ricas" no mundo do desenvolvimento

A Arquitetura de Software é uma disciplina que envolve a organização e o design estrutural de um sistema de software. Ela abrange a definição de componentes, interfaces, padrões de comunicação e decisões de alto nível que afetam a funcionalidade, desempenho, escalabilidade, manutenção e qualidade geral do sistema.

As melhores práticas em arquitetura de software incluem o uso de padrões de projeto, princípios de design como a separação de preocupações, modularidade e coesão, bem como a consideração de atributos de qualidade, como segurança, usabilidade e desempenho.

A Arquitetura de Software desempenha um papel fundamental no dia a dia do desenvolvimento de software, fornecendo um guia estrutural para os desenvolvedores e ajudando a garantir a construção de sistemas robustos e sustentáveis.

Referências científicas relevantes incluem o livro Software Architecture in Practice de Len Bass, Paul Clements e Rick Kazman, além do artigo A Survey of Architectural Approaches for Managing Uncertainty in Software-Intensive Systems de Paris Avgeriou e Uwe Zdun.


Clean Architecture

A Arquitetura Limpa (Clean Architecture) é um conceito de desenvolvimento de software que foca na criação de código modular e de simples manutenção. A estrutura possibilita ao programador criar uma base de código que seja de fácil compreensão, teste e ampliação. Ela ressalta a segregação da essência primordial do negócio da aplicação dos pormenores da infraestrutura.

A arquitetura limpa é essencial para profissionais que já atuam ou querem ingressar no mercado, como arquitetos de software, analistas de sistemas, designers de sistemas, gerentes de software e programadores.

Devido à natureza de crescimento dos sistemas, é crucial adotar certos padrões para evitar que se tornem caóticos. Com essa preocupação em mente, engenheiros de software sêniores compilaram um conjunto abrangente de diretrizes necessárias para desenvolver um código robusto e resiliente às mudanças. Esse conjunto de princípios é amplamente conhecido na comunidade de desenvolvedores pelo acrônimo SOLID.

O padrão para esta estrutura de projeto procura aderir aos princípios do SOLID: S (Princípio da Responsabilidade Única), I (Segregação de Interfaces) e D (Inversão de Dependência, não a ser confundido com Injeção de Dependência), separando a camada de persistência de dados do restante da aplicação. Isso resulta em um desacoplamento efetivo, permitindo maior flexibilidade e manutenção no código.

Na figura a seguir é possível ver a ideia que cada camada da arquitetura de um software tem como responsabilidade para orquestrar seu devido papel. A camada mais profunda, a entidade, é um conjunto de atributos e regras relacionados e um atributo é, basicamente, um valor de campo. Há atributos específicos da fonte de dados e o significado dos atributos existentes pode ser diferentes, de acordo com cada conjunto de dados.

Neste artigo, usaremos como mapeador de objeto relacional de banco de dados o TypeORM para Node.js, o qual pode ser usado com TypeScript e JavaScript (ES5, ES6, ES7, ES8), para fornecer recursos que usa bancos de dados.

A camada de entidade da aplicação

Na camada mais interna, ou seja, a mais abstrata, criaremos uma entidade chamada User para representar as regras de negócio da nossa entidade, com suas propriedades e atributos. Para exemplo sobre regras relacionadas à entidade, vamos definir um método (linha 26) que retorna a URL do Avatar do usuário, verificando se a imagem foi salva no AWS S3 ou em disco (pasta) no servidor.

Outra regra de negócio nesta entidade está relacionada ao atributo senha do usuário, a qual não pode ser exposta (linha 41) em nenhum tipo de ‘select’ de banco de dados.

Como esta classe tem somente a responsabilidade de cuidar do Usuário, ela respeita o primeiro princípio do SOLID (S), que diz que um módulo deve ser responsável por um, e apenas um, ator.

src/modules/users/infrastructure/typeorm/entities/User.ts


A camada de domínio da aplicação

Mais para fora, temos a camada de domínio, onde armazenamos a lógica da aplicação, ou seja, as regras de negócios da solução ou da empresa, também chamada por alguns de casos de uso. Os objetos dessa camada contêm as informações e a lógica para manipular esses dados, que são específicos do próprio domínio e são independentes dos processos de negócio que acionam essa lógica, além de serem totalmente alheios à camada de aplicação.

As alterações não devem impactar as entidades e os objetos que orquestram o fluxo de dados de entrada e saída destas entidades, as direcionam a usarem seus conjuntos de regras de negócios a fim de alcançarem os papéis que aquele módulo foi desempenhando a executar.

Primeiro, vamos criar uma interface que mapeia o objeto da entidade User.

src/modules/users/interfaces/objects/IUserObject.ts

Depois, vamos criar uma interface que mapeia o objeto da nossa camada de domínio, ou seja, do nosso caso de uso, onde estão nossas regras relativas ao negócio da aplicação.

src/modules/users/interfaces/classes/IUserRepository.ts


Por fim, vamos criar nosso caso de uso, onde armazenaremos nossas regras relativas ao negócio da aplicação, ou seja, à camada de domínio. Não esperamos que mudanças nesta camada afetem as entidades. Também não esperamos que essa camada seja afetada por alterações nas externalidades, como o banco de dados, a interface do usuário ou qualquer uma das estruturas comuns.

Esta camada é isolada de tais preocupações, como já dito anteriormente, no entanto, esperamos que as mudanças na orquestração da aplicação afetem os casos de uso. Se os detalhes de um caso de uso mudarem, algum código nesta camada certamente será afetado.

src/modules/users/infrastructure/typeorm/repositories/UsersRepository.ts

Observe que todos os métodos implementados neste caso de uso, ou seja, nesta camada de domínio, relativas às regras de negócios da aplicação, são uma extensão da interface criada no arquivo anterior.

O próximo passo é implementar um serviço que chama os métodos que irão se comunicar com o banco de dados e efetivamente manipular os dados, aqui, executamos o Princípio de Inversão de Dependência do SOLID. A dependência é injetada à classe que a utiliza, ou seja, obrigatoriamente ela precisa ser externa a essa classe. Observe a figura a seguir:

Inversão de Dependência


A classe a seguir será injetada respeitando o princípio da inversão de dependência para que possa ser chamada pela camada mais de fora, ou seja, a camada de adaptadores de interface.

src/modules/users/services/CreateUserService.ts


É nesta implementação que conseguimos perceber a inversão de dependência, sendo esta classe responsável por  acessar as operações da camada de domínio, aquela que tem as operações com o banco de dados. Perceba que antes de criarmos um usuário (linha 28), nós checamos se o usuário já existe (linha 23).

Métodos estes implementados na sua devida camada de domínio, observando o Princípio da Aberto/Fechado do SOLID, o qual diz que classes, métodos, módulo, entre outros, devem estar abertos para extensão, mas fechados para modificação.

A camada de apresentação da lógica

O próximo passo para que nosso modelo de arquitetura limpa ou arquitetura rica esteja quase completo em seu conceito, é implementar a camada de adaptadores de interface, ou a camada de apresentação da lógica. A aplicação nesta camada é um conjunto de adaptadores que transformam os dados de um formato mais adequado para os casos de uso e entidades em um formato mais adequado para uma entidade externa, como o Banco de Dados ou a Web. Por exemplo, é nesta camada que toda a estrutura de arquitetura MVC de uma interface gráfica está contida.

A partir de agora, podemos utilizar nosso controlador para chamar os serviços relativos à camada de domínio, onde se encontram nossas regras de negócio, ou seja, nossos casos de uso.

src/modules/users/infrastructure/http/controllers/UserController.ts

Perceba que é aqui que chamamos o método dependente (herdado) da camada de domínio (linha 24) e então o executamos (linha 25), respeitando o princípio na inversão de dependência.

A camada de apresentação da interface

A camada de infraestrutura da interface lógica da aplicação é o local onde se encontra nossa interface com o usuário e será demonstrada no código abaixo, com a chamada da rota de ‘store’ (linha 24), que é responsável por criar/armazenar um novo usuário no banco de dados.

src/modules/users/infrastructure/http/routes/users.routes.ts


Esta camada, por ser a mais externa, em geral é composta de estruturas e tecnologias como o banco de dados e estrutura da Web. A Web, assim como o banco de dados, são “detalhes”, por isso mantemos essas coisas do lado de fora, onde menos danos podem ser causados.

A seguir você pode conferir como ficou a organização da minha estrutura de diretórios para esta arquitetura.




Conclusão

Seguir essas diretrizes básicas não é complicado e evitará muitos problemas no futuro. Ao dividir o software em diferentes camadas e aderir à regra de dependência, você criará um sistema que é naturalmente testável, com todas as vantagens que isso acarreta. Se alguma das partes externas do sistema se tornar ultrapassada, como o banco de dados ou o framework Web, você poderá substituir esses componentes desatualizados com o mínimo de dificuldade.

A arquitetura limpa (Clean Architecture) juntamente com os princípios do design de classes orientado a objetos (SOLID), irão te fornecer um arcabouço de regras e práticas seguidas pelos melhores engenheiros de software na criação de uma estrutura de sistema consistente e fácil de ser escalável.

Se você deseja ver o código completo desta aplicação, acesse-o através do meu repositório.

Sucesso!

💡
As opiniões e comentários expressos neste artigo são de propriedade exclusiva de seu autor e não representam necessariamente o ponto de vista da Revelo.

A Revelo Content Network acolhe todas as raças, etnias, nacionalidades, credos, gêneros, orientações, pontos de vista e ideologias, desde que promovam diversidade, equidade, inclusão e crescimento na carreira dos profissionais de tecnologia.Jueves, 5 de enero