Maestro v0.3.0 Maestro.Aggregate.Root behaviour View Source
Core behaviour and functionality provided by Maestro for processing commands and managing aggregate state.
Traditional domain entities are referred to as aggregates in the literature. At the outermost edge of a bounded context, you find an aggregate root. The goal of this library is to greatly simplify the process of implementing an event sourced application by owning the flow of non-domain data (i.e. commands, events, and snapshots) to allow you to focus on the business logic of evaluating your commands and applying the subsequent events to your domain objects.
The most crucial piece to this is the aggregate root.
Maestro.Aggregate.CommandHandler
defines a behaviour
with the goal of
isolating a single command handler's eval
. Similarly, there is the
Maestro.Aggregate.EventHandler
behaviour which defines how to apply
that
event to the aggregate. With these key components modeled explicitly, the
Maestro.Aggregate.Root
focuses on the dataflow and ensuring that queries to
aggregate state flow properly.
The aggregate root dispatches to the particular command handlers and event
handlers by means of an opinionated dynamic dispatch. To ensure that these
things are handled in a consistent manner, the aggregate root is modeled as a
GenServer
and provides the requisite lifecycle hooks.
use Maestro.Aggregate.Root
takes the following options:
:command_prefix
- module prefix for finding commands:event_prefix
- module prefix for finding events:projections
- zero or more modules that implement theProjectionHandler
behaviour for the events that are generated by this aggregate root. These projections are invoked within the transaction that commits the events.
Link to this section Summary
Functions
struct to event type of the form "The.ModuleName -> the.module_name" dropping the provided prefix for conciseness
Look up an aggregate by its ID. The module is provided to start the right type of aggregate should it not already be started.
Callbacks
If you extend the aggregate to provide other functionality, call
is
available to assist in pushing that functionality into the aggregate's
context.
Evaluate the command within the aggregate's context.
Forces the aggregate to retrieve any events. Since Maestro operates in a node-local manner, it's entirely possible some other node has processed commands/events.
A (potentially) stale read of the aggregate's state. If you want to ensure the
state is as up-to-date as possible, see fetch/1
.
When an aggregate root is created, this callback is invoked to generate the state
Create a new aggregate along with the provided initial_state
function. This
function should only fail if there was a problem generating an HLC timestamp.
Snapshots are stored in a single-row-per-aggregate manner and are used to make it easier/faster to hydrate the aggregate root. This function should return the map which will be JSON encoded when moving to a durable store.
Recover a past version of the aggregate's state by specifying a maximum sequence number. The aggregate's snapshot and any/all events will be used to get the state back to that point.
Using the aggregate root's prepare_snapshot
function, generate and store a
snapshot. Useful if there are a lot of events, big events, or just a healthy
amount of aggregate state to compose.
Moving from the snapshotted representation to the aggregate root's structure can be a complicated process that requires custom hooks. Otherwise, a default implementation is provided that simply lifts the map out of the snapshot and uses it as the state of the aggregate.
Link to this section Types
command()
View Source
command() :: Maestro.Types.Command.t()
command() :: Maestro.Types.Command.t()
id()
View Source
id() :: HLClock.Timestamp.t()
id() :: HLClock.Timestamp.t()
sequence()
View Source
sequence() :: non_neg_integer()
sequence() :: non_neg_integer()
stack()
View Source
stack() :: Exception.stacktrace()
stack() :: Exception.stacktrace()
Link to this section Functions
child_spec(opts) View Source
event_type(pre, str) View Source
struct to event type of the form "The.ModuleName -> the.module_name" dropping the provided prefix for conciseness
start_link(opts) View Source
whereis(agg_id, mod) View Source
Look up an aggregate by its ID. The module is provided to start the right type of aggregate should it not already be started.
Link to this section Callbacks
call(id, msg) View Source
If you extend the aggregate to provide other functionality, call
is
available to assist in pushing that functionality into the aggregate's
context.
evaluate(command) View Source
Evaluate the command within the aggregate's context.
fetch(id) View Source
Forces the aggregate to retrieve any events. Since Maestro operates in a node-local manner, it's entirely possible some other node has processed commands/events.
get(id) View Source
A (potentially) stale read of the aggregate's state. If you want to ensure the
state is as up-to-date as possible, see fetch/1
.
initial_state()
View Source
(optional)
initial_state() :: any()
initial_state() :: any()
When an aggregate root is created, this callback is invoked to generate the state
Create a new aggregate along with the provided initial_state
function. This
function should only fail if there was a problem generating an HLC timestamp.
prepare_snapshot(root) View Source (optional)
Snapshots are stored in a single-row-per-aggregate manner and are used to make it easier/faster to hydrate the aggregate root. This function should return the map which will be JSON encoded when moving to a durable store.
replay(id, sequence) View Source
Recover a past version of the aggregate's state by specifying a maximum sequence number. The aggregate's snapshot and any/all events will be used to get the state back to that point.
snapshot(id) View Source
Using the aggregate root's prepare_snapshot
function, generate and store a
snapshot. Useful if there are a lot of events, big events, or just a healthy
amount of aggregate state to compose.
use_snapshot(root, snapshot)
View Source
(optional)
use_snapshot(root :: t(), snapshot :: Maestro.Types.Snapshot.t()) :: any()
use_snapshot(root :: t(), snapshot :: Maestro.Types.Snapshot.t()) :: any()
Moving from the snapshotted representation to the aggregate root's structure can be a complicated process that requires custom hooks. Otherwise, a default implementation is provided that simply lifts the map out of the snapshot and uses it as the state of the aggregate.