ASP .NET Core - Implementando Clean Architecture - II

 Hoje vamos continuar a apresentar os conceitos e princípios que nos ajudam a implementar uma arquitetura aderente aos princípios da Clean Architecture.

Continuando o artigo anterior vamos prosseguir definindo os projetos  Domain, Application e Infra.

Definindo o Domain e o Contexto

Vamos criar um projeto do tipo Class Library (.NET Core) em nossa solução CleanArch.

Para isso clique com o botão do mouse sobre a solução e selecione Add-> New Project;

Escolha o template Classe Library(.NET Core) a seguir informe o nome CleanArch.Domain e clique em Create;

No projeto criado vamos remover o arquivo Class1.cs e alterar o <TargetFramework> do arquivo de projeto para net5.0:

Vamos criar uma pasta chamada Entities neste projeto e nesta pasta criar a classe que representa o nosso modelo de domínio e que a titulo de exemplo será a classe Product:

    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public decimal Price { get; set; }
    }

A classe Product foi definida com apenas 4 propriedades e sem nenhum comportamento. É uma classe anêmica que em um projeto mais aderente à Clean Architecture deveria ser mais expressiva.

Como neste artigo eu quero mostrar basicamente como criar um projeto usando a estrutura de um projeto aderente à Clean Architecture, para deixar o exemplo mais simples, vou trabalhar com classes anêmicas, mas isso em um projeto de produção não seria feito assim, deveríamos definir também os comportamentos e usar apenas o modificador get.

Vamos criar agora um novo arquivo de contexto de forma a podermos atualizar o banco de dados usado pela aplicação com este novo modelo de domínio.

Para isso vamos criar um novo projeto do tipo Class Library(.NET Core) na solução chamado CleanArch.Infra.Data da mesma forma que criamos o projeto CleanArch.Domain. Lembrando de alterar o TargetFramework para net5.0.

Após criar o projeto vamos incluir uma referência neste projeto para o projeto CleanArch.Domain via menu suspenso Add->Project Reference e aproveitar e incluir uma referência no projeto CleanArch.UI.MVC ao projeto CleanArch.Domain.

Agora no  CleanArch.Infra.Data vamos criar uma pasta chamada Context e criar nesta pasta a classe ProductDbContext que representa o arquivo de contexto para o nosso domínio Product.

Mas antes precisamos incluir no projeto as referências ao Entity Framework Core:

Após isso podemos criar a classe de contexto:
public  class ProductDbContext : DbContext
{
   public ProductDbContext(DbContextOptions options) : base(options)
   { }
  public DbSet<Product> Products { get; set; }
}

A seguir vamos configurar este novo Contexto no arquivo Startup do projeto CleanArch.UI.MVC.

Inclua o trecho de código abaixo no método ConfigureServices da classe Startup:

  ...
  services.AddDbContext<ProductDbContext>(options =>
         options.UseSqlServer(
           Configuration.GetConnectionString("ProductConnection")));
...

E agora precisamos definir no arquivo appsettings do projeto a string de conexão definida como ProductConnection :

Com isso podemos aplicar o Migrations usando os comandos:

Após gerar o arquivo de script XXXXXX_InicialProducts na pasta Migrations do projeto e após o comando update-database teremos o banco de dados ProductDB e a tabela Products criadas no SQL Server.

Aqui podemos aproveitar e incluir alguns dados para teste na tabela Products.

Podemos fazer isso usando uma instrução SQL Insert Into no SQL Server Management Studio:

Executando esta consulta e abrindo a tabela Products veremos os dados inseridos:

Vamos agora criar o projeto Application.

Criando o projeto Application

Vamos criar o projeto do tipo Class Library (.NET Core) chamado CleanArch.Application na solução da mesma forma que fizemos anteriormente, alterando o TargetFramework para net 5.0.

Após criar o projeto vamos incluir uma referência neste projeto para o projeto CleanArch.Domain via menu suspenso Add->Project Reference.

Neste projeto vamos definir os serviços, as interfaces e as view models. Assim vamos criar 3 pastas neste projeto:

  1. Interfaces
  2. Services
  3. ViewModels

Vamos começar definindo as ViewModels.

Uma ViewModel  representa os dados que você deseja exibir em sua view ou página, seja usado para texto estático ou para valores de entrada (como caixas de texto e listas suspensas) que podem ser adicionados ao banco de dados. É algo diferente do seu modelo de domínio. É um modelo para a a view. Em outras palavras, ele cria uma máscara para os modelos de domínio.

Vamos criar a classe ProductViewModel na pasta ViewModels e definir apenas a propriedade para obter uma lista de produtos do banco de dados conforme o código abaixo:

using CleanArch.Domain.Entities;
using System.Collections.Generic;
namespace CleanArch.Application.ViewModels
{
    public class ProductViewModel
    {
        public IEnumerable<Product> Produtos { get; set; }
    }
}

A seguir na pasta Interfaces vamos criar a interface IProductService que vai atuar como um contrato para a funcionalidade que estamos implementando.

using CleanArch.Application.ViewModels;
namespace CleanArch.Application.Interfaces
{
    public interface IProductService
    {
        ProductViewModel GetProdutcs();
    }
}

Quando o método GetProducts() for implementado, ele retornará uma lista de produtos, e ele só conhece o ViewModel, não sabe nada do modelo de domínio Product, então estamos abstraindo a entidade central fazendo isso, ao invés de ter tudo em um só lugar.

Antes de definir a implementação para IProductService, temos que definir uma maneira de obter os dados do banco de dados. Para fazer isso, podemos usar o Entity Framework diretamente, mas vamos usar o padrão Repository para separar a lógica do negócio e as camadas de acesso a dados em nosso aplicativo.

Então vamos criar a interface do repositório no projeto Domain.

Crie uma pasta chamada Interfaces no projeto Domain e a seguir crie a interface IProducttRepository nesta pasta.

using CleanArch.Domain.Entities;
using System.Collections.Generic;
namespace CleanArch.Domain.Interfaces
{
    public interface IProductRepository
    {
        IEnumerable<Product> GetProducts();
    }
}

Neste momento se o projeto MVC, ou a camada de apresentação (que não tem nenhuma ideia sobre a entidade de domínio Product) solicitar uma lista de livros, ela vai precisar falar com ProductService (que nós não implementamos ainda, usando IProductService), e ProductService precisa obtê-lo a partir do repositório ProductRepository (que também não implementamos ainda, usando IProductRepository)

Então, vamos implementá-los. Começando com ProductRepository.

Obs: O padrão de projeto Repository medeia entre o domínio e as camadas de mapeamento de dados usando uma interface semelhante a uma coleção para acessar os objetos do domínio. Em outras palavras, podemos dizer que o padrão Repositório atua como um intermediário ou camada intermediária entre o restante do aplicativo e a lógica de acesso aos dados.

Vamos implementar a classe concreta ProductRepository que define o padrão Repository no projeto CleanArch.Infra.Data.

Crie uma pasta Repositories neste projeto e a seguir crie a classe ProductRepository com o código a seguir:

Note que injetamos uma instância do contexto para a seguir ter acesso a entidade Products que esta mapeada para a tabela Products.

Agora podemos implementar o serviço ProductService no projeto Application.

Para isso crie a classe ProductService na pasta Services do projeto e inclua o código abaixo:

Observe que injetamos uma instância da nossa implementação do padrão Repository para acessar o método GetProducts() a partir do Repositório.

A estrutura atual do nosso projeto é vista na figura :

E as referências entre os projetos esta definida assim:

  1. CleanArch.Application  depende de CleanArch.Domain
  2. CleanArch.Infra.Data  depende de CleanArch.Domain
  3. CleanArch.UI.MVC  depende de CleanArch.Infra.Data
  4. CleanArch.Domain não depende de ninguém

Agora precisamos tratar da implementação do projeto CleanArch.Infra.IoC, que nos ajudará a conter e separar as dependências.

Faremos isso na próxima parte do artigo.

"Se esperamos em Cristo só nesta vida, somos os mais miseráveis de todos os homens."
1 Coríntios 15:19

Referências:


José Carlos Macoratti