Registering Event Schemas
Every event type has a JSON schema, derived from the struct or its own (see Describing Events with Schemas), but the database only checks events against a schema once it is registered. The Evolve and Ignore functions collect the schemas of all events of a state. To get them as a slice of EventSchema, each with the fields EventType and Schema, call the Schemas function on the state.
Then hand them over to the RegisterSchemas function, together with a context and the store:
err := architecturekit.RegisterSchemas(context.TODO(), store, bookState.Schemas())
if err != nil {
// ...
}
RegisterSchemas accepts the schemas of several states at once. An event type that several of them bring along with the same schema is registered once. If they bring it along with two different schemas, RegisterSchemas returns an error of the category ErrPermanent, as in permanent failure: event type "io.eventsourcingdb.library.book-acquired" has two different schemas, and so it does for a schema that is nil, which it reports as event type … has no schema, in either case before it registers anything. Call it on every start, before the application serves requests: for an event type the database knows already, it checks that the registered schema is exactly the one from the code. If the context ends first, it returns an error that wraps the one of the context. A registration that has begun is finished, since the context no longer stops it, but none begins once the context has ended.
An event that no state has a rule for, such as InventoryTaken, which only Write writes (see Writing to Several Subjects), is part of no Schemas. To register its schema as well, call the SchemaOf function with the type of the event. It returns an EventSchema by the rule that Evolve applies, the event's own schema or the derived one. Since RegisterSchemas takes slices of them, hand it over in a slice of its own:
err := architecturekit.RegisterSchemas(context.TODO(), store,
bookState.Schemas(),
[]architecturekit.EventSchema{architecturekit.SchemaOf[InventoryTaken]()},
)
if err != nil {
// ...
}
Note that SchemaOf panics where Evolve would, for example for an event that has its Schema function only from an embedded field, or for a pointer or an interface as the event type.
Changing a Schema#
Changed in 2.0A registered schema can not change. If it differs from the one from the code, RegisterSchemas returns an error of the category ErrPermanent, so that the application does not start with a schema the database does not enforce. It does the same if the database refuses a schema, for example because stored events of the type do not match it. The error names the event type and, where the database gives one, the description.
To change the shape of an event, introduce a new event type instead, and translate the stored events of the old type with an upcaster (see Versioning Events).
Note that this also holds if you remove the Schema function of an event whose schema is registered already: the derived schema has to be exactly the registered one, or RegisterSchemas fails. Keep the Schema function of such an event, unless you have compared both.
Writing Events That Do Not Match#
Changed in 2.0Once a schema is registered, the database rejects every event of the type that does not match it. Execute then returns an error of the category ErrPermanent, because writing the same event again gives the same result, and the error contains the description the database gives, for example from a database that only offers the v1 API, such as EventSourcingDB 1.2:
schema conflict: event candidate does not match schema: additionalProperties 'tags' not allowed