Subject
The subject of an event is the target the event is about — the identity used to key
compliance concerns such as PII encryption. In most systems the subject and the
EventSourceId are the same, but they don’t have to be.
Consider a ShippingAddressChanged event. The EventSourceId is the order identifier
(that is what binds all events for that order together), but the subject is the customer
the address is about. PII encryption keys are held per-subject, not per-event-source, so that
every event that touches the same person can be decrypted with the same key regardless of
which aggregate produced it.
Default — the EventSourceId
Section titled “Default — the EventSourceId”When you don’t specify a subject, Chronicle uses the EventSourceId as the subject. This is
the right default for most slices where the aggregate and the subject coincide:
using Cratis.Chronicle;using Cratis.Chronicle.Events;
[EventType]public record SubjectAuthorRegistered(string Name);
public class SubjectAuthorService(IEventStore eventStore){ public Task Register(EventSourceId authorId, string name) => // Subject defaults to authorId; encryption keys for any PII on SubjectAuthorRegistered // are keyed by authorId. eventStore.EventLog.Append(authorId, new SubjectAuthorRegistered(name));}Kotlin does not support this workflow yet.Java does not support this workflow yet.defmodule MyApp.Events.SubjectAuthorRegistered do use Chronicle.Events.EventType, id: "subject-author-registered"
defstruct [:name]end
defmodule MyApp.SubjectAuthorService do alias MyApp.Events.SubjectAuthorRegistered
def register(author_id, name) do # Subject defaults to author_id; encryption keys for any PII on SubjectAuthorRegistered # are keyed by author_id. Chronicle.append(author_id, %SubjectAuthorRegistered{name: name}) endendTypeScript does not support this workflow yet.Specifying an explicit subject
Section titled “Specifying an explicit subject”When the event source is different from the subject, pass Subject explicitly. Subject has
an implicit conversion from EventSourceId, so mixing the two is painless:
using Cratis.Chronicle;using Cratis.Chronicle.Events;
[EventType]public record SubjectShippingAddressChanged(string Street);
public class SubjectShippingService(IEventStore eventStore){ public Task ChangeAddress(EventSourceId orderId, Subject customerId, string street) => eventStore.EventLog.Append( eventSourceId: orderId, @event: new SubjectShippingAddressChanged(street), subject: customerId);}Kotlin does not support this workflow yet.Java does not support this workflow yet.defmodule MyApp.Events.SubjectShippingAddressChanged do use Chronicle.Events.EventType, id: "subject-shipping-address-changed"
defstruct [:street]end
defmodule MyApp.SubjectShippingService do alias MyApp.Events.SubjectShippingAddressChanged
def change_address(order_id, customer_id, street) do Chronicle.append(order_id, %SubjectShippingAddressChanged{street: street}, subject: customer_id ) endendTypeScript does not support this workflow yet.Subject also converts implicitly from string and Guid. Kotlin, Java, and TypeScript don’t currently expose a subject append option at all — only C# and Elixir do.
Implicit subject with [Subject]
Section titled “Implicit subject with [Subject]”Rather than threading subject through every call site, decorate the relevant property on the
event type with [Subject]. Chronicle will read the property value automatically when no
explicit subject is provided:
using Cratis.Chronicle;using Cratis.Chronicle.Compliance.GDPR;using Cratis.Chronicle.Events;
public record SubjectOrderId(Guid Value);public record SubjectCustomerId(Guid Value);
[EventType]public record SubjectShippingAddressChangedWithImplicitSubject( SubjectOrderId Order, [Subject] SubjectCustomerId Customer, [PII] string Street, [PII] string City);
public class SubjectImplicitSubjectService(IEventStore eventStore){ // Subject is derived from the Customer property — no explicit subject needed. public Task ChangeAddress(SubjectOrderId orderId, SubjectCustomerId customerId, string street, string city) => eventStore.EventLog.Append( orderId.Value, new SubjectShippingAddressChangedWithImplicitSubject(orderId, customerId, street, city));}An explicit subject argument always takes precedence over a [Subject] property. This declarative form is C#-only — Elixir requires passing subject: explicitly on every append (shown above); Kotlin, Java, and TypeScript don’t support a subject at all yet.
Compliance
Section titled “Compliance”When an event type declares [PII] properties, Chronicle encrypts those properties at append
time using a per-subject encryption key. The key is created on first use and stored in the
encryption key store. Because the key lives under the subject, every event that shares a
subject is encrypted with the same key — and once that key is deleted (for example, to honor a
right-to-erasure request), every PII property across every event for that subject becomes
unrecoverable in one operation.
Read more about PII compliance.