Refactor domain layer to follow Clean Architecture

Updated the domain layer to align with Clean Architecture principles:
- Removed Entity Framework dependencies from the project.
- Updated AGENTS.md to document the new architecture.
- Introduced repository interfaces for data access abstraction.
- Added a generic IRepository interface for CRUD operations.
- Implemented entity-specific repositories for Profile, ProfileSqlJob, and ProfileHistory.
- Ensured the domain layer is infrastructure-independent with pure POCOs.
- Updated project structure and documentation for clarity.
This commit is contained in:
2026-07-09 13:33:39 +02:00
parent ce47653831
commit 3e007ccfeb
6 changed files with 230 additions and 75 deletions

View File

@@ -0,0 +1,12 @@
using ECMJobRunner.Domain.Entities;
namespace ECMJobRunner.Domain.Interfaces
{
/// <summary>
/// Repository interface for CfgProfile entity
/// </summary>
public interface ICfgProfileRepository : IRepository<CfgProfile>
{
// Add custom CfgProfile-specific methods here if needed
}
}

View File

@@ -0,0 +1,12 @@
using ECMJobRunner.Domain.Entities;
namespace ECMJobRunner.Domain.Interfaces
{
/// <summary>
/// Repository interface for ProfileHistory entity
/// </summary>
public interface IProfileHistoryRepository : IRepository<ProfileHistory>
{
// Add custom ProfileHistory-specific methods here if needed
}
}

View File

@@ -0,0 +1,12 @@
using ECMJobRunner.Domain.Entities;
namespace ECMJobRunner.Domain.Interfaces
{
/// <summary>
/// Repository interface for ProfileSqlJob entity
/// </summary>
public interface IProfileSqlJobRepository : IRepository<ProfileSqlJob>
{
// Add custom ProfileSqlJob-specific methods here if needed
}
}

View File

@@ -0,0 +1,99 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
namespace ECMJobRunner.Domain.Interfaces
{
/// <summary>
/// Generic repository interface for data access operations
/// </summary>
/// <typeparam name="TEntity">Entity type</typeparam>
public interface IRepository<TEntity> where TEntity : class
{
/// <summary>
/// Get entity by ID asynchronously
/// </summary>
Task<TEntity?> GetByIdAsync(long id, CancellationToken cancellationToken = default);
/// <summary>
/// Get all entities asynchronously
/// </summary>
Task<IEnumerable<TEntity>> GetAllAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Find entities by predicate asynchronously
/// </summary>
Task<IEnumerable<TEntity>> FindAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default);
/// <summary>
/// Get single entity by predicate asynchronously
/// </summary>
Task<TEntity?> SingleOrDefaultAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default);
/// <summary>
/// Add new entity from DTO asynchronously
/// Maps DTO to entity and adds it
/// </summary>
/// <typeparam name="TDto">DTO type</typeparam>
/// <param name="dto">DTO containing values for new entity</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Created entity</returns>
Task<TEntity> AddAsync<TDto>(TDto dto, CancellationToken cancellationToken = default) where TDto : class;
/// <summary>
/// Add multiple entities from DTOs asynchronously
/// Maps DTOs to entities and adds them
/// </summary>
/// <typeparam name="TDto">DTO type</typeparam>
/// <param name="dtos">DTOs containing values for new entities</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Number of entities added</returns>
Task<int> AddRangeAsync<TDto>(IEnumerable<TDto> dtos, CancellationToken cancellationToken = default) where TDto : class;
/// <summary>
/// Update entities matching predicate with DTO values asynchronously
/// Maps DTO properties onto matching entities
/// </summary>
/// <typeparam name="TDto">DTO type</typeparam>
/// <param name="predicate">Predicate to find entities</param>
/// <param name="dto">DTO containing values to update</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <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 predicate with DTO values asynchronously
/// Throws exception if multiple entities match
/// </summary>
/// <typeparam name="TDto">DTO type</typeparam>
/// <param name="predicate">Predicate to find entity</param>
/// <param name="dto">DTO containing values to update</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>True if entity was found and updated, false otherwise</returns>
Task<bool> UpdateSingleAsync<TDto>(Expression<Func<TEntity, bool>> predicate, TDto dto, CancellationToken cancellationToken = default) where TDto : class;
/// <summary>
/// Delete entities matching predicate asynchronously (hard delete)
/// </summary>
/// <param name="predicate">Predicate to find entities to delete</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Number of entities deleted</returns>
Task<int> DeleteAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default);
/// <summary>
/// Delete single entity matching predicate asynchronously (hard delete)
/// Throws exception if multiple entities match
/// </summary>
/// <param name="predicate">Predicate to find entity to delete</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>True if entity was found and deleted, false otherwise</returns>
Task<bool> DeleteSingleAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default);
/// <summary>
/// Save all changes asynchronously
/// </summary>
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
}
}