Forks and Commands

conta-fork-yeah is designed to compose with command-based architectures (conta-command). This page explains the relationship and the audit/replay properties that fall out of it.

Forks happen via commands

In a system built on conta-command, every state change is a Command. A fork is no exception: it’s just a particular kind of command — one that takes a ForkSource as input and produces (potentially many) child commands as a side effect.

The library expresses this in two places:

ForkEnabled

The marker for commands that initiate a fork. Its single method getForkFrom() exposes the source the fork is starting from.

ForkContext.spawnedBy()

The triggering command, carried along through the operation. ForkContext.toSpawnedByRef() renders it as cmd:<uuid>, which downstream code can stamp onto audit fields of every entity produced by the fork.

The pay-off is auditability: given any forked entity, you can answer "which command made this?" by reading its lineage and the stamped command reference.

Has the source changed since we forked?

A fork is a snapshot — it captures the source as it was at one moment. The source can keep evolving afterwards. Two questions naturally follow:

  1. Has any change been applied to the source since we forked from it?

  2. If yes, should the same change be applied here as well?

The library doesn’t answer either question on its own — but it gives you the primitives to answer them in your domain code.

What’s recorded

  • ForkSource.forkedAt — the timestamp of the fork.

  • ForkSource.identifier — the SourceIdentifier of the source entity.

  • ForkContext.toSpawnedByRef() — the command that drove the fork, as cmd:<uuid>. Stamped onto each forked entity if you wire it through.

What you build on top

If your application persists every command (see command persistence), you can answer questions like:

  • "Commands that touched the source after forkedAt`" — query the store, scoped by `ForkSource.identifier.uuid1().

  • "Was this forked entity itself modified later?" — same query against the forked entity’s identifier.

  • "Diff between source-now and source-at-fork" — by re-projecting the source from its commands up to and after forkedAt.

For example, listing later edits to the source ninja might look like:

select cmd_uuid, executed_at, payload
from   command_store
where  target_uuid  = :sourceNinjaUuid    -- ForkSource.identifier.uuid1()
and    executed_at  > :forkedAt           -- ForkSource.forkedAt
order  by executed_at;

The library deliberately does not auto-apply later source changes onto the fork, nor detect conflicts between concurrent edits — those are domain decisions. Sometimes a forward-port is desirable (mid-cycle correction propagates onward), sometimes the fork is meant to be a frozen snapshot for filing or compliance. The library’s job is to record enough lineage that the consumer can build whichever propagation policy fits.