Skip to content

Attribute Reference

This page documents the attributes and interfaces that define an event-sourced domain model for the Event Store module.

[EventStore]

Marks a static partial class as the event store container. The generator discovers all [EventSourcedAggregate] and [Projection] types in the assembly.

Property Type Default Description
Instrumented bool true Enable OpenTelemetry tracing spans via .WithActivity()
EventBaseType Type? null Custom event base type (defaults to IAggregateEvent)
Schema string? null PostgreSQL schema name. Defaults to store class name in snake_case (e.g., AppEventStoreapp_event_store).
[EventStore]
public static partial class AppEventStore;

// Custom schema name
[EventStore(Schema = "orders")]
public static partial class OrderingEventStore;

// Custom event base type
[EventStore(EventBaseType = typeof(IMyDomainEvent))]
public static partial class AppEventStore;
Requirement Diagnostic
Must be partial DSES01 (Error)
Should be static DSES02 (Warning)
One per assembly DSES04 (Error)

[EventSourcedAggregate]

Marks a partial class or record as an event-sourced aggregate.

Property Type Default Description
PluralName string? Inferred Override the nested class name (default: TypeName + "s")
[EventSourcedAggregate]
public partial record Order(OrderId Id, string CustomerName, int ItemCount);

// Custom plural name
[EventSourcedAggregate(PluralName = "People")]
public partial record Person(PersonId Id, string Name);
Requirement Diagnostic
Must be partial DSES03 (Error)
Must have a [StreamId]-typed property DSES06 (Error)
Requires [EventStore] in assembly DSES05 (Info)

[StreamId]

Companion attribute for [TypedId]. Generates StreamName property and FromStreamName() parser on the ID struct.

Property Type Default Description
Prefix string? Inferred Stream name prefix (default: type name without "Id" suffix, lowercase)
Separator string "-" Separator between prefix and value
[TypedId]
[StreamId]
public readonly partial struct OrderId;

// Generated:
//   orderId.StreamName → "order-550e8400-e29b-41d4-a716-446655440000"
//   OrderId.FromStreamName("order-550e8400-...") → OrderId

IAggregateEvent

Marker interface for domain events. All events appended to aggregate streams should implement this.

public sealed record OrderCreated(OrderId OrderId, string CustomerName) : IAggregateEvent;
public sealed record OrderItemAdded(string Sku, int Quantity) : IAggregateEvent;

Event Sourcing Conventions

Aggregates use a convention-based Create/Apply pattern:

Method Signature Purpose
Create static T Create(TEvent) Factory — builds the aggregate from the first event
Apply T Apply(TEvent) Updates aggregate state from subsequent events
ShouldDelete bool ShouldDelete(TEvent) (Optional) Returns true to archive the stream

Method resolution is cached per aggregate type via AggregateFold<T>. One Create overload per event type, one Apply overload per event type.

[EventSourcedAggregate]
public partial record Order(OrderId Id, string CustomerName, int ItemCount, bool IsCompleted)
{
    // First event — creates the aggregate
    public static Order Create(OrderCreated e) =>
        new(e.OrderId, e.CustomerName, 0, false);

    // Subsequent events — update state
    public Order Apply(OrderItemAdded e) =>
        this with { ItemCount = ItemCount + e.Quantity };

    public Order Apply(OrderCompleted _) =>
        this with { IsCompleted = true };
}

See Generated Code for the interfaces and implementations these attributes produce, or Effects for the Eff<RT, T> composition layer.