Api Rest .Net completa com JwtToken, integração com api ViaCep utilizando padrão de arquitetura clean architecture
Hernani Almeida
Posted on July 27, 2024
Ferramentas necessárias:
Neste artigo vamos implementar uma api rest utilizando o padrao Clean Architecture, nessa api iremos integrar com uma api externa para buscar dados, configurar e acessar banco de dados postgres fazendo mapeamento e relacionamento de tabelas, criptografar dados para salvar banco de dados e configurar o swagger para realizar a autenticação e o crud em nossa api, bora la.
Para iniciar e necessário fazer o download e instalar o Sdk do .net e a ide Visual Studio Community, links logo acima.
Dentro do terminal digite o comando dotnet --version
e devera retornar a versão instalada do sdk que você tem instalada.
Feito isso abra o Visual Studio e crie um novo projeto api web conforme o passo a passo abaixo.
Abra esse projeto dentro do visual studio e ele ira ter a seguinte estrutura
Antes de iniciarmos, ja vamos instalar dependencias que iremos utilizar em nossa aplicação que serão as seguintes
Instale cada uma delas seguindo o passo a passo abaixo
Clique botão direito na raiz da aplicação e entre em Gerenciar pacotes do nuget
Na aba que ira abrir vá em Procurar
e digite cada dependencia acima para instalar ao projeto conforme abaixo
Vamos estruturar nossa api utilizando o padrão de arquitetura ´Clean Architecture´ o que nos possibilita ter um sistema coeso e de baixo acoplamento, trabalhando a separação de responsabilidades ao nosso sistema,o que facilita a manutenabilidade de um sistema complexo e robusto.
Para isso vamos ter 3 camadas em nossa api fazendo a separação da mesma nas seguintes estruturas.
Domain Estrutura que será o nosso core, ou seja o núcleo da nossa api, dentro dela era conter o domínio da nossa aplicação e essa camada não depende de nenhuma camada do nosso sistema.
Application Essa camada conterá os casos de usos da nossa aplicação, contendo os serviços que manipulara o nosso domínio para realizar as transações de nossa api.
Observação: Essa camada depende da camada de domínio porem não pode ter nenhuma dependência com a camada seguinte.
Infrastructure Essa camada conterá toda parte de tecnologia/infraestrutura do nosso sistema e será a camada responsável por disponibilizar acesso externo a nossa api.
Observação: Essa camada tem dependência com as outras duas camadas citadas.
Vamos seguir com nossa api, vamos criar 3 pastas em nossa aplicação que ira simbolizar as camadas citadas acima, a estrutura do nosso sistema devera ficar conforme a imagem abaixo.
Vamos iniciar nossa aplicação definindo nossa camada de Domínio
, vamos definir nossa entity Employer
conforme abaixo.
Employer
using FirstApi.Domain.ValueObjects;
namespace FirstApi.Domain.Entities
{
public class Employer
{
public int Id { get; set; }
public string? Nome { get; set; }
public string? Cargo { get; set; }
public Payment? Payment { get; set; }
public double? PaymentTotal { get; set; }
public void AddPaymentByPremiation(double premiation)
{
PaymentTotal = PaymentTotal + premiation;
}
}
}
**Observação: **Note que temos o método addPaymentByPremiation
em nossa entity, método e responsável por adicionar uma premiação ao pagamento do funcionário, ou seja, nossa classe de domínio não será anêmica.
Vamos definir também em nossa camada de Domínio
uma interface com o contrato do nosso repositório ligado a entidade Employer e uma classe Payment que e um value-object da classe Employer.
IEmployerRepository
using FirstApi.Domain.Entities;
namespace FirstApi.Domain.Repositories
{
public interface IEmployerRepository
{
Task<List<Employer>> FindEmployers();
Task<Employer> FindEmployer(int id);
Task<Employer> Register(Employer employer);
Task<Employer> UpdateEmployer(Employer employer);
void DeleteEmployer(Employer employer);
}
}
Payment
namespace FirstApi.Domain.ValueObjects
{
public class Payment
{
public double Salary { get; set; }
public double Benefits { get; set; }
public double getPaymentTotal()
{
return Salary + Benefits;
}
}
}
A estrutura do nosso projeto agora ficou conforme a imagem abaixo, nossa camada de Domain é o núcleo da nossa api, portanto, ela não tem dependência com nenhuma outra camada do nosso sistema.
Seguimos agora para a camada Application
que e a responsável pelos serviços e implementação da regra de negocio de nossa api, na Clean Architecture utilizamos UseCases
,ou seja, casos de uso para implementar e realizar uma ação em nossa api, nessa ação definimos o que iremos receber de dados em nossa request para realizar a ação e o que vamos retornar após a ação realizada, isso nos da a facilidade para alterarmos, se necessário, tanto os dados de entrada como os de saída da nossa api tornando nossa aplicação resiliente a mudanças.
Para não estender o artigo não vou seguir passo a passo mas o codigo dessa camada pode ser visualizado no repositorio
Nossa camada de Application
sera implementado 4 casos de uso ligados a entity Employer e ficara implementado conforme abaixo.
Observação: Como já citamos antes essa camada tem dependência com a camada de Domain
pois utilizamos a entity Employer
e a interface IEmployerRepository
definida na camada de domínio que será implementada na camada mais externa da nossa aplicação, ou seja, a Infrastructure.
Para realizarmos a injeção da classe IEmployerRepository e dos services de cada casos de uso via construtor temos que adiciona-los ao escopo da nossa aplicação dentro da classe Program.cs, que ficara conforme abaixo.
using FirstApi.Application.UseCases.CasesEmployer.ConsultEmployer;
using FirstApi.Application.UseCases.CasesEmployer.DeleteEmployer;
using FirstApi.Application.UseCases.CasesEmployer.Register;
using FirstApi.Application.UseCases.CasesEmployer.UpdateEmployer;
using FirstApi.Domain.Repositories;
using FirstApi.Infrastructure.Data;
using FirstApi.Infrastructure.Handler;
using FirstApi.Infrastructure.Repositories;
using Microsoft.EntityFrameworkCore;
namespace FirstApi
{
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddScoped<IEmployerRepository, EmployerRepository>();
builder.Services.AddScoped<IRegisterEmployerService, RegisterEmployerService>();
builder.Services.AddScoped<IUpdateEmployerService, UpdateEmployerService>();
builder.Services.AddScoped<IConsultEmployerService, ConsultEmployerService>();
builder.Services.AddScoped<IDeleteEmployerService, DeleteEmployerService>();
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
// global error handler
app.UseMiddleware<GlobalExceptionHandler>();
app.MapControllers();
app.Run();
}
}
}
A classe EmployerRepository iremos construir na camada de Infrastructure
Após definirmos nossos Usecases
vamos seguir agora para a camada seguinte da nossa aplicação, a camada Infrastructure
, responsável pela parte tecnológica do nosso sistema e por disponibilizar acesso externo a nossa api, nessa camada fica as classes de Controllers, configurações banco de dados, implementações dos repositórios para acesso a transações no banco de dados, fluxo de mensagerias e etc...
Vamos configurar agora a conexão ao nossa database postgres, vamos criar uma pasta Data
e dentro dela uma classe SystemDbContext.cs
que ira configurar um DbContext, um tipo de ORM em dotnet.
SystemDbContext
using FirstApi.Domain.Entities;
using FirstApi.Infrastructure.Data.Map;
using Microsoft.EntityFrameworkCore;
namespace FirstApi.Infrastructure.Data
{
public class SystemDbContext : DbContext
{
public SystemDbContext(DbContextOptions<SystemDbContext> options)
: base(options)
{
}
public DbSet<Employer> Employers { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new EmployerMap());
base.OnModelCreating(modelBuilder);
}
}
}
Dentro da pasta Data
crie uma nova pasta Map
e dentro dela uma classe EmployerMap.cs
que ira mapear a tabela Employer para ser criada em nosso database.
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore;
using FirstApi.Domain.Entities;
namespace FirstApi.Infrastructure.Data.Map
{
public class EmployerMap : IEntityTypeConfiguration<Employer>
{
public void Configure(EntityTypeBuilder<Employer> builder)
{
builder.HasKey(x => x.Id);
builder.Property(x => x.Nome).IsRequired().HasMaxLength(255);
builder.Property(x => x.Cargo).IsRequired().HasMaxLength(255);
builder.Property(x => x.PaymentTotal).IsRequired();
builder.ToTable("Employers")
.OwnsOne(x => x.Payment, x =>
{
x.Property(a => a.Salary)
.HasColumnName("salary")
.IsRequired();
x.Property(a => a.Benefits)
.HasColumnName("benefits")
.IsRequired()
.HasDefaultValue(0);
});
}
}
}
Após isso temos que definir nossa string de conexão ao database no arquivo appsetings.Development.json que ficara assim.
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"ConnectionStrings": {
"DefaultConnection": "Host=localhost;Port=5433;Database=net;Username=admin;Password=password"
}
}
Vamos configurar na classe main da nossa aplicação Program.cs
as configurações de acesso ao banco conforme abaixo.
Agora vamos rodar dois comandos dentro do console Gerenciador de pacotes que ira criar uma migration e criar nossa tabela dentro do database.
Va no console Gerenciador de pacotes que entre com o comando Add-Migration InitialDB -Context SystemDbContext
Danto tudo certo, em seguida entre com o comando Update-Database -Context SystemDbContext
Crie uma pasta Repositories
e dentro dela a classe EmployerRepository
que ira implementar a classe IEmployerRepository
definida em nosso dominio.
using FirstApi.Domain.Entities;
using FirstApi.Domain.Repositories;
using FirstApi.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;
namespace FirstApi.Infrastructure.Repositories
{
public class EmployerRepository : IEmployerRepository
{
private readonly SystemDbContext _Dbcontext;
public EmployerRepository(SystemDbContext context)
{
_Dbcontext = context;
}
async Task<Employer> IEmployerRepository.Register(Employer employer)
{
await _Dbcontext.Employers.AddAsync(employer);
_Dbcontext.SaveChanges();
return employer;
}
async void IEmployerRepository.DeleteEmployer(Employer employer)
{
_Dbcontext.Employers.Remove(employer);
await _Dbcontext.SaveChangesAsync();
}
async Task<Employer> IEmployerRepository.UpdateEmployer(Employer employer)
{
_Dbcontext.Employers.Update(employer);
await _Dbcontext.SaveChangesAsync();
return employer;
}
async Task<Employer> IEmployerRepository.FindEmployer(int id)
{
return await _Dbcontext.Employers.FirstOrDefaultAsync(u => u.Id == id);
}
async Task<List<Employer>> IEmployerRepository.FindEmployers()
{
return await _Dbcontext.Employers.ToListAsync();
}
}
}
Crie agora uma pasta Controllers
e dentro a classe EmployerController.cs
que será a responsável por disponibilizar acesso externo a nossa api via chamadas Http e ficara conforme abaixo.
using FirstApi.Application.UseCases.CasesEmployer;
using FirstApi.Application.UseCases.CasesEmployer.ConsultEmployer;
using FirstApi.Application.UseCases.CasesEmployer.DeleteEmployer;
using FirstApi.Application.UseCases.CasesEmployer.Register;
using FirstApi.Application.UseCases.CasesEmployer.UpdateEmployer;
using FirstApi.Domain.Entities;
using Microsoft.AspNetCore.Mvc;
namespace FirstApi.Infrastructure.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class EmployerController
{
private IRegisterEmployerService _registerService;
private IUpdateEmployerService _updateService;
private IConsultEmployerService _consultService;
private IDeleteEmployerService _deleteService;
public EmployerController(
IRegisterEmployerService service,
IUpdateEmployerService updateService,
IConsultEmployerService consultService,
IDeleteEmployerService deleteService)
{
_registerService = service;
_updateService = updateService;
_consultService = consultService;
_deleteService = deleteService;
}
[HttpPost]
public RegisterEmployerOutput Post([FromBody] RegisterEmployerInput input)
{
return _registerService.Execute(input);
}
[HttpPut]
public UpdateEmployerOutput Put([FromBody] UpdateEmployerInput input)
{
return _updateService.Execute(input);
}
[HttpGet("{id}")]
public ConsultEmployerOutput findEmployer(int id)
{
return _consultService.FindEmployerById(id);
}
[HttpGet]
public List<ConsultEmployerOutput> findEmployers()
{
return _consultService.FindEmployers();
}
[HttpDelete("{id}")]
public string Delete(int id)
{
return _deleteService.DeleteEmployer(id);
}
}
}
Nossa camada Infrastructure
ficara assim.
Crie um arquivo com o nome docke-compose.yaml na raiz do projeto, vamos configurar esse arquivo para subir um DB Postgres no docker.
version: '3'
services:
postgres:
image: 'postgres:alpine'
volumes:
- postgres-volume:/var/lib/postgresql/data
ports:
- 5433:5432
environment:
POSTGRES_USER: admin
POSTGRES_PASSWORD: password
POSTGRES_DB: net
volumes:
postgres-volume:
Na pasta raiz rode o comando docker-compose up -d conforme abaixo para subir o database no docker.
Vamos rodar nossa aplicação clicando nesse botao que fica na parte de cima do visual studio
E já podemos ver o swagger da nossa aplicação.
Vamos testar nossa aplicação, primeiro vou realizar um registro
Agora vou buscar esse registro pelo Id
Para seguirmos o projeto vamos ter a segunda parte desse artigo, onde vamos criar um fluxo para salvar um usuário obtendo seus dados de Endereço através de uma integração da nossa api com a api ViaCep, ate la.
Posted on July 27, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.