ASP .NET Core - Implementando Clean Architecture - I

Hoje vamos iniciar a implementação da Clean Architecture em uma aplicação ASP .NET Core MVC.

Continuando o artigo anterior hoje vamos iniciar a criação de uma aplicação ASP .NET Core seguindo as orientações e recomendações da Clean Architecture.

Vamos criar uma aplicação ASP .NET Core para vendas de produtos e para não tornar o artigo muito extenso iremos assumir algumas simplificações a nível de modelo de domínio e de regras de negócio.

Criando a solução e a estrutura do projeto no VS 2019 Community

Vamos iniciar criando uma Blank Solution chamada Shop.

A seguir vamos criar os projetos individuais na solução.

No menu File selecione Add -> New Project, selecione o template Class Library(.NET Core) e informe o nome Shop.Application;

A seguir vamos repetir o procedimento acima e criar os seguintes projetos todos do tipo Class Library(.NET Core):

Para criar o projeto Web selecione o menu File-> Add-> New Project e selecione o template ASP .NET Core Web Application e informando o nome Shop.Web.Mvc

A seguir escolha o template Web Application(Model-View-Controller) e clique em Create;

Ao final teremos a solução com 5 projetos conforme abaixo:

  • Shop.Application -  Interfaces, serviços e regras de negócio;
  • Shop.Domain - modelo de domínio;
  • Shop.Infra.Data - lógica de acesso aos dados, repositórios, Migrations;
  • Shop.Infrastructure.IoC - Injeção de dependência;
  • Shop.Web.Mvc -  Aplicação Web;

 

Obs: Você pode criar a estrutura das pastas na Solução e em cada pasta criar o respectivo projeto. Para simplificar eu criei os projetos diretamente na Solução.

Definindo o Domain

Vamos iniciar definindo as entidades no projeto Shop.Domain que representam o nosso modelo de domínio.

Vamos criar uma pasta chamada Entities e nesta pasta criar a classe Product que representa o nosso modelo de domínio:

Aqui estou usando um modelo totalmente anêmico com get/set públicos para simplificar o exemplo mas em um projeto de produção do mundo real essa abordagem não é aconselhada.

Definindo a lógica de acesso aos dados

Vamos iniciar a definição da lógica de acesso aos dados criando uma pasta Context no projeto Shop.Infra.Data e nesta pasta vamos criar a classe AppDbContext.

Antes de definir o código da classe vamos incluir neste projeto os seguintes pacotes:

Clique com o botão direito do mouse sobre Dependencies e selecione Manage Nuget Packages;

Selecione a guia Browse e instale cada um dos pacotes mencionados. Ao final teremos o resultado abaixo no projeto:

Obs: Note que estamos usando as versões estáveis e todos os pacotes tem a mesma versão.

Agora precisamos incluir uma referência aos projetos Shop.Domain  neste projeto.

Clique com o botão direito do mouse sobre o projeto e selecione Add-> Project Reference;

Selecione os projetos Shop.Domain e clique em OK;

Agora podemos definir o código da classe AppDbContext que representa o contexto da nossa aplicação onde definimos o mapeamento ORM e as opções para obter o provedor do banco de dados e a string de conexão.

Registrando as opções do Contexto no projeto MVC

Vamos agora definir as opções do contexto registrando o serviço e definindo o provedor do banco de dados usado e a string de conexão com o SQL Server no projeto Shop.Web.Mvc.

Agora precisamos incluir uma referência aos projetos Shop.Infra.Data e Shop.Infrastructure.IoC neste projeto.

Clique com o botão direito do mouse sobre o projeto Shop.Web.Mvc e selecione Add-> Project Reference;

Selecione o projeto Shop.Infra.Data e Shop.Infrastructure.IoC e clique em OK;

Agora podemos definir o código do método ConfigureServices conforme abaixo:

Vamos aproveitar e definir a string de conexão identificada pelo nome 'ShopConnection' no arquivo appsettings.json do projeto MVC:

Aqui você deve informar a string de conexão relativa à instância do seu SQL Server local.

Aplicando o Migrations para gerar o banco de dados e a tabela

Podemos aplicar o Migrations e assim gerar o banco de dados ShopDB e a tabela Products no SQL Server.

Antes precisamos permitir a criação de uma instância DbContext derivada em tempo de projeto para reunir detalhes sobre os tipos de entidade do aplicativo e como eles são mapeados para um esquema de banco de dados.

Lembre-se que as referências para o EF Core e suas dependências estão presentes somente no projeto Shop.Infra.Data enquanto que as configurações para o contexto, definidas via injeção de dependência usando DbContextOptions, como provedor do banco de dados e string de conexão estão definidas no projeto Shop.Web.Mvc.

Assim vamos ter que criar o DbContext em tempo de projeto e gerar o Migrations no projeto Shop.Infra.Data implementando a interface IDesignTimeDbContextFactory<TContext>, visto que , se uma classe que implementa esta interface for encontrada no mesmo projeto que o DbContext derivado ou no projeto de inicialização do aplicativo, as ferramentas contornam as outras maneiras de criar o DbContext e usam a fábrica em tempo de projeto no seu lugar.

Assim vamos criar outro arquivo de contexto na pasta Context do projeto chamado AppDbContextFactory com o seguinte código:

public class AppDbContextFactory : IDesignTimeDbContextFactory<AppDbContext>
{
        public AppDbContext CreateDbContext(string[] args)
        {
            var optionsBuilder = new DbContextOptionsBuilder<AppDbContext>();
            optionsBuilder.UseSqlServer("Data Source=desktop-bhp8771\\sqlexpress;Initial Catalog=ShopDB;Integrated Security=True");

            return new AppDbContext(optionsBuilder.Options);
        }
}

Obs: Se você quiser pode criar no projeto um arquivo appsettings.json e definir a string de conexão neste arquivo ao invés de incluir no arquivo acima.

Agora podemos aplicar o Migrations.

No Visual Studio acione o menu Tools-> Nuget Package Manager -> Package Manager Console ;

E na janela do Package Manager Console selecione o projeto Shop.Infra.Data e defina os comandos do Migrations:

add-migration 'MigracaioInicial' -Context AppDbContext
update-database

Ao final veremos a pasta Migrations criada no projeto.

E podemos conferir acionando o Server Explorer no Visual Studio que o banco de dados ShopDB e a tabela Products foram criados no SQLServer:

Note que a tabela Products ainda não possui nenhum registro. Mais adiante vamos cuidar disso.

Configurando o projeto Application

Todos os nossos serviços, interfaces e ViewModels serão definidos no projeto Shop.Application.

Agora precisamos incluir uma referência ao projeto Shop.Domain neste projeto.

Clique com o botão direito do mouse sobre o projeto Shop.Web.Mvc e selecione Add-> Project Reference;

Selecione o projeto Shop.Domain e clique em OK;

A seguir vamos criar as seguintes pastas no projeto para organizar o código:

Assim o projeto deverá ter a seguinte estrutura:

Vamos começar definindo as ViewModels.

Uma ViewModel representa os dados que você deseja exibir em sua view ou página, seja usando texto estático ou  valores de entrada (como caixas de texto e listas suspensas) que podem ser adicionados ao banco de dados.

Portanto a ViewModel é diferente do seu modelo de domínio. É um modelo usado apenas para a View, ou seja, ela cria uma máscara para os modelos de domínio.

Assim vamos criar uma nova classe na pasta ViewModels chamada ProductViewModel onde obteremos uma lista de produtos do banco de dados, e a seguir inclua o seguinte código na classe ProductViewModel.cs :

observe que estamos obtendo os dados do projeto a partir de Shop.Domain.Entities.

A seguir vamos criar uma interface para atuar como um contrato para a funcionalidade que estamos tentando implementar.

Na pasta Interfaces, crie uma nova interface chamada IProductService com o seguinte código:

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

Antes de escrevermos a implementação para IProductService, temos que definir uma maneira de obter os dados do banco de dados. Para fazer isso, o que normalmente usamos em .NET é um ORM chamado Entity Framework, mas usaremos aqui vamos usar o padrão Repository para separar a ´lógica do negócio e as camadas de acesso a dados em nosso aplicativo.

Mas como o artigo já esta ficando enorme vamos continuar isso na próxima parte.

"Portanto, cingindo os lombos do vosso entendimento, sede sóbrios, e esperai inteiramente na graça que se vos ofereceu na revelação de Jesus Cristo;"
1 Pedro 1:13

Referências:


José Carlos Macoratti