Skip to content

Implement BaseEntity and BaseRepository Pattern for Consistent Data Layer Architecture #106

@hootanht

Description

@hootanht

📋 Issue Summary

The current data layer architecture lacks consistency and standardization. We need to implement a BaseEntity class and BaseRepository pattern to establish a solid foundation for all domain entities and data access operations.

🎯 Current Problems

1. Inconsistent Entity Structure

  • All entities use int IDs instead of long for better scalability
  • No audit trail fields (CreatedAt, UpdatedAt, CreatedBy, UpdatedBy)
  • Each entity has its own ID property without inheritance
  • Missing standardized soft delete pattern across entities

2. Repository Code Duplication

  • Only AuthorRepository exists with repetitive CRUD operations
  • No generic base repository for common operations
  • Future entities will require duplicate CRUD implementations
  • No standardized approach for data access patterns

3. Missing Audit Capabilities

  • No tracking of who created/modified records
  • No timestamps for data change tracking
  • Missing compliance and accountability features

🚀 Proposed Solution

BaseEntity Class Implementation

Create a BaseEntity abstract class in Data/Models/ with the following properties:

public abstract class BaseEntity
{
    public long Id { get; set; }
    public DateTimeOffset CreatedAt { get; set; }
    public DateTimeOffset UpdatedAt { get; set; }
    public string? CreatedBy { get; set; }
    public string? UpdatedBy { get; set; }
}

Key Benefits:

  • Long ID: Better scalability for high-volume applications
  • DateTimeOffset: Timezone-aware timestamps for global applications
  • Audit Fields: Track who and when made changes
  • Nullable User Fields: Support anonymous/system operations

BaseRepository Interface & Implementation

Create generic repository pattern in Service/Interface/ and Service/Implement/:

public interface IBaseRepository<TEntity> where TEntity : BaseEntity
{
    // Read Operations
    Task<TEntity?> GetByIdAsync(long id, CancellationToken ct = default);
    Task<IEnumerable<TEntity>> GetAllAsync(CancellationToken ct = default);
    Task<IEnumerable<TEntity>> GetPagedAsync(int page, int pageSize, CancellationToken ct = default);
    
    // Write Operations
    Task<TEntity> AddAsync(TEntity entity, CancellationToken ct = default);
    Task<TEntity> UpdateAsync(TEntity entity, CancellationToken ct = default);
    Task DeleteAsync(long id, CancellationToken ct = default);
    Task<bool> ExistsAsync(long id, CancellationToken ct = default);
    
    // Bulk Operations
    Task AddRangeAsync(IEnumerable<TEntity> entities, CancellationToken ct = default);
    Task UpdateRangeAsync(IEnumerable<TEntity> entities, CancellationToken ct = default);
    Task DeleteRangeAsync(IEnumerable<long> ids, CancellationToken ct = default);
}

📋 Implementation Tasks

Phase 1: Core Infrastructure

  • Create BaseEntity abstract class in Data/Models/BaseEntity.cs
  • Create IBaseRepository<T> interface in Service/Interface/IBaseRepository.cs
  • Create BaseRepository<T> implementation in Service/Implement/BaseRepository.cs
  • Register BaseRepository in Tools/ExtentionMethod/AddServiceExtentionMethod.cs

Phase 2: Entity Migration

  • Update Book entity to inherit from BaseEntity
  • Update Author entity to inherit from BaseEntity
  • Update Category entity to inherit from BaseEntity
  • Update Keyword entity to inherit from BaseEntity
  • Review junction entities (BookAuthor, BookKeyword, BookRelation) for BaseEntity needs

Phase 3: Repository Refactoring

  • Update IAuthorRepository to inherit from IBaseRepository<Author>
  • Refactor AuthorRepository to inherit from BaseRepository<Author>
  • Create additional repositories (BookRepository, CategoryRepository, etc.)
  • Update service registration in DI container

Phase 4: Database Migration

  • Generate EF migration for ID type changes (intlong)
  • Generate EF migration for new audit fields
  • Update Entity Framework configurations in Data/Configuration/
  • Test migration scripts thoroughly

Phase 5: Application Updates

  • Update ViewModels to use long IDs
  • Update Controllers to handle long IDs
  • Update Service layer to populate audit fields
  • Update URL routing for long ID parameters

🔧 Technical Considerations

Entity Framework Configuration

public override int SaveChanges()
{
    UpdateAuditFields();
    return base.SaveChanges();
}

private void UpdateAuditFields()
{
    var entries = ChangeTracker.Entries<BaseEntity>();
    var currentUser = _httpContextAccessor.HttpContext?.User?.Identity?.Name;
    var now = DateTimeOffset.UtcNow;
    
    foreach (var entry in entries)
    {
        switch (entry.State)
        {
            case EntityState.Added:
                entry.Entity.CreatedAt = now;
                entry.Entity.CreatedBy = currentUser;
                entry.Entity.UpdatedAt = now;
                entry.Entity.UpdatedBy = currentUser;
                break;
            case EntityState.Modified:
                entry.Entity.UpdatedAt = now;
                entry.Entity.UpdatedBy = currentUser;
                break;
        }
    }
}

Backward Compatibility

  • Plan migration strategy for existing data
  • Consider creating database backup before major changes
  • Implement gradual rollout approach
  • Update existing API consumers about ID type changes

Performance Considerations

  • Use appropriate indexing for audit fields
  • Consider soft delete implementation in BaseEntity
  • Optimize queries for common filtering patterns
  • Plan for audit log table if detailed history needed

🎯 Expected Benefits

  1. Consistency: Standardized entity structure across application
  2. DRY Principle: Eliminate repository code duplication
  3. Maintainability: Centralized data access patterns
  4. Scalability: Long IDs support high-volume growth
  5. Auditability: Built-in change tracking and accountability
  6. Developer Experience: Faster development of new entities

📚 References

  • Current Entities: Data/Models/Book.cs, Author.cs, Category.cs, Keyword.cs
  • Existing Repository: Service/Implement/AuthorRepository.cs
  • Service Registration: Tools/ExtentionMethod/AddServiceExtentionMethod.cs
  • EF Configurations: Data/Configuration/ folder
  • Architecture Guidelines: .github/copilot-instructions.md

🏷️ Labels

enhancement architecture data-layer technical-debt repository-pattern


Priority: High - This foundational change will impact all future development
Effort: Large - Requires careful planning and migration strategy
Impact: High - Improves code quality, maintainability, and developer productivity

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions