Установка
dotnet add package Koto.EventSourcing.Marten
Что внутри
| Тип | Назначение |
|---|---|
EventSourcedAggregateRoot<TId> | Базовый класс с RaiseEvent, Apply и Reconstitute |
IEventSourcedRepository<TAgg, TId> | Контракт персистентности для event-sourced агрегатов |
MartenEventSourcedRepository<TAgg, TId> | Реализация на Marten |
InlineProjection<TReadModel, TId> | База для инлайновых single-stream проекций |
AsyncProjection<TReadModel, TId> | База для асинхронных daemon single-stream проекций |
AddKotoMarten() | Регистрирует Marten и generic-репозиторий |
Использование
1 — Event-sourced агрегат
public sealed class Order : EventSourcedAggregateRoot<OrderId>
{
public decimal Total { get; private set; }
public bool IsShipped { get; private set; }
public static Order Place(OrderId id, decimal total)
{
var order = new Order();
order.RaiseEvent(new OrderPlaced(Guid.NewGuid(), DateTime.UtcNow, id, total));
return order;
}
public void Ship() => RaiseEvent(new OrderShipped(Guid.NewGuid(), DateTime.UtcNow));
protected override void Apply(IDomainEvent @event)
{
switch (@event)
{
case OrderPlaced e: Total = e.Total; break;
case OrderShipped: IsShipped = true; break;
}
}
private Order() { }
}
2 — Регистрация
builder.Services.AddKotoMarten(
builder.Configuration.GetConnectionString("Db")!,
opts =>
{
// опциональная дополнительная настройка Marten
opts.Projections.Add<OrderSummaryProjection>(ProjectionLifecycle.Async);
});
3 — Репозиторий
public class PlaceOrderHandler : ICommandHandler<PlaceOrderCommand, Result<OrderId>>
{
private readonly IEventSourcedRepository<Order, OrderId> _orders;
public async Task<Result<OrderId>> Handle(PlaceOrderCommand cmd, CancellationToken ct)
{
var order = Order.Place(OrderId.New(), cmd.Total);
await _orders.SaveAsync(order, ct);
return order.Id;
}
}
4 — Проекция
public sealed class OrderSummaryProjection : AsyncProjection<OrderSummary, Guid>
{
public OrderSummary Create(OrderPlaced e) => new(e.OrderId, e.Total, false);
public OrderSummary Apply(OrderShipped _, OrderSummary current) =>
current with { IsShipped = true };
}
Marten вместе с EF Core
Marten и Koto.Infrastructure.EFCore могут делить один экземпляр PostgreSQL. Marten управляет потоками событий (mt_events, mt_streams); EF Core — read-моделями и остальными сущностями.