From 1ca6eb30f7e541faf7d653ee509a8b2fe36571bd Mon Sep 17 00:00:00 2001 From: makampos Date: Tue, 26 Nov 2024 11:43:45 -0300 Subject: [PATCH] feat: implement repository pattern with interfaces - Added IUnitOfWork for common SaveChangesAsync - Added IRepository for common methods: 'AddAsync', 'UpdateAsync', 'DeleteAsync' - Added IReadOnlyRepository to separate write and query operations --- .../Repositories/IDefaultUnitOfWork.cs | 6 ++++ .../Repositories/IReadOnlyRepository.cs | 15 +++++++++ .../Repositories/IRepository.cs | 10 ++++++ .../Repositories/IUnitOfWork.cs | 6 ++++ .../InfrastructureModuleInitializer.cs | 2 ++ .../Repositories/DefaultUnitOfWork.cs | 10 ++++++ .../Repositories/ReadOnlyRepository.cs | 33 +++++++++++++++++++ .../Repositories/Repository.cs | 31 +++++++++++++++++ .../Repositories/RepositoryProperties.cs | 33 +++++++++++++++++++ .../Repositories/UnitOfWork.cs | 11 +++++++ 10 files changed, 157 insertions(+) create mode 100644 backend/src/Ambev.DeveloperEvaluation.Domain/Repositories/IDefaultUnitOfWork.cs create mode 100644 backend/src/Ambev.DeveloperEvaluation.Domain/Repositories/IReadOnlyRepository.cs create mode 100644 backend/src/Ambev.DeveloperEvaluation.Domain/Repositories/IRepository.cs create mode 100644 backend/src/Ambev.DeveloperEvaluation.Domain/Repositories/IUnitOfWork.cs create mode 100644 backend/src/Ambev.DeveloperEvaluation.ORM/Repositories/DefaultUnitOfWork.cs create mode 100644 backend/src/Ambev.DeveloperEvaluation.ORM/Repositories/ReadOnlyRepository.cs create mode 100644 backend/src/Ambev.DeveloperEvaluation.ORM/Repositories/Repository.cs create mode 100644 backend/src/Ambev.DeveloperEvaluation.ORM/Repositories/RepositoryProperties.cs create mode 100644 backend/src/Ambev.DeveloperEvaluation.ORM/Repositories/UnitOfWork.cs diff --git a/backend/src/Ambev.DeveloperEvaluation.Domain/Repositories/IDefaultUnitOfWork.cs b/backend/src/Ambev.DeveloperEvaluation.Domain/Repositories/IDefaultUnitOfWork.cs new file mode 100644 index 0000000..cd676b5 --- /dev/null +++ b/backend/src/Ambev.DeveloperEvaluation.Domain/Repositories/IDefaultUnitOfWork.cs @@ -0,0 +1,6 @@ +namespace Ambev.DeveloperEvaluation.Domain.Repositories; + +public interface IDefaultUnitOfWork : IUnitOfWork +{ + IProductRepository ProductRepository { get; init; } +} \ No newline at end of file diff --git a/backend/src/Ambev.DeveloperEvaluation.Domain/Repositories/IReadOnlyRepository.cs b/backend/src/Ambev.DeveloperEvaluation.Domain/Repositories/IReadOnlyRepository.cs new file mode 100644 index 0000000..6977c7a --- /dev/null +++ b/backend/src/Ambev.DeveloperEvaluation.Domain/Repositories/IReadOnlyRepository.cs @@ -0,0 +1,15 @@ +using Ambev.DeveloperEvaluation.Domain.Common; +using Ambev.DeveloperEvaluation.Domain.Entities; + +namespace Ambev.DeveloperEvaluation.Domain.Repositories; + +public interface IReadOnlyRepository where TEntity : BaseEntity +{ + Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default); + Task> GetAllAsync( + int pageNumber = 1, + int pageSize = 10, + string? order = null, + string? category = null, + CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/backend/src/Ambev.DeveloperEvaluation.Domain/Repositories/IRepository.cs b/backend/src/Ambev.DeveloperEvaluation.Domain/Repositories/IRepository.cs new file mode 100644 index 0000000..5a0bf5d --- /dev/null +++ b/backend/src/Ambev.DeveloperEvaluation.Domain/Repositories/IRepository.cs @@ -0,0 +1,10 @@ +using Ambev.DeveloperEvaluation.Domain.Common; + +namespace Ambev.DeveloperEvaluation.Domain.Repositories; + +public interface IRepository : IReadOnlyRepository where TEntity: BaseEntity +{ + Task AddAsync(TEntity entity, CancellationToken cancellationToken = default); + Task UpdateAsync(TEntity entity, CancellationToken cancellationToken = default); + Task DeleteAsync(TEntity entity, CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/backend/src/Ambev.DeveloperEvaluation.Domain/Repositories/IUnitOfWork.cs b/backend/src/Ambev.DeveloperEvaluation.Domain/Repositories/IUnitOfWork.cs new file mode 100644 index 0000000..0aead83 --- /dev/null +++ b/backend/src/Ambev.DeveloperEvaluation.Domain/Repositories/IUnitOfWork.cs @@ -0,0 +1,6 @@ +namespace Ambev.DeveloperEvaluation.Domain.Repositories; + +public interface IUnitOfWork +{ + Task SaveChangesAsync(CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/backend/src/Ambev.DeveloperEvaluation.IoC/ModuleInitializers/InfrastructureModuleInitializer.cs b/backend/src/Ambev.DeveloperEvaluation.IoC/ModuleInitializers/InfrastructureModuleInitializer.cs index e804321..00102eb 100644 --- a/backend/src/Ambev.DeveloperEvaluation.IoC/ModuleInitializers/InfrastructureModuleInitializer.cs +++ b/backend/src/Ambev.DeveloperEvaluation.IoC/ModuleInitializers/InfrastructureModuleInitializer.cs @@ -16,5 +16,7 @@ public void Initialize(WebApplicationBuilder builder) builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); + builder.Services.AddScoped(typeof(IDefaultUnitOfWork), typeof(DefaultUnitOfWork)); + } } \ No newline at end of file diff --git a/backend/src/Ambev.DeveloperEvaluation.ORM/Repositories/DefaultUnitOfWork.cs b/backend/src/Ambev.DeveloperEvaluation.ORM/Repositories/DefaultUnitOfWork.cs new file mode 100644 index 0000000..7f9ca6b --- /dev/null +++ b/backend/src/Ambev.DeveloperEvaluation.ORM/Repositories/DefaultUnitOfWork.cs @@ -0,0 +1,10 @@ +using Ambev.DeveloperEvaluation.Domain.Repositories; + +namespace Ambev.DeveloperEvaluation.ORM.Repositories; + +public class DefaultUnitOfWork( + DefaultContext defaultContext, + IProductRepository productRepository) : UnitOfWork(defaultContext), IDefaultUnitOfWork +{ + public IProductRepository ProductRepository { get; init; } = productRepository; +} \ No newline at end of file diff --git a/backend/src/Ambev.DeveloperEvaluation.ORM/Repositories/ReadOnlyRepository.cs b/backend/src/Ambev.DeveloperEvaluation.ORM/Repositories/ReadOnlyRepository.cs new file mode 100644 index 0000000..78764ca --- /dev/null +++ b/backend/src/Ambev.DeveloperEvaluation.ORM/Repositories/ReadOnlyRepository.cs @@ -0,0 +1,33 @@ +using Ambev.DeveloperEvaluation.Domain.Common; +using Ambev.DeveloperEvaluation.Domain.Entities; +using Ambev.DeveloperEvaluation.Domain.Repositories; +using Microsoft.EntityFrameworkCore; + +namespace Ambev.DeveloperEvaluation.ORM.Repositories; + +public class ReadOnlyRepository( + DefaultContext defaultContext +) : RepositoryProperties(defaultContext), IReadOnlyRepository where TEntity : BaseEntity +{ + public async Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default) + { + return await SetAsTracking.FirstOrDefaultAsync(x => x.Id == id, cancellationToken); + } + + public async Task> GetAllAsync( + int pageNumber = 1, + int pageSize = 10, + string? order = null, + string? category = null, + CancellationToken cancellationToken = default + ) + { + var totalCount = await SetAsNoTracking.CountAsync(cancellationToken); + var items = await SetAsNoTracking + .Skip((pageNumber - 1) * pageSize) + .Take(pageSize) + .ToListAsync(cancellationToken); + + return PagedResult.Create(items, totalCount, pageNumber, pageSize); + } +} \ No newline at end of file diff --git a/backend/src/Ambev.DeveloperEvaluation.ORM/Repositories/Repository.cs b/backend/src/Ambev.DeveloperEvaluation.ORM/Repositories/Repository.cs new file mode 100644 index 0000000..1e38789 --- /dev/null +++ b/backend/src/Ambev.DeveloperEvaluation.ORM/Repositories/Repository.cs @@ -0,0 +1,31 @@ +using Ambev.DeveloperEvaluation.Domain.Common; +using Ambev.DeveloperEvaluation.Domain.Repositories; + +namespace Ambev.DeveloperEvaluation.ORM.Repositories; + +public class Repository( + DefaultContext defaultContext +) : ReadOnlyRepository(defaultContext), + IRepository where TEntity : BaseEntity +{ + public async Task AddAsync(TEntity entity, CancellationToken cancellationToken = default) + { + await Set.AddAsync(entity, cancellationToken); + } + + public async Task UpdateAsync(TEntity entity, CancellationToken cancellationToken = default) + { + await Task.Run(() => + { + Set.Update(entity); + }, cancellationToken); + } + + public async Task DeleteAsync(TEntity entity, CancellationToken cancellationToken = default) + { + await Task.Run(() => + { + Set.Remove(entity); + }, cancellationToken); + } +} \ No newline at end of file diff --git a/backend/src/Ambev.DeveloperEvaluation.ORM/Repositories/RepositoryProperties.cs b/backend/src/Ambev.DeveloperEvaluation.ORM/Repositories/RepositoryProperties.cs new file mode 100644 index 0000000..9bb173e --- /dev/null +++ b/backend/src/Ambev.DeveloperEvaluation.ORM/Repositories/RepositoryProperties.cs @@ -0,0 +1,33 @@ +using Ambev.DeveloperEvaluation.Domain.Common; +using Microsoft.EntityFrameworkCore; + +namespace Ambev.DeveloperEvaluation.ORM.Repositories; + +public class RepositoryProperties( + DefaultContext defaultContext +) where TEntity : BaseEntity +{ + protected readonly DefaultContext ApplicationDbContext = defaultContext; + + protected DbSet Set => ApplicationDbContext.Set(); + + protected IQueryable SetAsTracking + { + get + { + var query = Set.AsTracking(); + + return query; + } + } + + protected IQueryable SetAsNoTracking + { + get + { + var query = Set.AsNoTracking(); + + return query; + } + } +} \ No newline at end of file diff --git a/backend/src/Ambev.DeveloperEvaluation.ORM/Repositories/UnitOfWork.cs b/backend/src/Ambev.DeveloperEvaluation.ORM/Repositories/UnitOfWork.cs new file mode 100644 index 0000000..3bb5f57 --- /dev/null +++ b/backend/src/Ambev.DeveloperEvaluation.ORM/Repositories/UnitOfWork.cs @@ -0,0 +1,11 @@ +using Ambev.DeveloperEvaluation.Domain.Repositories; + +namespace Ambev.DeveloperEvaluation.ORM.Repositories; + +public class UnitOfWork(DefaultContext defaultContext) : IUnitOfWork +{ + public async Task SaveChangesAsync(CancellationToken cancellationToken = default) + { + return await defaultContext.SaveChangesAsync(cancellationToken) > 0; + } +} \ No newline at end of file