Skip to content
@tde.io/plexis  ·  npm install @tde.io/plexis  ·  npm  ·  GitHub

Snapshots and Restore

domain.snapshot() captures the current phase, context, and history length as a plain object. domain.restore(snapshot) rewinds the domain to that captured point. Together they let you checkpoint a domain and pick it back up later.

import { defineDomain, when, on, target, terminal } from '@tde.io/plexis';
const order = defineDomain('order', () => {
when('pending', () => {
on('submit', target('processing'));
});
when('processing', () => {
on('ship', target('shipped'));
});
when('shipped', terminal());
return { context: { orderId: 'ord_1', items: 3 }, initial: 'pending' };
});
await order.follow('submit');
const snap = order.snapshot();
// snap === { phase: 'processing', context: { orderId: 'ord_1', items: 3 }, historyLength: 1 }

DomainSnapshot is a plain object — { phase, context, historyLength }. It is safe to JSON.stringify as long as the context contains only JSON-serializable values.

const order2 = defineDomain('order', () => {
when('pending', () => {
on('submit', target('processing'));
});
when('processing', () => {
on('ship', target('shipped'));
});
when('shipped', terminal());
return { context: { orderId: '', items: 0 }, initial: 'pending' };
});
order2.restore(snap);
console.log(order2.phase); // 'processing'
console.log(order2.context.orderId); // 'ord_1'

restore() sets the domain’s current phase and context to the snapshot values and resets the history length counter. The domain can then follow events from the restored point.

Snapshots capture only the data layer: phase name, context object, and history length. They do not capture:

  • Handler functionsenter, exit, action, guard, condition are part of the domain definition, not the snapshot. They are always sourced from the current definition at runtime.
  • Subscriberssubscribe() listeners are not saved or restored.
  • Full history entries — only the count is stored, not the individual DomainHistoryEntry records.

If you need the full history across boundaries, store it separately alongside the snapshot.

Restoring a snapshot into a different domain definition than the one that produced it can cause problems:

  • If the restored phase no longer exists in the new definition, restore() will set the domain to that phase — subsequent follow() calls may encounter UNKNOWN_EVENT errors or undefined behavior.
  • If context shape changed, TypeScript will flag mismatches at compile time but the runtime will not reject them — your handlers may see unexpected fields or missing keys.

To avoid this, version your snapshots alongside your domain definitions. If the definition changes in a breaking way, write a migration function that transforms old snapshots into the new shape before restoring.

type SnapV1 = { phase: string; context: { orderId: string }; historyLength: number };
type SnapV2 = {
phase: string;
context: { orderId: string; version: number };
historyLength: number;
};
function migrateSnapshot(v1: SnapV1): SnapV2 {
return {
...v1,
context: { ...v1.context, version: 2 },
};
}

This page covers the mechanics of the snapshot API. For the full story — serializing to a database, rehydrating in a serverless function, and handling request-scoped domains — see Persist and Rehydrate in Advanced.

For the DomainSnapshot type and snapshot/restore signatures, see the Domain Instance reference.