Skip to content

Closing Streams

Event streams can be permanently closed to prevent further appends. Once a stream is closed, any attempt to append events to it will result in a constraint violation of type StreamClosed.

Closing a stream is useful when a logical unit of work is complete and no further events should be recorded for that stream. Examples include:

  • Finalizing an invoice — once issued, no further line items should be added.
  • Archiving a case — the case is resolved and the event history is sealed.
  • Completing an order — the order lifecycle has ended and further mutations are disallowed.

Call CompleteStream on the event log with the stream type and stream identifier you want to close:

var result = await eventLog.CompleteStream(
new EventStreamType("invoices"),
new EventStreamId("invoice-42"));
result.Match(
sequenceNumber => Console.WriteLine($"Stream closed at sequence number {sequenceNumber}"),
error => Console.WriteLine($"Failed to close stream: {error}"));

The method returns Result<EventSequenceNumber, CompleteStreamError>.

ErrorMeaning
AlreadyCompletedThe stream has already been closed.
DefaultStreamCannotBeCompletedThe default stream (EventStreamType.All / EventStreamId.Default) cannot be closed.

After a stream is closed, any append targeting that stream is rejected with a StreamClosed constraint violation:

var appendResult = await eventLog.Append(
eventSourceId,
new SomeEvent("data"),
new EventStreamType("invoices"),
new EventStreamId("invoice-42"));
if (!appendResult.IsSuccess)
{
var violation = appendResult.ConstraintViolations
.FirstOrDefault(v => v.ConstraintType == ConstraintType.StreamClosed);
// violation is non-null; stream is closed
}

The rejection is enforced by the ClosedStreamConstraintValidator which is automatically active for every event sequence — no additional configuration is required.

You can query the current status of a stream at any time:

bool isClosed = await eventLog.IsStreamCompleted(
new EventStreamType("invoices"),
new EventStreamId("invoice-42"));