ASP.NET Core - Implementando Onion Architecture com CQRS - IV


 Hoje vamos concluir a implementação da Onion Architecture em uma aplicação ASP .NET Core aplicando o CQRS.

Continuando a terceira parte do artigo vamos implementar a Web API no projeto ApiAlunos.

Implementando os recursos da camada CrossCutting

Antes de iniciarmos a implementação da API na camada ApiAlunos precisamos definir os recursos da camada CrossCutting criada no projeto Infrastructure.

Talvez o nome CrossCutting não tenha sido o mais apropriado, pois nesta camada vamos definir a classe estática DependencyInjection e o método de extensão AddInfrastructure onde vamos registrar os serviços , os repositórios, o AutoMapper e o MediatR de forma a podermos injetar estes recursos na camada Presentation.

Nota: A rigor o termo CrossCutting ou responsabilidades transversais refere-se métodos comuns fornecidos por uma camada ou serviço específico usado por todas as camadas de uma aplicação. Seria como uma camada/serviço que cruza toda a hierarquia.

No nosso projeto a camada CrossCutting faz parte da Infrastructure e será usada para registrar os serviços e recursos que serão usados na API.

Esta camada vai precisar ter uma referência aos projetos : Application e Persistence.

Assim vamos criar a classe DependencyInjection com o código abaixo:

using Application.Interfaces;
using Application.Mappings;
using Application.Services;
using Domain.Interfaces;
using MediatR;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Persistence.Context;
using Persistence.Repositories;
using System;

namespace CrossCutting
{
    public static class DependencyInjection
    {
        public static IServiceCollection AddInfrastructure(this IServiceCollection services,
            IConfiguration configuration)
        {
            services.AddDbContext<ApplicationDbContext>(options =>
             options.UseSqlServer(configuration.GetConnectionString("DefaultConnection"
              ), b => b.MigrationsAssembly(typeof(ApplicationDbContext).Assembly.FullName)));

            services.AddScoped<IAlunoRepository, AlunoRepository>();
            services.AddScoped<IAlunoService, AlunoService>();

            services.AddAutoMapper(typeof(DomainToDtoMappingProfile));

            var myhandlers = AppDomain.CurrentDomain.Load("Application");
            services.AddMediatR(myhandlers);

            return services;
        }
    }
}

Pronto, agora poderemos carregar os serviços definidos nesta camada na classe Startup do projeto API.

Criando a Web API no projeto ApiAlunos

Para iniciar vamos definir a chamada do método de extensão AddInfrastructure no método ConfigureServices da classe Startup do projeto ApiAlunos:

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddInfrastructure(Configuration);
            services.AddControllers();

            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "ApiAlunos", Version = "v1" });
            });
        }

No arquivo appsettings.json temos que definir a string de conexão com o banco de dados SQL Server usado no projeto:

{
  "ConnectionStrings": {
    "DefaultConnection": "SUA_INSTANCIA;Initial Catalog=OnionDBTeste;Integrated Security=True"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

Aqui você deve usar a sua string de conexão. (O nome do banco de dados usado : OnionDBTeste)

Criando o controlador AlunosController

Vamos criar o controlador AlunosController na pasta Controllers com o código abaixo:

using Application.DTOs;
using Application.Interfaces;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace ApiAlunos.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class AlunosController : ControllerBase
    {

        private readonly IAlunoService _alunoService;
        public AlunosController(IAlunoService alunoService)
        {
            _alunoService = alunoService;
        }

        [HttpGet]
        public async Task<ActionResult<IEnumerable<AlunoDto>>> Get()
        {
            var alunos = await _alunoService.GetAlunosAsync();
            if (alunos == null)
            {
                return NotFound("Alunos não encontrados");
            }
            return Ok(alunos);
        }

        [HttpGet("{id}", Name = "GetAluno")]
        public async Task<ActionResult<AlunoDto>> Get(int id)
        {
            var produto = await _alunoService.GetAlunoIdAsync(id);
            if (produto == null)
            {
                return NotFound("Aluno não encontrado");
            }
            return Ok(produto);
        }

        [HttpPost]
        public async Task<ActionResult> Post([FromBody] AlunoDto alunoDto)
        {
            if (alunoDto == null)
                return BadRequest("Dados inválidos");

            await _alunoService.CreateAsync(alunoDto);

            return new CreatedAtRouteResult("GetAluno",
                new { id = alunoDto.Id }, alunoDto);
        }

        [HttpPut("{id}")]
        public async Task<ActionResult> Put(int id, [FromBody] AlunoDto alunoDto)
        {
            if (id != alunoDto.Id)
            {
                return BadRequest("Dados inválidos");
            }

            if (alunoDto == null)
                return BadRequest("Dados inválidos");

            await _alunoService.UpdateAsync(id, alunoDto);

            return Ok(alunoDto);
        }

        [HttpDelete("{id}")]
        public async Task<ActionResult<AlunoDto>> Delete(int id)
        {
            var alunoDto = await _alunoService.GetAlunoIdAsync(id);

            if (alunoDto == null)
            {
                return NotFound("Aluno não encontrado");
            }

            await _alunoService.DeleteAsync(id);

            return Ok(alunoDto);
        }
    }
}

No controlador estamos injetando um instância do serviço IAlunoService e realizando as operações para incluir, alterar, consultar e excluir dados de um aluno usando os verbos HTTP Get, Post, Put e Delete.

Agora é só alegria ...

Executando o projeto teremos a apresentação dos endpoints da API exibidos na interface do Swagger:

E assim implementamos em um pequeno projeto a Arquitetura Cebola ou Onion Architecture onde temos um código desacoplado que é mais fácil de estender e manter.

A implementação do CQRS feita foi a mais simples e arcaica possível , e estamos usando apenas uma base de dados, realizando apenas as separações dos comandos e consultas. Existem implementações mais elaboradas que usam duas bases de dados, a base para leitura e a base para escrita que permite otimizar e obter um melhor desempenho na aplicação do padrão CQRS.

Pegue o projeto aqui : OnionArch.zip (sem as referências no projeto API)

"como está escrito: Não há justo, nem um sequer, não há quem entenda, não há quem busque a Deus;
todos se extraviaram, à uma se fizeram inúteis; não há quem faça o bem, não há nem um sequer."
Romanos 3:10-12

Referências:


José Carlos Macoratti