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.
Why close a stream?
Section titled “Why close a stream?”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.
How to close a stream
Section titled “How to close a stream”Call CompleteStream on the event log with the stream type and stream identifier you want to close:
using Cratis.Chronicle.EventSequences;
public class ClosingStreamsInvoiceCloser(IEventLog eventLog){ public async Task CloseInvoiceStream(EventStreamId invoiceStreamId) { var result = await eventLog.CompleteStream(new EventStreamType("invoices"), invoiceStreamId);
result.Switch( sequenceNumber => Console.WriteLine($"Stream closed at sequence number {sequenceNumber}"), error => Console.WriteLine($"Failed to close stream: {error}")); }}The method returns Result<EventSequenceNumber, CompleteStreamError>.
Error cases
Section titled “Error cases”| Error | Meaning |
|---|---|
AlreadyCompleted | The stream has already been closed. |
DefaultStreamCannotBeCompleted | The default stream (EventStreamType.All / EventStreamId.Default) cannot be closed. |
What happens after closing
Section titled “What happens after closing”After a stream is closed, any append targeting that stream is rejected with a StreamClosed constraint violation:
using Cratis.Chronicle.Events;using Cratis.Chronicle.Events.Constraints;using Cratis.Chronicle.EventSequences;using System.Linq;
[EventType]public record ClosingStreamsInvoiceLineAdded(string Description, decimal Amount);
public class ClosingStreamsInvoiceLineAppender(IEventLog eventLog){ public async Task<bool> TryAppendLine(EventSourceId invoiceId) { var appendResult = await eventLog.Append( invoiceId, new ClosingStreamsInvoiceLineAdded("Consulting", 500m), new EventStreamType("invoices"), new EventStreamId("invoice-42"));
if (!appendResult.IsSuccess) { var violation = appendResult.ConstraintViolations .FirstOrDefault(v => v.ConstraintType == ConstraintType.StreamClosed); return violation is null; }
return true; }}The rejection is enforced by the ClosedStreamConstraintValidator which is automatically active for every event sequence — no additional configuration is required.