.NET
-
Apresentando Hexagonal
Architecture - II
![]() |
Neste artigo vou vou continuar apresentando os fundamentos da arquitetura Hexagonal e mostrar um exemplo prático de uso desta arquitetura na plataforma .NET. |
Apresentação do projeto
Vamos criar um
pequeno projeto ASP.NET Core Web API com base nos conceitos da arquitetura
hexagonal. Esta API vai gerenciar usuários a partir da entidade
User que contém as propriedades
Id nome e email.
Estou usando o mesmo modelo de dominio que foi usado nas semanas anteriores na
arquitetura cebola e arquitetura limpa e criando uma api para gerenciar
usuários.
O projeto foi criado no VS 2022 preview usando o .NET 8.0 e contém uma solução
HexagonalArch
e 5 projetos :
Nesta solução criamos a solution folder Adapters e dentro desta pasta criamos duas solution Folders
- Infra.Data que contém a referencia ao EF Core in Memory onde criamos as pastas Context e Repositories;
- Infra.Email contendo a pasta Operations ;
Na pasta
Core criamos os projetos do tipo Class
Library :
1 - Application - Nesta camada é onde
ficam as implementações, das regras de negócio, de acordo com a interface
IUserService. Na classe UserServiceManager
registramos injetamo a Interface “IUserService” a que será usada como porta de
entrada para a regra de negócio.
Recebemos, via construtor, todas as dependências com o mundo externo que
necessitamos. No caso as operações com o banco de dados e email.
2 - Domain - Nesta camada é onde ficam a
declaração das nossas “Portas”/Interfaces (Adapters)
de saída para comunicação com o meio externo, além das entidades que
representam o nosso negócio e a “porta de entrada” para a regra de negócio
especificamente. Aqui temos a definição das interfaces
IEmailService, IUserRepository e IUserService.
A camada de domínio, que contém essas interfaces, não se preocupa com a
implementação concreta das funcionalidades, mas sim com os contratos que
garantem como essas funcionalidades podem ser usadas. As implementações
concretas dessas interfaces ficarão nas camadas de adaptação (por exemplo, Infra.Data
e Infra.Email).
Na pasta Primary esta o projeto que vai desencadear
alguma ação : Neste caso, o projeto API, onde no controlador temos o acionamento
da aplicação via método Action e uso da interface de serviço
Na pasta Secondary temos a o projeto que vai
definir a saida definindo as implementações do repositorio, do serviço de
mail
Note que em cada projeto definimos as dependências com métodos de extensão que
serão carregados na classe Program do projeto API
Na classe Program defini um método
DataInit para incluir dois usuários quando da carga
da aplicação:
void
DataInit(IApplicationBuilder app) { using (var serviceScope = app.ApplicationServices.CreateScope()) { var context = serviceScope.ServiceProvider.GetService<InMemoryContext>(); context.Database.EnsureCreated(); if (!context.Users.Any()) { context.Users.AddRange( new User(Guid.NewGuid(), "Maria Silva", "mariasilva@email.com"), new User(Guid.NewGuid(), "Paulo Santos", "paulosantos@email.com") ); context.SaveChanges(); } } |
Criamos na pasta Services a classe UserServiceManager que implementa IUserService e define o acesso aos dados através do repositório UserRepository:
using Domain.Adapters;
using Domain.Entities;
using Domain.Ports;
namespace Application.Services;
public class UserServiceManager : IUserService
{
private readonly IEmailService _emailAdapter;
private readonly IUserRepository _userRepository;
public UserServiceManager(IEmailService emailAdapter,
IUserRepository userRepository)
{
_emailAdapter = emailAdapter;
_userRepository = userRepository;
}
public async Task<IEnumerable<User>> GetAllUsersAsync()
{
var users = await _userRepository.GetAll();
return users;
}
public async Task<User> AddNewUserAsync(User user)
{
await _userRepository.Insert(user);
_emailAdapter.SendEmail("macoratti@yahoo.com",
"teste@email.com", "User was included with sucess...", "Added user");
return user;
}
public async Task<User> UpdateUserAsync(User user)
{
var userUpdated = await _userRepository.Update(user);
_emailAdapter.SendEmail("macoratti@yahoo.com",
"teste@email.com", "User was updated with sucess...", "Updated user");
return userUpdated;
}
public async Task<User> DeleteUserAsync(Guid id)
{
var userDeleted = await _userRepository.Delete(id);
_emailAdapter.SendEmail("macoratti@yahoo.com",
"teste@email.com", "User was deleted with sucess...", "Deleted user");
return userDeleted;
}
}
|
O código da classe UserRepository é o seguinte:
using Domain.Adapters;
using Domain.Entities;
using Infra.Data.Context;
using Microsoft.EntityFrameworkCore;
namespace Infra.Data.Repositories;
public class UserRepository : IUserRepository
{
private readonly InMemoryContext _context;
public UserRepository(InMemoryContext context)
{
_context = context;
}
public async Task<IEnumerable<User>> GetAll()
{
return await _context.Users.ToListAsync();
}
public async Task<User> Insert(User user)
{
if (user is null)
{
throw new ArgumentNullException(nameof(user));
}
_context.Users.Add(user);
await _context.SaveChangesAsync();
return user;
}
public async Task<User> Update(User user)
{
_context.Users.Update(user);
await _context.SaveChangesAsync();
return user;
}
public async Task<User> Delete(Guid id)
{
var user = await _context.Users.FirstOrDefaultAsync(u => u.Id == id);
if (user is null)
{
throw new ArgumentNullException(nameof(user));
}
_context.Users.Remove(user);
await _context.SaveChangesAsync();
return user;
}
}
|
E o controlador UsersController usa o serviço IUserService para realizar as operações:
using Domain.Entities;
using Domain.Ports;
using Microsoft.AspNetCore.Mvc;
namespace API.Controllers;
[Route("api/[controller]")]
[ApiController]
public class UsersController : ControllerBase
{
private readonly IUserService _userService;
public UsersController(IUserService clubService) =>
_userService = clubService;
[HttpGet]
public async Task<IActionResult> GetAllUsers()
{
var users = await _userService.GetAllUsersAsync();
return Ok(users);
}
[HttpPost]
public async Task<IActionResult> RegisterUser(User user)
{
await _userService.AddNewUserAsync(user);
return Ok(user);
}
[HttpPut("{id:Guid}")]
public async Task<IActionResult> UpdateUser(Guid id, User user)
{
if (id != user.Id)
{
return BadRequest();
}
await _userService.UpdateUserAsync(user);
return Ok(user);
}
[HttpDelete("{id:Guid}")]
public async Task<IActionResult> DeleteUser(Guid id)
{
var user = await _userService.DeleteUserAsync(id);
return Ok(user);
}
}
|
Pegue o código do projeto aqui: https://github.com/macoratti/HexagonalArch
E estamos conversados...
"(Disse Jesus) Quem crê no Filho de Deus, em si mesmo tem o testemunho; quem
a Deus não crê mentiroso o fez, porquanto não creu no testemunho que Deus de seu
Filho deu"
1
João 5:10
Referências:
C# - Tasks x Threads. Qual a diferença
DateTime - Macoratti.net
Null o que é isso ? - Macoratti.net
Formatação de data e hora para uma cultura ...
C# - Calculando a diferença entre duas datas
NET - Padrão de Projeto - Null Object Pattern
C# - Fundamentos : Definindo DateTime como Null ...
C# - Os tipos Nullable (Tipos Anuláveis)