Memento Pattern (TypeScript)
Intent
Capture and restore an object's internal state without exposing its internals, enabling undo/redo and rollback with clear ownership of snapshots.
When to use
- •You need undo/redo for complex state changes.
- •Transaction-like rollback is required after partial failures.
- •Encapsulation matters; external code must not access internal fields.
- •Multiple independent instances need their own history.
- •You want metadata-only history in a caretaker (timestamp/label).
- •Snapshot timing should be controlled outside the originator.
- •You need deterministic restoration for tests and audits.
When NOT to use
- •Immutable state already allows keeping prior values cheaply.
- •A differential log or event sourcing is more appropriate.
- •Snapshots are too large or too frequent for memory limits.
- •External resources (files/handles/network) cannot be restored safely.
- •You only need a simplified API (Facade is enough).
- •State is trivial and can be recomputed quickly.
- •Undo/redo is not a real requirement (avoid extra complexity).
Mental model
Originator owns state and knows how to snapshot/restore; Caretaker owns timing/history; Memento is an immutable snapshot object.
Recommended TS shapes
- •Classic: originator creates opaque
Mementoobjects; caretaker stores them and never reads payload. - •Strict: memento exposes only metadata +
restore(originator); payload stays private. - •Command+Memento: commands capture pre-state mementos and call
undo()by restoring.
Example 1: Text editor snapshot (classic caretaker stack)
Use an editor originator with a history caretaker storing opaque mementos (text, cursor, selection) and metadata only.
Example 2: Command + Memento undo/redo
Commands capture a pre-mutation memento, execute mutations, and undo by restoring; caretaker manages undo/redo stacks.
Example 3: Transactional rollback for a service aggregate
Use mementos to rollback a Cart/OrderDraft when validation fails mid-update.
Testing strategy (pragmatic)
- •Round-trip tests: snapshot -> mutate -> restore -> equals original.
- •Ensure caretakers cannot access payload fields.
- •Test bounded history behavior and snapshot frequency policies.
Common pitfalls
- •Mutable references inside mementos causing shared-state leaks.
- •Snapshotting too often (memory blowup) or too rarely (coarse undo).
- •Unbounded history growth with no caps/eviction.
- •Caretaker peeking into snapshot data (breaks encapsulation).
- •Restoring stale external references (files, sockets).
- •Non-deterministic mutations between snapshot and restore.
- •Forgetting to snapshot before mutation.
- •Treating mementos as serialization format for external APIs.
Checklist for refactors
- •Identify the originator and its state boundary.
- •Define a snapshot contract (what is captured, what is not).
- •Keep mementos immutable and opaque to caretakers.
- •Wire snapshot-before-mutation points.
- •Add a caretaker history with size limits or checkpoints.
- •Ensure restore is deterministic and tested.
- •Decide whether Command+Memento is needed for undo/redo flows.
- •Document snapshot frequency and memory impact.
Output expectations
When invoked, produce: a memento API (originator + memento + caretaker), snapshot timing, bounded history strategy, and minimal TS examples tailored to the user input.