refactor(application): Remove IUnitOfWork, add generic IRepository<T> with AutoMapper
- Remove IUnitOfWork pattern (not needed with auto-save repositories) - Add generic IRepository<T> with CreateAsync<TDto>, UpdateSingleAsync<TDto>, DeleteSingleAsync - Add UpdateAsync/DeleteAsync for bulk operations - Add ICommandPublisher interface for RabbitMQ integration - Add service interfaces: IEmailService, IPdfProcessingService, IDmsService, IEncryptionService, IEmailQueue - Configure Application DI with MediatR, AutoMapper, FluentValidation
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
using MediatR;
|
||||
|
||||
namespace DigitalData.EmailProfiler.Application.Common.Interfaces;
|
||||
|
||||
/// <summary>
|
||||
/// Interface for publishing commands to a message broker (e.g., RabbitMQ)
|
||||
/// </summary>
|
||||
public interface ICommandPublisher
|
||||
{
|
||||
/// <summary>
|
||||
/// Publishes a command to the message broker for asynchronous processing
|
||||
/// </summary>
|
||||
/// <typeparam name="TCommand">The command type (must implement IRequest)</typeparam>
|
||||
/// <param name="command">The command to publish</param>
|
||||
/// <param name="cancellationToken">Cancellation token</param>
|
||||
/// <returns>Task representing the publish operation</returns>
|
||||
Task PublishAsync<TCommand>(TCommand command, CancellationToken cancellationToken = default)
|
||||
where TCommand : IRequest;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using System.Reflection;
|
||||
using FluentValidation;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace DigitalData.EmailProfiler.Application;
|
||||
|
||||
/// <summary>
|
||||
/// Dependency injection configuration for Application layer.
|
||||
/// </summary>
|
||||
public static class DependencyInjection
|
||||
{
|
||||
public static IServiceCollection AddApplicationServices(this IServiceCollection services)
|
||||
{
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
|
||||
// MediatR - Register all handlers
|
||||
services.AddMediatR(config =>
|
||||
{
|
||||
config.RegisterServicesFromAssembly(assembly);
|
||||
});
|
||||
|
||||
// AutoMapper - Register all profiles
|
||||
services.AddAutoMapper(assembly);
|
||||
|
||||
// FluentValidation - Register all validators
|
||||
services.AddValidatorsFromAssembly(assembly);
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
@@ -3,17 +3,58 @@ using System.Linq.Expressions;
|
||||
namespace DigitalData.EmailProfiler.Application.Interfaces.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// Base repository interface for common CRUD operations.
|
||||
/// Base repository interface for common CRUD operations with AutoMapper support.
|
||||
/// Changes are automatically saved after each operation - no explicit SaveChanges needed.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Entity type</typeparam>
|
||||
public interface IRepository<T> where T : class
|
||||
/// <typeparam name="TEntity">Entity type</typeparam>
|
||||
public interface IRepository<TEntity> where TEntity : class
|
||||
{
|
||||
Task<T?> GetByIdAsync(int id, CancellationToken cancellationToken = default);
|
||||
Task<IEnumerable<T>> GetAllAsync(CancellationToken cancellationToken = default);
|
||||
Task<IEnumerable<T>> FindAsync(Expression<Func<T, bool>> predicate, CancellationToken cancellationToken = default);
|
||||
Task<T> AddAsync(T entity, CancellationToken cancellationToken = default);
|
||||
Task UpdateAsync(T entity, CancellationToken cancellationToken = default);
|
||||
Task DeleteAsync(T entity, CancellationToken cancellationToken = default);
|
||||
Task<bool> ExistsAsync(Expression<Func<T, bool>> predicate, CancellationToken cancellationToken = default);
|
||||
Task<int> CountAsync(Expression<Func<T, bool>>? predicate = null, CancellationToken cancellationToken = default);
|
||||
// ==================== QUERY OPERATIONS ====================
|
||||
|
||||
Task<TEntity?> GetByIdAsync(int id, CancellationToken cancellationToken = default);
|
||||
Task<IEnumerable<TEntity>> GetAllAsync(CancellationToken cancellationToken = default);
|
||||
Task<IEnumerable<TEntity>> FindAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default);
|
||||
Task<TEntity?> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default);
|
||||
Task<bool> ExistsAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default);
|
||||
Task<int> CountAsync(Expression<Func<TEntity, bool>>? predicate = null, CancellationToken cancellationToken = default);
|
||||
|
||||
// ==================== CREATE OPERATIONS ====================
|
||||
|
||||
/// <summary>
|
||||
/// Create entity from DTO using AutoMapper and save immediately.
|
||||
/// </summary>
|
||||
/// <returns>Created entity with ID populated</returns>
|
||||
Task<TEntity> CreateAsync<TDto>(TDto dto, CancellationToken cancellationToken = default) where TDto : class;
|
||||
|
||||
// ==================== UPDATE OPERATIONS ====================
|
||||
|
||||
/// <summary>
|
||||
/// Update ALL entities matching expression using DTO via AutoMapper.
|
||||
/// WARNING: This can update multiple records. Use UpdateSingleAsync for single-record updates.
|
||||
/// </summary>
|
||||
/// <returns>Number of entities updated</returns>
|
||||
Task<int> UpdateAsync<TDto>(Expression<Func<TEntity, bool>> predicate, TDto dto, CancellationToken cancellationToken = default) where TDto : class;
|
||||
|
||||
/// <summary>
|
||||
/// Update SINGLE entity matching expression using DTO via AutoMapper.
|
||||
/// SAFETY: Throws exception if zero or multiple entities match the predicate.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">Thrown when zero or multiple entities match</exception>
|
||||
Task UpdateSingleAsync<TDto>(Expression<Func<TEntity, bool>> predicate, TDto dto, CancellationToken cancellationToken = default) where TDto : class;
|
||||
|
||||
// ==================== DELETE OPERATIONS ====================
|
||||
|
||||
/// <summary>
|
||||
/// Delete ALL entities matching expression.
|
||||
/// WARNING: This can delete multiple records. Use DeleteSingleAsync for single-record deletes.
|
||||
/// </summary>
|
||||
/// <returns>Number of entities deleted</returns>
|
||||
Task<int> DeleteAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Delete SINGLE entity matching expression.
|
||||
/// SAFETY: Throws exception if zero or multiple entities match the predicate.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">Thrown when zero or multiple entities match</exception>
|
||||
Task DeleteSingleAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
namespace DigitalData.EmailProfiler.Application.Interfaces.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// Unit of Work pattern for transaction management.
|
||||
/// </summary>
|
||||
public interface IUnitOfWork : IDisposable
|
||||
{
|
||||
IEmailAccountRepository EmailAccounts { get; }
|
||||
IEmailProfileRepository EmailProfiles { get; }
|
||||
IEmailProcessRepository EmailProcesses { get; }
|
||||
IEmailHistoryRepository EmailHistories { get; }
|
||||
IEmailOutboxRepository EmailOutbox { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Save all changes to the database.
|
||||
/// </summary>
|
||||
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Begin a database transaction.
|
||||
/// </summary>
|
||||
Task BeginTransactionAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Commit the current transaction.
|
||||
/// </summary>
|
||||
Task CommitTransactionAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Rollback the current transaction.
|
||||
/// </summary>
|
||||
Task RollbackTransactionAsync(CancellationToken cancellationToken = default);
|
||||
}
|
||||
Reference in New Issue
Block a user