Table of Contents

Class EfCoreTracedAggregateRepository<TEntity, TKey>

Namespace
Savvyio.Extensions.EFCore.Domain.EventSourcing
Assembly
Savvyio.Extensions.EFCore.Domain.EventSourcing.dll

Provides an implementation of the EfCoreRepository<TEntity, TKey> that is optimized for Domain Driven Design and Event Sourcing.

public class EfCoreTracedAggregateRepository<TEntity, TKey> : ITracedAggregateRepository<TEntity, TKey>, IReadableRepository<TEntity, TKey>, IWritableRepository<TEntity, TKey>, IRepository<TEntity, TKey> where TEntity : class, IEntity<TKey>, ITracedAggregateRoot<TKey>

Type Parameters

TEntity

The type of the entity that implements the ITracedAggregateRoot<TKey> interface.

TKey

The type of the key that uniquely identifies the entity.

Inheritance
EfCoreTracedAggregateRepository<TEntity, TKey>
Implements
IReadableRepository<TEntity, TKey>
IWritableRepository<TEntity, TKey>
IRepository<TEntity, TKey>
Derived

Examples

EfCoreTracedAggregateRepository<TAggregateRoot, TKey, TContext> stores and loads event-sourced aggregates by writing and reading individual traced domain event rows. The setup requires a context configured with ModelBuilder.AddEventSourcing, an IMarshaller for event serialization, and the aggregate type with RegisterDelegates implemented. The example creates a repository, appends an event, and rehydrates the aggregate from stored events.

using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Savvyio;
using Savvyio.Domain.EventSourcing;
using Savvyio.Extensions.EFCore;
using Savvyio.Extensions.EFCore.Domain.EventSourcing;
using Savvyio.Handlers;

namespace ExampleApp;

public sealed class EventSourcingWorkflow
{
    public Task<OrderTimeline> LoadAsync(Guid id)
    {
        var source = new EfCoreDataSource(new EfCoreDataSourceOptions
        {
            ContextConfigurator = builder => builder.EnableDetailedErrors(),
            ModelConstructor = modelBuilder => modelBuilder.AddEventSourcing<OrderTimeline, Guid>()
        });

        var repository = new EfCoreTracedAggregateRepository<OrderTimeline, Guid>(source, new SimpleMarshaller());
        repository.Add(new OrderTimeline(id, "PO-8001"));

        return repository.GetByIdAsync(id);
    }
}

public sealed class OrderTimeline : TracedAggregateRoot<Guid>
{
    public OrderTimeline(Guid id, string orderNumber) : base()
    {
        AddEvent(new OrderPlaced(id, orderNumber));
    }

    private OrderTimeline(Guid id, IEnumerable<ITracedDomainEvent> events) : base(id, events)
    {
    }

    public string OrderNumber { get; private set; } = string.Empty;

    protected override void RegisterDelegates(IFireForgetRegistry<ITracedDomainEvent> handler)
    {
        handler.Register<OrderPlaced>(e =>
        {
            Id = e.OrderId;
            OrderNumber = e.OrderNumber;
        });
    }
}

public sealed record OrderPlaced(Guid OrderId, string OrderNumber) : TracedDomainEvent;

public sealed class SimpleMarshaller : IMarshaller
{
    public Stream Serialize<TValue>(TValue value)
    {
        return new MemoryStream(JsonSerializer.SerializeToUtf8Bytes(value));
    }

    public Stream Serialize(object value, Type inputType)
    {
        return new MemoryStream(JsonSerializer.SerializeToUtf8Bytes(value, inputType));
    }

    public TValue Deserialize<TValue>(Stream data)
    {
        return JsonSerializer.Deserialize<TValue>(data)!;
    }

    public object Deserialize(Stream data, Type returnType)
    {
        return JsonSerializer.Deserialize(data, returnType)!;
    }
}

Constructors

EfCoreTracedAggregateRepository(IEfCoreDataSource, IMarshaller)

Initializes a new instance of the EfCoreTracedAggregateRepository<TEntity, TKey> class.

public EfCoreTracedAggregateRepository(IEfCoreDataSource source, IMarshaller marshaller)

Parameters

source IEfCoreDataSource

The IEfCoreDataSource that handles actual I/O communication with a source of data.

marshaller IMarshaller

The IMarshaller that is used when converting between ITracedDomainEvent and arbitrary data.

Methods

Add(TEntity)

Marks the specified entity to be added in the data store when SaveChangesAsync(Action<AsyncOptions>) is called.

public void Add(TEntity entity)

Parameters

entity TEntity

The aggregate to add.

AddRange(IEnumerable<TEntity>)

Marks the specified entities to be added in the data store when SaveChangesAsync(Action<AsyncOptions>) is called.

public void AddRange(IEnumerable<TEntity> entities)

Parameters

entities IEnumerable<TEntity>

The aggregates to add.

GetByIdAsync(TKey, Action<AsyncOptions>)

Loads an aggregate from the specified id asynchronous.

public Task<TEntity> GetByIdAsync(TKey id, Action<AsyncOptions> setup = null)

Parameters

id TKey

The key that uniquely identifies the aggregate.

setup Action<AsyncOptions>

The AsyncOptions which may be configured.

Returns

Task<TEntity>

A Task<TResult> that represents the asynchronous operation. The task result either contains the entity of the operation or null if not found.

Exceptions

MissingMethodException

TEntity does not have a suitable constructor.

See Also

IReadableRepository<TEntity, TKey>
IWritableRepository<TEntity, TKey>