Skip to content

Effects Composition

This page covers the Eff<RT, T> effect layer generated by the Event Store module — capability interfaces, effect methods, stream extensions, runtime integration, and OpenTelemetry instrumentation.

Capability Interface

A single capability interface is generated per event store:

public interface IHasAppEventStore : IAutoCommittable
{
    IOrderEventStore OrderEventStore { get; }
    // ... one property per aggregate
}

Use as a runtime constraint for effect methods. The capability interface itself extends IAutoCommittable for transactional commit across all aggregate stores.

Effect Methods

Effect methods are generated as nested static classes on the event store container:

public static partial class AppEventStore
{
    // Commit all pending changes across all stores
    public static Eff<RT, Unit> Commit<RT>()
        where RT : IHasAppEventStore => ...

    public static partial class Orders
    {
        public static Eff<RT, IEventStream<Order>> FetchForWriting<RT>(OrderId id)
            where RT : IHasAppEventStore => ...

        public static Eff<RT, Option<Order>> Aggregate<RT>(OrderId id)
            where RT : IHasAppEventStore => ...

        public static Eff<RT, Unit> StartStream<RT>(OrderId id, object @event)
            where RT : IHasAppEventStore => ...

        public static Eff<RT, OrderId> StartStream<RT>(object @event)
            where RT : IHasAppEventStore => ...

        public static Eff<RT, Unit> Append<RT>(OrderId id, object @event)
            where RT : IHasAppEventStore => ...

        public static Eff<RT, IReadOnlyList<EventEnvelope>> FetchStream<RT>(OrderId id)
            where RT : IHasAppEventStore => ...
    }
}

Nullable → Option

Effect methods wrap nullable results in Option<T>. AggregateAsync returns Task<Order?> on the interface but Eff<RT, Option<Order>> in effects.

Stream Extensions

The generator also produces extension methods for fluent event appending in Eff pipelines:

public static class AppEventStoreStreamExtensions
{
    public static Eff<RT, Unit> Append<RT>(
        this IEventStream<Order> stream, object @event)
        where RT : IHasAppEventStore { ... }

    public static Eff<RT, Unit> AppendMany<RT>(
        this IEventStream<Order> stream, IEnumerable<object> events)
        where RT : IHasAppEventStore { ... }
}

Use with FetchForWriting for the fetch-append-commit pattern:

var program =
    from stream in AppEventStore.Orders.FetchForWriting<AppRuntime>(orderId)
    from _ in stream.Append<AppRuntime>(new OrderItemAdded("SKU-001", 3))
              >> AppEventStore.Commit<AppRuntime>()
    select unit;

Usage in Handlers

public static class OrderCommands
{
    [CommandHandler]
    public static Eff<AppRuntime, OrderCreated> Handle(CreateOrder cmd) =>
        from id in AppEventStore.Orders.StartStream<AppRuntime>(
            new OrderCreated(OrderId.New(), cmd.CustomerName))
        select new OrderCreated(id);

    [CommandHandler]
    public static Eff<AppRuntime, Unit> Handle(AddOrderItem cmd) =>
        from stream in AppEventStore.Orders.FetchForWriting<AppRuntime>(cmd.OrderId)
        from _ in stream.Append<AppRuntime>(new OrderItemAdded(cmd.Sku, cmd.Quantity))
        select unit;
}

Auto-commit is handled by the Dispatch moduleCommitAsync() is called automatically after each command handler.

Using with Runtime

Declare the event store on your runtime with [Uses]:

[Runtime]
[Uses(typeof(AppEventStore))]
public sealed partial class AppRuntime;

The runtime bootstrapper registers all event stores and configures OpenTelemetry tracing automatically.

OpenTelemetry Instrumentation

When Instrumented = true (the default on [EventStore]), each effect method is wrapped with .WithActivity():

  • Creates an Activity span from ActivitySource("{Namespace}.{StoreName}", "1.0.0")
  • Span names include the aggregate and operation (e.g., Orders.FetchForWriting, Orders.Append)
  • The bootstrapper registers the activity source with the OpenTelemetry tracing pipeline

Set Instrumented = false to skip instrumentation:

[EventStore(Instrumented = false)]
public static partial class AppEventStore;

See Generated Code for the plain .NET interfaces, or Attributes for the full attribute reference.