Web APIs - Design Patterns, Serviços e Repositórios


Neste artigo vamos tratar da importância em usar padrões de projetos, serviços e repositórios com WEB APIs ASP .NET Core.

Tudo na vida tem um propósito, e, no desenvolvimento de software, usamos padrões para facilitar o desenvolvimento desacoplando o software e extraindo responsabilidades em classes separadas.

O objetivo e dar a cada classe do projeto uma única responsabilidade que é um dos pilares dos princípios SOLID. Isso vale para qualquer tipo de projeto mas neste artigo vamos focar nas Web APIs da ASP .NET Core.

Assim, para poder criar Web APIs robustas e de forma estruturada devemos usar padrões de projeto que vão nos conduzir ao princípio da responsabilidade única (SRP).

Podemos pensar nos padrões de projetos como ferramentas que são independentes da linguagem e que podem ser usados em diferentes aplicações e linguagens.

Vamos pensar no fluxo básico envolido em uma aplicação Web API:

Esse fluxo pode parecer complexo mas na verdade é uma abordagem modular e testável muito usada :

De maneira bem simples a requisição HTTP vem do Controller para a fonte de dados passando pelo Serviço e pelo Repositório.

Definindo o domínio

O domínio representa o assunto do projeto ou da Web API, ou seja, do que o projeto vai tratar.

Vamos supor que vamos tratar de gerenciar informações de produtos. Logo o domínio é o Produto.

Definindo o esquema do projeto usando o domínio produto podemos definir:

  1. A classe ProdutoController - Camada de apresentação

  2. A classe ProdutoService - Camada da lógica de domínio

  3. A classe ProdutoRepository - Camada de Acesso a dados

A figura a seguir mostra o esquema definido:

Temos aqui o que é conhecido por forte acoplamento que ocorre quando uma classe invoca outra classe que invoca outra classe.

Você sabe o que significa acoplamento ?

- Acoplamento é o nível de dependência/conhecimento que pode existir entre as classes;
- Uma classe com acoplamento fraco não é dependente de muitas classes para fazer o que ele tem que fazer;
- Uma classe com acoplamento forte depende de muitas outras classes para fazer o seu serviço;
- Uma classe com acoplamento forte é mais difícil de manter, de entender e de ser reusada;

Resumindo :  O ideal é ter um acoplamento fraco.

Assim temos que as classes:

Nosso objetivo é reduzir forte acoplamento e fazemos isso usando interfaces.

Assim podemos incluir duas interfaces em nosso projeto para reduzir o acoplamento entre as classes que estão fortemente acopladas.

  1. IProdutoService

  2. IProdutoRepository

A seguir vamos implementar essas interfaces nas classes ProdutoService e ProdutoRepository.

Mas como as interfaces fazem o desacoplamento ?

Como uma interface é apenas um contrato vazio que não contém código, uma classe que depende de uma interface depende apenas de seu contrato e não de sua implementação. Aplicar este princípio nos permitirá reduzir o forte acoplamento entre as classes.

Assim, após essa implementação, nossas classes usarão apenas outras interfaces, não mais uma implementação concreta. Isso pode ser representado da seguinte forma:

Como podemos ver no esquema anterior, a classe ProdutoController só conhece a interface IProdutoService e não sabe mais nada.

O mesmo acontece com a classe ProdutoService, sua consciência do sistema é limitada à interface IProdutoRepository.

E, melhor ainda, a classe ProdutoRepository é a única que sabe sobre a fonte de dados.

Usando interfaces em vez de classes concretas, quebramos as dependências diretas entre nossas implementações.

E como podemos fazer a vinculação entre as classes e as interfaces ?

Podemos fazer a vinculação entre as classes e as interfaces usando a injeção de dependência.

A injeção de dependência

Uma dos princípios básicos do paradigma da programação orientada a objetos, e, um dos pilares dos princípios SOLID é o princípio da inversão da dependência que diz que:

"Classes não devem depender diretamente de outras classes, mas 'depender de abstrações'."

Essa última frase leva à Inversão de Controle (IoC), o que significa que as dependências são gerenciadas em outro lugar. Em vez de criar tipos concretos que usam outros tipos, as classes só dependem de abstrações (interfaces) e dependências (implementações) são injetadas.

Para entender o princípio vejamos a classe Cliente:

    public class Cliente
    {
       Pedido meuPedido = new Pedido();
       public void ObterPedidos(Pedido pedido)
       {
           meuPedido.getPedidos(pedido);
       }
    }

Neste código a classe Cliente esta criando uma instância da classe Pedido e definindo o método ObterPedidos() que retorna os pedidos de um cliente usando o método getPedidos definido na classe Pedido.

Temos aqui uma violação do princípio da responsabilidade única ou SRP - Single Responsability Principle que diz o seguinte:  "Deve existir um e somente UM MOTIVO para que uma classe mude"

Portanto uma classe deve ser implementada tendo apenas um único objetivo.(uma única responsabilidade)

Quando uma classe possui mais que um motivo para ser alterada é por que provavelmente ela esta fazendo mais coisas do que devia, ou seja, ela esta tendo mais de um objetivo.

É o que ocorre com a classe Cliente, nela temos um forte acoplamento com a classe Pedido pois a classe Cliente possui a responsabilidade de criar uma instância da classe Pedido e usar um de seus métodos e isso não deveria ser responsabilidade da classe Cliente.

Dizemos que a classe Cliente esta fortemente acoplada a classe Pedido o que leva a que qualquer mudança feita na classe Pedido afete a classe Cliente.

Com esta abordagem temos os seguintes problemas:

Temos um problema e vamos resolvê-lo, não é mesmo ???  Como ???

Fazendo a inversão de controle na classe Cliente e tirando responsabilidades dela.

É aqui que entra a inversão de controle...

Vamos inverter o controle na classe Cliente e em vez de deixar a responsabilidade da criação da classe Pedido para a classe Cliente vamos dar a ela esta dependência.

É aqui que entra a injeção de dependência...

Vamos injetar a dependência na classe Cliente.

Então para resolver o problema da classe Cliente vamos fazer assim: Inverter o controle utilizando a injeção de dependência.

Aplicando isso ao nosso exemplo temos:

Na figura abaixo temos uma representação deste processo :

A maneira de implementar a inversão de controle chama-se injeção de dependência (DI) que nos trás os seguintes benefícios;

Em suma, a DI isola a implementação de um objeto da construção do objeto do qual ele depende.

Vale a pena fazer tudo isso ????

E quais os benefícios que essa abordagem trás ?

Muuitas...

Ela oferece a oportunidade de trocar uma fonte de dados com o mínimo ou nenhum impacto na lógica do domínio. (A troca pode até ser feita em tempo de execução.)

A mesma coisa vale para o Controlador e o Serviço, ajudando você a manter sua API pública saudável.

Por exemplo, poderíamos codificar duas classes que implementam o IProdutoRepository e então trocar nossa lógica de acesso a dados de um banco de dados relacional, como o SQL Server, para um Armazenamento de Tabelas do Azure, tão facilmente quanto mudar a ligação de injeção de dependência.

Ambas as implementações podem ser usadas ao mesmo tempo para diferentes propósitos em nosso programa, compartilhando a Lógica de Domínio enquanto persistem os dados em diferentes locais.

Idéia concreta: uma fonte de dados configurável para cada usuário, em tempo de execução.

Vimos assim a importãncia em conhecer e usar os seguintes padrões:

Portanto é importante conhecer e compreender e saber usar esses conceitos durante o desenvolvimento de seus projetos.

"E esta é a mensagem que dele(Jesus) ouvimos, e vos anunciamos: que Deus é luz, e não há nele trevas nenhumas."
1 João 1:5

Veja os Destaques e novidades do SUPER DVD Visual Basic (sempre atualizado) : clique e confira !

Quer migrar para o VB .NET ?

Quer aprender C# ??

Quer aprender os conceitos da Programação Orientada a objetos ?

Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ?

Referências:


José Carlos Macoratti