Closing stream constraint
Adorn the event type class with [ClosesStream] to close a stream scope whenever an event of that type is appended. The closure is written in the same step as the event, and later appends inside the scope are rejected with a StreamClosed violation. For the concept, the guarantees and when to prefer it over CompleteStream, see Closing events.
Chronicle discovers [ClosesStream]-adorned types automatically and registers the constraints with the Kernel when the client starts.
Defining a closing constraint
Section titled “Defining a closing constraint”Choose the dimensions that make up the closed scope:
using Cratis.Chronicle.EventSequences;using Cratis.Chronicle.Events;using Cratis.Chronicle.Events.Constraints;
[EventType][ClosesStream(Dimensions = ClosedStreamDimensions.EventSourceId | ClosedStreamDimensions.EventStreamType)]public record ConstraintsModelBoundClosingReportDelivered(string Recipient);Dimensions is a ClosedStreamDimensions flags value: EventSourceId, EventSourceType, EventStreamType and EventStreamId. Without it the scope is event source id, stream type and stream id of the append.
Take the stream id from a property
Section titled “Take the stream id from a property”Set EventStreamIdFrom to the name of an event property to use its value as the stream id. This implies the EventStreamId dimension:
using Cratis.Chronicle.Events;using Cratis.Chronicle.Events.Constraints;
[EventType][ClosesStream(EventStreamIdFrom = nameof(Period))]public record ConstraintsModelBoundClosingBooksClosed(string Period, decimal ClosingBalance);The property must have a non-empty value on every append. The kernel refuses an append where it is missing, null or empty. Avoid a nullable property; the analyzer CHR0012 warns on nullable event properties.
Name and reopening events
Section titled “Name and reopening events”Name sets the owner of the closure and defaults to the event type name. ReopenedBy lists the event types that remove this constraint’s closure for the exact scope they resolve to:
using Cratis.Chronicle.Events;using Cratis.Chronicle.Events.Constraints;
[EventType][ClosesStream(Name = "close-month", EventStreamIdFrom = nameof(Month), ReopenedBy = [typeof(ConstraintsModelBoundClosingMonthReopened)])]public record ConstraintsModelBoundClosingMonthClosed(string Month);
[EventType]public record ConstraintsModelBoundClosingMonthReopened(string Month, string Reason);A reopening event type must carry the EventStreamIdFrom property too, or the client throws PropertyDoesNotExistOnEventType when it discovers the constraint.
Attribute reference
Section titled “Attribute reference”| Property | Meaning |
|---|---|
Dimensions | Dimensions of the closed scope. Defaults to event source id, stream type and stream id |
EventStreamIdFrom | Payload property that provides the stream id |
ReopenedBy | Event types that reopen this constraint’s scope |
Name | Owner name. Defaults to the event type name. Cannot be empty |
EventSequences | Event sequences the declaration applies to. Empty means all |
Declarations that share a Name merge into one constraint and must use the same dimensions and stream id property, otherwise the client throws ConflictingClosesStreamDeclarations.
Registering or changing the declaration does not close anything that is already in the log.