Clean Architecture -
Implementando o padrão Unit Of Work
![]() |
Hoje veremos como implementar o padrão Unit Of Work em um projeto ASP.NET Core Web API que usa a Clean Architecture. |
Vamos mostrar a implementação do padrão em um projeto ASP.NET Core Web API que vai gerenciar informações sobre usuários definidos pela entidade User que vai conter as propriedades Email e Name. (Estamos simplificando o modelo de domínio)
Para aplicar a Arquitetura Limpa, podemos dividir a aplicação em quatro camadas:
A estrutura do projeto criado é mostrado a seguir:
Podemos identificar neste projeto os seguintes componentes:
Repositório
Um Repositório é usado para gerenciar a persistência e recuperação agregadas. Ele faz a mediação entre a camada de acesso a dados e o domínio e desacopla a camada de domínio da camada de dados de forma eficaz. Ele faz isso fornecendo acesso semelhante a uma coleção aos dados subjacentes.
O repositório oferece uma interface de coleção, fornecendo métodos para adicionar, modificar, remover e buscar objetos de domínio. Isso permite que o domínio permaneça agnóstico em relação ao mecanismo de persistência subjacente. Isso permite que ambas as camadas evoluam independentemente, mantendo alta coesão com baixo acoplamento.
Na figura abaixo temos uma comparação das abordagens sem usar repositório, usando o repositório e usando o repositório e o padrão unit of work:
Unidade de Trabalho (Unit of Work)
O padrão Unidade de Trabalho (UoW) é um padrão de projeto muito utilizado que ajuda a gerenciar transações e manter a consistência dos dados em aplicativos. Quando combinado com a Arquitetura Limpa, fornece uma base sólida para a construção de aplicativos escaláveis e de fácil manutenção.
Este padrão é usado para gerenciar transações e garantir que múltiplas operações sejam tratadas como uma única unidade lógica. Ele fornece uma maneira de agrupar operações de banco de dados, garantindo que elas sejam bem-sucedidas ou falhem como um todo. O princípio fundamental por trás do padrão Unidade de Trabalho é manter a consistência e a integridade dos dados, confirmando ou revertendo alterações de maneira coordenada.
A figura abaixo compara 3 abordagens mostrando o uso do repositório com o padrão Unit Of Work:
Integrando o Padrão de Unidade de Trabalho na
Arquitetura Limpa
Para implementar o padrão Unidade de Trabalho em um aplicativo .NET Core
no contexto da Arquitetura Limpa, podemos seguir as seguintes etapas:
1- Defina as interfaces principais
- Crie uma interface IUnitOfWork
que vai definir o contrato para a Unidade de Trabalho, incluindo métodos como
Commit() ou Save(), Rollback() e outros métodos que sejam pertinentes ao cenário
da sua aplicação;
2- Implemente a Unidade de Trabalho
Crie uma implementação concreta da interface IUnitOfWork
que deverá encapsulará os limites transacionais e coordenará as operações do
banco de dados. A implementação da Unidade de Trabalho deve fornecer métodos
para iniciar uma transação, confirmar alterações e reverter a transação, se
necessário.
3- Fazer a integração com repositórios
Modifique as implementações dos seus repositórios para funcionarem com a
implementação da Unidade de Trabalho. Cada método de repositório
deve ser incluído na transação da Unidade de Trabalho, garantindo que as
alterações feitas pelo repositório sejam confirmadas ou revertidas juntas.
4- Integração da camada de aplicação
Na camada de Aplicativo, injete a interface IUnitOfWork
no comando apropriado ou nos manipuladores de consulta que exigem operações
transacionais. Dentro desses manipuladores, execute as operações necessárias
usando os repositórios agrupados na Unidade de Trabalho.
5- Usando a Unit of Work implementada
Quando um comando ou consulta requer operações transacionais, recupere a
instância da Unidade de Trabalho por meio de injeção de dependência. Comece a
transação usando o método da Unidade de Trabalho. Execute as operações de banco
de dados necessárias usando os repositórios agrupados na Unidade de Trabalho.
Por fim, chame o método Commit() da Unidade de
Trabalho para persistir as alterações ou o método Rollback()
para descartar as alterações.
Da teoria para a prática
De forma a dar uma visão mais concreta sobre as etapas acima descritas vamos considerar um exemplo de uma aplicação de vendas de produtos para ilustrar o uso do padrão Unit Of Work.
1- Definindo a interface
public interface IUnitOfWork : IDisposable
{
void Commit();
void Rollback();
IRepository<TEntity> GetRepository<TEntity>() where TEntity : class;
}
|
2- Implementando a interface
public class UnitOfWork : IUnitOfWork
{
private readonly DbContext _context;
private Dictionary<Type, object> _repositories;
public UnitOfWork(DbContext context)
{
_context = context;
_repositories = new Dictionary<Type, object>();
}
public void Commit()
{
_context.SaveChanges();
}
public void Rollback()
{
// Rollback se for necessário
}
public IRepository<TEntity> GetRepository<TEntity>() where TEntity : class
{
if (_repositories.ContainsKey(typeof(TEntity)))
{
return (IRepository<TEntity>)_repositories[typeof(TEntity)];
}
var repository = new Repository<TEntity>(_context);
_repositories.Add(typeof(TEntity), repository);
return repository;
}
public void Dispose()
{
_context.Dispose();
}
}
|
Vamos entender este código:
Iniciamos definindo as seguintes variáveis :
A seguir no construtor temos que :
3- Integrando com os repositórios
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
private readonly DbContext _context;
private readonly DbSet<TEntity> _dbSet;
public Repository(DbContext context)
{
_context = context;
_dbSet = _context.Set<TEntity>();
}
// implementação dos métodos dos repositórios
}
|
4- Usando a implementação na camada Application
public class ProductService
{
private readonly IUnitOfWork _unitOfWork;
private readonly IRepository<Product> _productRepository;
public ProductService(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
_productRepository = _unitOfWork.GetRepository<Product>();
}
public void CreateProduct(Product product)
{
// Executa a lógica de negócio e as operações do repositório usando _productRepository...
_unitOfWork.Commit();
}
}
|
Com este exemplo mostramos como o padrão Unidade de Trabalho pode ser integrado a uma aplicação de vendas para lidar com operações de banco de dados. Com este padrão, podemos garantir que as operações relacionadas sejam tratadas como uma única unidade lógica, promovendo a integridade dos dados e a consistência transacional.
E estamos
conversados...
"E disse-lhe Jesus: Em verdade
te digo que hoje estarás comigo no Paraísoadm."
Lucas 23:43
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)