An open API service indexing awesome lists of open source software.

https://github.com/guilhermebley/catalogocleanarchitecture

Clean Architecture Concepts.
https://github.com/guilhermebley/catalogocleanarchitecture

asp-net-core csharp dapper dapper-donet-core mysql unit-of-work

Last synced: 2 months ago
JSON representation

Clean Architecture Concepts.

Awesome Lists containing this project

README

          

# Catalogo

## CLEAN ARCHITECTURE
- ASP NET
- Solution with clean architecture
- Use Dapper
- Unit Of Work

## Unit Of Work With Dapper

The objective is make a Unit Of Work, which can execute a `SaveChanges` from various Repositories, persistency the data.

With the Dependency Injection is managed a shared connection on the repositories.
In Catalogo.CrossCutting.IoC we could see this:
```C#
services
.AddSingleton(configuration)
.AddTransient()
.AddScoped()
.AddScoped()
.AddScoped()
.AddScoped()

// UoW
.AddScoped()
.AddScoped(x => x.GetRequiredService())
.AddScoped(x => x.GetRequiredService());
```

The persistence of data execution is make in `Catalogo.Application.UoW.IUnitOfWork`, which have delivered to Services.
The connection to access the data was inserted in the interface `Catalogo.Infrastructure.Context.UnitOfWork.Repository`, which is shared to Repositories.

```C#
namespace Catalogo.Infrastructure.Context
{
///
/// Give a shared connection to repositorys
///
public interface IUnitOfWorkRepository : IUnitOfWork
{
///
/// Avaliable connection
///
IDbConnection Connection { get; }

///
/// Avaliable Transaction
///
IDbTransaction Transaction { get; }
}
}

namespace Catalogo.Application.UoW
{
///
/// Unit of work give a shared transactions to repositorys
///
public interface IUnitOfWork : IDisposable
{
///
/// Identifier of unit
///
Guid Identifier { get; }

///
/// Necessary to create and open a new connection
///
/// async result of opened
Task OpenConnectionAsync();

///
/// Creates a connection (if method haven't executed) and transaction
///
///
/// Starts a transaction to the repositorys
/// Use > after execute
///
/// async result of opened
Task BeginTransactionAsync();

///
/// Commits if is ok or roll back if throw a exception
///
/// async
Task SaveChangesAsync();
}
}
```

And a unique class implements a both (namespace `Catalogo.Infrastructure.Context`).

```C#
///
/// Manage connections and transactions
///
public class UnitOfWorkRepository : IUnitOfWorkRepository
{
private DbConnection _connection { get; set; }
public DbTransaction _transaction { get; set; }

public IDbConnection Connection => _connection ?? throw new DataException("Connection is closed.");

public IDbTransaction Transaction => _transaction;

public Guid Identifier { get; } = Guid.NewGuid();

private readonly IConnectionFactory _connectionFactory;

public UnitOfWorkRepository(IConnectionFactory connectionFactory)
{
_connectionFactory = connectionFactory;
}

public async Task BeginTransactionAsync()
{
await OpenConnectionAsync();

if (_transaction is null)
_transaction = await _connection.BeginTransactionAsync();

return this;
}

public async Task SaveChangesAsync()
{
try
{
await _transaction?.CommitAsync();
}
catch
{
await _transaction?.RollbackAsync();
throw;
}
finally
{
_transaction?.Dispose();
_transaction = null;
}
}

public void Dispose()
{
if (_transaction is not null)
{
_transaction.Dispose();
_transaction = null;
}

if (_connection is not null)
{
_connection.Dispose();
_connection = null;
}
}

public async Task OpenConnectionAsync()
{
if (_connection is null)
{
_connection = _connectionFactory.CreateConn();
await _connection.OpenAsync();
}

return this;
}
}
```

To use this implementation two parts needed.

- Repository

Needs a connection and transaction to execute the commands, and in all of the connections in DataBase should be use this way:

```C#
[...]
public class CategoriaRepository : RepositoryBase, ICategoriaRepository
{
private readonly IUnitOfWorkRepository _uoW;

///
/// Connection to execute commands and querys
///
protected IDbConnection _connection => _uoW.Connection;

///
/// Shared execution
///
protected IDbTransaction _transaction => _uoW.Transaction;

public RepositoryBase(IUnitOfWorkRepository uoW)
{
_uoW = uoW;
}

public async Task CreateAsync(Produto product)
{
return
await _connection.ExecuteAsync(
"INSERT INTO catalagodapper.produto (Nome, Descricao, Preco, ImagemUrl, Estoque, DataCadastro, IdCategoria) VALUES (@Nome, @Descricao, @Preco, @ImagemUrl, @Estoque, @DataCadastro, @IdCategoria);",
product,
_transaction
);
}
}
[...]
```
- Services

The Services manage a open connection to Repositories, having two ways, the simple execution, without transaction, or with it.

```C#
public class CategoriaService : ICategoriaService
{
private readonly IUnitOfWork _uow;
private ICategoriaRepository _categoryRepository;
private readonly IMapper _mapper;

public CategoriaService(
IUnitOfWork uow,
ICategoriaRepository categoryRepository,
IMapper mapper)
{
_uow = uow;
_categoryRepository = categoryRepository;
_mapper = mapper;
}

public async Task Add(CategoriaDTO categoryDto)
{
var categoryEntity = _mapper.Map(categoryDto);

categoryEntity.Validate();

using (await _uow.BeginTransactionAsync())
{
if ((await _categoryRepository.GetByName(categoryEntity.Nome)) is not null)
throw new ConflictException($"Category with name {categoryEntity.Nome} already exists.");

await _categoryRepository.CreateAsync(categoryEntity);
await _uow.SaveChangesAsync();
}
}
}
```