TaskHub.Shared

TaskHub.Shared.Persistence.EntityFramework - Technical Manual

The TaskHub.Shared.Persistence.EntityFramework module is a sophisticated abstraction over EF Core designed for high-performance, observable microservices. It enforces architectural consistency by integrating the Outbox pattern and Media management directly into the database transaction lifecycle.

πŸ› Deep Architecture

1. ContextBase Internals

ContextBase<TEntity> is the primary abstract class that your service-specific DbContext must inherit from. It handles several critical responsibilities:

2. UnitOfWorkBase & The Event Pipeline

The most powerful feature of this module is how it bridges the Domain Layer and the Persistence Layer. The UnitOfWorkBase class overrides the standard saving mechanism:

  1. Change Tracker Inspection: Before SaveChangesAsync, it scans the EF Core Change Tracker for all entities implementing IAggregate.
  2. Domain Event Extraction: It extracts all pending IDomainEvents from these aggregates.
  3. Outbox Conversion: If the Outbox feature is enabled, these events are serialized into OutboxMessage records using the IOutboxMessageFactory.
  4. Atomic Transaction: Both your business data changes and the new OutboxMessage records are committed in a single database transaction. This guarantees that an event is never missed if the data change succeeds.
  5. Event Clearing: After a successful save, ClearEvents() is called on the aggregates to prevent duplicate event creation.

πŸ›  API Reference

ContextBase<TEntity>

| Method | Description | | :β€” | :β€” | | protected override void OnModelCreating(ModelBuilder builder) | Orchestrates assembly scanning and conditional feature configuration. | | DbSet<TEntity> Data | A shortcut to the primary entity set. |

UnitOfWorkBase<TContext>

| Method | Description | | :β€” | :β€” | | public async Task SaveAsync(CancellationToken ct) | The main entry point for transactional saves. Handles Outbox integration. |

PersistenceOptions

| Property | Type | Description | | :β€” | :β€” | :β€” | | ConnectionString | string | The database connection string. | | Outbox | OutboxOptions | Settings for the Outbox pattern. | | Media | MediaOptions | Settings for media metadata tracking. |


πŸš€ Real-World Implementation Examples

1. Defining a Domain-Driven Context

using Microsoft.EntityFrameworkCore;
using TaskHub.Shared.Persistence.EntityFramework.Context;
using TaskHub.Shared.Persistence.EntityFramework.Options;

namespace TaskHub.ProjectService.Infrastructure.Persistence;

public class ProjectDbContext(ContextOptions settings) : ContextBase<Project>(settings)
{
    // Primary set 'Data' inherited from base
    public DbSet<TaskItem> TaskItems => Set<TaskItem>();
}

2. Custom Entity Configuration

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

public class ProjectConfiguration : IEntityTypeConfiguration<Project>
{
    public void Configure(EntityTypeBuilder<Project> builder)
    {
        builder.ToTable("Projects");
        builder.HasKey(x => x.Id);
        
        // Automatic Value Object mapping example
        builder.Property(x => x.Title)
            .HasConversion(x => x.Value, x => new Title(x))
            .HasMaxLength(200);
            
        builder.HasMany(x => x.Tasks)
            .WithOne()
            .HasForeignKey(x => x.ProjectId);
    }
}

3. Using the Unit of Work in a Command Handler

public class CompleteProjectHandler(IUnitOfWork unitOfWork, IProjectRepository repository) 
    : ICommandHandler<CompleteProjectCommand, Result>
{
    public async Task<Result> HandleAsync(CompleteProjectCommand cmd, CancellationToken ct)
    {
        var project = await repository.GetAsync(cmd.Id, ct);
        if (project == null) return ResultFactory.OnFailed(404, "Project not found");

        project.MarkAsComplete(); // This internally adds a Domain Event
        
        // This single call will save the Project AND the OutboxMessage for the event
        await unitOfWork.SaveAsync(ct);
        
        return ResultFactory.OnSuccess();
    }
}

βš™οΈ Configuration Schema

"Persistence": {
  "ConnectionString": "Host=localhost;Database=TaskHub;Username=admin;Password=secret",
  "Outbox": {
    "IsEnabled": true,
    "TableName": "OutboxMessages",
    "BatchSize": 100,
    "MaxRetryCount": 5,
    "ProcessingInterval": "00:00:10"
  },
  "Media": {
    "IsEnabled": true,
    "TableName": "MediaMetadata"
  }
}

Field Descriptions:


πŸ‘ Telemetry & Diagnostics

OpenTelemetry Spans

The module utilizes standard EF Core instrumentation, creating spans for:

Tags


βœ… Best Practices & Anti-Patterns

🟒 Best Practices

πŸ”΄ Anti-Patterns