Patches

Event sourcing promises a complete history, but its hardest design problem is often the events. Teams spend much time naming event types, deciding which aggregate uses each event, and deciding which intent each event represents. These decisions can require a complete domain model before the system records useful truth. Recording concrete effects comes first.

Methods such as EventStorming help teams discover events, commands, aggregates, and domain boundaries. This work can improve shared understanding across a project. It also shows that event selection is a major design activity rather than a small implementation detail. Recording an exact change does not wait for teams to complete that model.

Effects before event types

Domain language helps people communicate, but Stardust does not require a predefined set of event types before accepting change. A project does not need predefined aggregates, tables, or a complete schema. JSON Merge Patch supplies one well-defined contract for intended state. The same semantics work across projects without encoding each project's event vocabulary.

JSON Patch describes a sequence of editing operations against specific document paths. Those operations depend on the current document structure and order. JSON Merge Patch describes the intended values instead. Patch processing can compare that intent with current state before it records any effect.

Requested and committed patches

A submitted patch describes the state that the caller wants. Write it to the patch.json or patch.dust command file. Its keys are decimal entity IDs or temporary names for new entities. Patch processing compares each field in that request with the current facts. A field whose value already matches is skipped, and a null for a field that does not exist is skipped. A request with no logical effect commits no transaction.

dust99B
163 {active  false
     retired null
     role    engineer}
164 {active true
     note   reviewed}

An Internet of Things (IoT) device can send its complete current state as one large patch. This keeps the sender simple because it does not need to remember its previous report. Only values that differ from current facts enter history. A large transport request can therefore produce a small historical change.

Device intended state

Requested merge patch

Current Stardust facts

Compare each field

Changed facts only

Schema validation of changed documents

Atomic commit

Transaction merge patch in the log

Fixed semantics

An omitted field stays unchanged. Null retracts a field, and an entity set to null deletes that entity. Every other value replaces its field. An array or object is one value, so the patch gives the complete intended array or object.

A patch can create an entity under a temporary name that starts with #_. The patch can refer to it as {#link #_name}. Stardust rejects an id that it did not allocate. The stardust.entity.create command or a mutation can also create an entity. Every change in one patch commits together or not at all. These semantics do not depend on project-specific operation types.

History contains real differences

The committed transaction records actual effects rather than the full submitted document. Its log entry is a merge patch of changed data entities and definitions. The entry gives each changed field its new value and each retracted field a null. Time travel follows these differences instead of repeated snapshots of unchanged state. Smaller histories improve reconstruction because callers do not need to calculate their own changes.

Each transaction file records changed fields under entity IDs. It includes the transaction entity's stardust/transaction and stardust/committedAt fields. It has no entities or transaction object around the IDs. Reification makes the transaction and its effects queryable.