Skip to content

Domain Events

TypeMD uses domain events to decouple producers (entities, services) from consumers (index writer, TUI, MCP server). Entity methods return DomainEvent values; the use case layer collects and dispatches them after successful operations.

vault.Events.Subscribe(func(e core.DomainEvent) {
switch e := e.(type) {
case core.ObjectCreated:
// handle new object
case core.PropertyChanged:
// handle property update
case core.TypeSaved:
// handle type schema change
}
})

Events are dispatched synchronously in the order they were collected. Handlers should be lightweight — heavy work should be queued for async processing.

Emitted by ObjectService during user-initiated operations (create, save, link, unlink).

EventPurposePayload
ObjectCreatedNew object createdObject
ObjectSavedExisting object savedObject
PropertyChangedSingle property value changedObjectID, Key, Old, New
ObjectLinkedRelation created between objectsFromID, ToID, RelName
ObjectUnlinkedRelation removed between objectsFromID, ToID, RelName
TagAutoCreatedTag object auto-created during syncTag, ReferencedBy

Emitted by Vault.SaveType() and Vault.DeleteType() during type schema operations.

EventPurposePayload
TypeSavedType schema created or updatedSchema
TypeDeletedType schema deletedName

Events are not emitted when the operation fails (e.g., validation error on SaveType, attempting to delete a built-in type).

Emitted by the Reconciler during sync; consumed by the Projector to update the SQLite index.

EventPurposePayload
ObjectUpsertedObject needs to be written to indexID, Type, Filename, PropsJSON, Body
ObjectDeletedObject removed from disk (stale cleanup)ID
RelationsClearedClear relations before rebuildingObjectID (per-object), NonTagOnly (full sync), or TagsOnly (tag sync)
RelationIndexedA single relation needs to be indexedName, FromID, ToID
WikiLinksSyncedWiki-links resolved for an objectObjectID, Links []WikiLinkEntry
  • Full reconciliation (Reconciler.Reconcile()): Emits a RelationsCleared{NonTagOnly: true} event followed by RelationIndexed events for all relations. Tag relations are managed separately via RelationsCleared{TagsOnly: true}.
  • Incremental reconciliation (Reconciler.ReconcileFiles()): Emits RelationsCleared{ObjectID: id} events for changed objects, then RelationIndexed events for their rebuilt relations. Unchanged objects are not affected.
  • Fire-and-forget: Event dispatch failure does not rollback the operation that produced the event.
  • Entity produces → use case dispatches: Entity methods return DomainEvent values; services collect and dispatch after successful operations.
  • Files are the source of truth: Events drive the SQLite index, but the index can always be rebuilt from files.