OpenCQRS

Domain Service

The IDomainService interface provides a high-level API for managing aggregates and domain events in an event-sourced system. It abstracts the complexities of event storage, retrieval, and aggregate reconstruction, allowing developers to focus on business logic.

Every store provider has its own implementation of the IDomainService interface. You can use it by injecting the interface into your handlers, services, or controllers.

Available Methods

Save Aggregate

Saves an aggregate to the event store with optimistic concurrency control, persisting all uncommitted domain events and updating the aggregate snapshot.

New aggregate

var streamId = new CustomerStreamId(customerId);
var aggregateId = new OrderAggregateId(orderId);
var aggregate = new OrderAggregate(orderId, amount: 25.45m);

var saveAggregateResult = await domainService.SaveAggregate(streamId, aggregateId, aggregate, expectedEventSequence: 0);

Update existing aggregate

var streamId = new CustomerStreamId(customerId);
var aggregateId = new OrderAggregateId(orderId);
var latestEventSequence = await domainService.GetLatestEventSequence(streamId);

var aggregateResult = await domainService.GetAggregate(streamId, aggregateId);
if (!aggregateResult.IsSuccess)
{
    return aggregateResult.Error;
}
aggregate = aggregateResult.Value;

aggregate.UpdateAmount(amount: 15.00m);

var saveAggregateResult = await domainService.SaveAggregate(streamId, aggregateId, aggregate, expectedEventSequence: latestEventSequence);

Save Domain Events

Saves an array of domain events to the event store with optimistic concurrency control, bypassing aggregate persistence. This method is ideal for scenarios where events are generated outside traditional aggregate workflows.

var streamId = new CustomerStreamId(customerId);
var latestEventSequence = await domainService.GetLatestEventSequence(streamId);

var events = new @event[]
{
    new OrderPlaced
    {
        OrderId = orderId,
        Amount = 25.45m
    },
    new OrderShipped
    {
        OrderId = orderId,
        ShippedDate = _timeProvider.GetUtcNow()
    }
};
var saveEventsResult = await domainService.SaveEvents(streamId, events, expectedEventSequence: latestEventSequence);

Update Aggregate

Updates an existing aggregate with new events from its stream, applying any events that occurred after the aggregate’s last known state.

var streamId = new CustomerStreamId(customerId);
var aggregateId = new OrderAggregateId(orderId);
var updateAggregateResult = await domainService.UpdateAggregate(streamId, aggregateId);

Get Aggregate

Retrieves an aggregate from the event store, either from its snapshot or by reconstructing it from events.

If the aggregate does not exist, but domain events that can be applied to the aggregate exist, the aggregate snapshot is stored automatically. This is useful when the domain changes, and you need a different aggregate structure. Increase the version of the aggregate type to force a snapshot creation.

var streamId = new CustomerStreamId(customerId);
var aggregateId = new OrderAggregateId(orderId);
var aggregateResult = await domainService.GetAggregate(streamId, aggregateId);

Optionally, it can be forced to apply any new domain events that occurred after the snapshot was created. This is useful when you want to ensure the aggregate is up to date with the latest events. If new events are found, the aggregate snapshot is updated automatically.

var streamId = new CustomerStreamId(customerId);
var aggregateId = new OrderAggregateId(orderId);
var aggregateResult = await domainService.GetAggregate(streamId, aggregateId, applyNewEvents: true);

Get In-Memory Aggregate

Reconstructs an aggregate entirely from events without using snapshots, providing a pure event-sourced view of the aggregate state.

var streamId = new CustomerStreamId(customerId);
var aggregateId = new OrderAggregateId(orderId);
var aggregateResult = await domainService.GetInMemoryAggregate(streamId, aggregateId);

Optionally, you can specify a sequence number or a date to reconstruct the aggregate up to a specific point in time.

var aggregateResult = await domainService.GetInMemoryAggregate(streamId, aggregateId, upToSequence);

or

var aggregateResult = await domainService.GetInMemoryAggregate(streamId, aggregateId, upToDate);

Get Domain Events

Retrieves all domain events from a specified stream, with optional filtering by event types.

var streamId = new CustomerStreamId(customerId);
var eventsResult = await domainService.GetEvents(streamId);

Optionally, you can filter the events by specific event types.

var streamId = new CustomerStreamId(customerId);
var eventTypes = new Type[] { typeof(OrderPlaced), typeof(OrderShipped) };
var eventsResult = await domainService.GetEvents(streamId, eventTypes);

Get Domain Events From Sequence

Retrieves domain events from a specified stream starting from a specific sequence number onwards, with optional filtering by event types.

var streamId = new CustomerStreamId(customerId);
var fromSequence = 5;
var eventsResult = await domainService.GetEventsFromSequence(streamId, fromSequence);

Optionally, you can filter the events by specific event types.

var streamId = new CustomerStreamId(customerId);
var fromSequence = 5;
var eventTypes = new Type[] { typeof(OrderPlaced), typeof(OrderShipped) };
var eventsResult = await domainService.GetEventsFromSequence(streamId, fromSequence, eventTypes);

Get Domain Events Up To Sequence

Retrieves domain events from a specified stream up to and including a specific sequence number, with optional filtering by event types.

var streamId = new CustomerStreamId(customerId);
var upToSequence = 10;
var eventsResult = await domainService.GetEventsUpToSequence(streamId, upToSequence);

Optionally, you can filter the events by specific event types.

var streamId = new CustomerStreamId(customerId);
var upToSequence = 10;
var eventTypes = new Type[] { typeof(OrderPlaced), typeof(OrderShipped) };
var eventsResult = await domainService.GetEventsUpToSequence(streamId, upToSequence, eventTypes);

Get Domain Events Between Sequences

Retrieves domain events from a specified stream from and to specific sequence numbers, with optional filtering by event types.

var streamId = new CustomerStreamId(customerId);
var fromSequence = 5;
var toSequence = 10;
var eventsResult = await domainService.GetEventsBetweenSequences(streamId, fromSequence, toSequence);

Optionally, you can filter the events by specific event types.

var streamId = new CustomerStreamId(customerId);
var fromSequence = 5;
var toSequence = 10;
var eventTypes = new Type[] { typeof(OrderPlaced), typeof(OrderShipped) };
var eventsResult = await domainService.GetEventsBetweenSequences(streamId, fromSequence, toSequence, eventTypes);

Get Domain Events From Date

Retrieves domain events from a specified stream starting from a specific date onwards, with optional filtering by event types.

var streamId = new CustomerStreamId(customerId);
var fromDate = new DateTime(2024, 6, 15, 17, 45, 48);
var eventsResult = await domainService.GetEventsFromDate(streamId, fromDate);

Optionally, you can filter the events by specific event types.

var streamId = new CustomerStreamId(customerId);
var fromDate = new DateTime(2024, 6, 15, 17, 45, 48);
var eventTypes = new Type[] { typeof(OrderPlaced), typeof(OrderShipped) };
var eventsResult = await domainService.GetEventsFromDate(streamId, fromDate, eventTypes);

Get Domain Events Up To Date

Retrieves domain events from a specified stream up to and including a specific date, with optional filtering by event types.

var streamId = new CustomerStreamId(customerId);
var upToDate = new DateTime(2024, 6, 15, 17, 45, 48);
var eventsResult = await domainService.GetEventsUpToDate(streamId, upToDate);

Optionally, you can filter the events by specific event types.

var streamId = new CustomerStreamId(customerId);
var upToDate = new DateTime(2024, 6, 15, 17, 45, 48);
var eventTypes = new Type[] { typeof(OrderPlaced), typeof(OrderShipped) };
var eventsResult = await domainService.GetEventsUpToDate(streamId, upToDate, eventTypes);

Get Domain Events Between Dates

Retrieves domain events from a specified stream from and to specific dates, with optional filtering by event types.

var streamId = new CustomerStreamId(customerId);
var fromDate = new DateTime(2024, 6, 15, 17, 45, 48);
var toDate = new DateTime(2024, 6, 25, 12, 46, 22);
var eventsResult = await domainService.GetEventsBetweenDates(streamId, fromDate, toDate);

Optionally, you can filter the events by specific event types.

var streamId = new CustomerStreamId(customerId);
var fromDate = new DateTime(2024, 6, 15, 17, 45, 48);
var toDate = new DateTime(2024, 6, 25, 12, 46, 22);
var eventTypes = new Type[] { typeof(OrderPlaced), typeof(OrderShipped) };
var eventsResult = await domainService.GetEventsBetweenDates(streamId, fromDate, toDate, eventTypes);

Get Domain Events Applied To Aggregate

Retrieves all domain events that have been applied to a specific aggregate instance, using the explicit aggregate-event relationship tracking. This method provides precise access to the events that actually contributed to an aggregate’s current state.

var streamId = new CustomerStreamId(customerId);
var aggregateId = new OrderAggregateId(orderId);
var eventsResult = await domainService.GetEventsAppliedToAggregate(streamId, aggregateId);

Get Latest Event Sequence

Retrieves the latest event sequence number for a specified stream, with optional filtering by event types. This method provides the current position in an event stream, essential for optimistic concurrency control and determining where to append new events in event sourcing operations.

var streamId = new CustomerStreamId(customerId);
var latestEventSequence = await domainService.GetLatestEventSequence(streamId);

Optionally, you can filter the events by specific event types.

var streamId = new CustomerStreamId(customerId);
var eventTypes = new Type[] { typeof(OrderPlaced), typeof(OrderShipped) };
var latestEventSequence = await domainService.GetLatestEventSequence(streamId, eventTypes);