replay 0.0.1 copy "replay: ^0.0.1" to clipboard
replay: ^0.0.1 copied to clipboard

An Event Sourcing framework for Dart.

An Event Sourcing framework for Dart.

Why Event Sourcing? #

Event sourcing is the pinnacle of event driven architectures!

It records all changes to an application's state as a sequence of immutable events, rather than storing only the current state.

This provides you with a complete audit log by design and enables temporal queries ("show me last Tuesday's state") by allowing you to deterministically replay past events.

Event Sourcing powers undo/redo in apps, audit trails in banking, and Git’s commit history.

Installation #

dart pub add replay

Features #

✔️ Idempotent event processing
✔️ Snapshotting support
✔️ In-memory or custom event storage support
🔜 Automatic partitioning (WIP)
✔️ Sound null safety
✔️ 100 % test coverage
✔️ No dependencies

Usage #

See example.

Start by defining immutable classes for:

  • the state of an aggregate
  • events with a common interface
  • commands with a common interface

Then create classes that implement CommandDecider and EventReducer.

Finally put it all together in one Aggregate and call process on every command:

import 'package:replay/replay.dart';

final aggregate = Aggregate<BankCommand, BankEvent, BankState>(
  initialState: BankState(),
  commandDecider: ComposableCommandDecider({
    OpenAccountCommand: OpenAccountCommandDecider(),
    CloseAccountCommand: CloseAccountCommandDecider(),
    TransferMoneyCommand: TransferMoneyCommandDecider(),
  }),
  eventReducer: ComposableEventReducer({
    BalanceSetEvent: BalanceSetEventReducer(),
    BalanceUnsetEvent: BalanceUnsetEventReducer(),
  }),
  eventStorage: InMemoryEventStorage(),
);

aggregate.process(OpenAccountCommand(accountName: 'Foo', initialBalance: 100));

Concepts #

Commands vs. Events #

Both commands and events describe actions from a business perspective, but that's where their similarities end.

Commands:

  • Request an action in imperative mood ("send message")
  • Originate externally
  • Immutable once issued
  • Can be invalid/rejected (require validation)
  • May produce 0-N events when processed

Events:

  • Record completed actions in past tense ("message sent")
  • Internally-generated facts
  • Always immutable (cannot be modified) & append-only
  • Can never be invalid
  • Used to construct the application state (single source of truth)
    • Use "snapshots" of the state for optimized queries (Command Query Responsibility Segregation - CQRS)
  • Idempotent: Processing the same event repeatedly always results in the same state
  • Atomic: Either processing succeeds completely or nothing changes

Aggregate #

An aggregate acts as a consistency boundary — grouping a command's events into atomic (all-or-nothing) operations. It enforces invariants by validating commands against the state, which is derived from past events. Valid commands produce new events, updating the aggregate. These events are stored immutably to enable auditability.

0
likes
140
points
288
downloads

Publisher

unverified uploader

Weekly Downloads

An Event Sourcing framework for Dart.

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

More

Packages that depend on replay