Writing a Plugin
Plugins are storage backends for events. The built-in Memory plugin is fine for testing, but production requires persistent storage. This guide shows you how to write your own.
The Plugin Interface
Your plugin must implement the Sourcing::Plugin role:
use Sourcing::Plugin;
unit class MyPlugin does Sourcing::Plugin;
method emit($event, :$current-version) { ... }
method get-events(%ids, %map) { ... }
method get-events-after($id, %ids, %map) { ... }
method supply { ... }
method store-cached-data(Mu:U $proj, %) { ... }
method get-cached-data(Mu:U $proj, %) is rw { ... }
Version-Checked Emit
For optimistic locking, implement a multi method with :$type, :%ids!, and :$current-version!:
multi method emit($event, :$type, :%ids!, :$current-version!) {
# Only aggregations can emit
unless $type ~~ Sourcing::Aggregation {
die "Only aggregations can emit events";
}
# CAS version check
my $stored-version = get-stored-version($type, %ids);
if $stored-version != $current-version {
Sourcing::X::OptimisticLocked.new(
:type($type), :ids(%ids),
:expected-version($current-version),
:actual-version($stored-version)
).throw;
}
# Store event and update version
store-event($event);
update-version($type, %ids, $current-version + 1);
# Broadcast on supply
$!supplier.emit: $event;
}
Event Storage
Store events in an append-only log. Each event needs:
- The event object (serialized for persistent storage)
- The aggregate type and IDs
- A sequential version number
Event Retrieval
get-events-after returns events after a given version:
method get-events-after(Int $id, %ids, %map) {
my @all-events = get-all-events-for(%ids);
return @all-events.grep({ .^name (elem) %map.keys })
.skip($id + 1);
}
Supply
Return a Supply that emits events as they're stored:
has Supplier $.supplier .= new;
has Supply() $.supply = $!supplier.Supply;
Activation
Provide a use class method:
method use(|c) {
PROCESS::<$SourcingConfig> = self.new: |c;
}
Usage
use MyPlugin;
MyPlugin.use; # Activates your plugin
# Now all sourcing operations use your plugin
my $account = sourcing BankAccount, :account-id(1);
Next Steps
- Study the Memory Plugin source for a complete implementation
- Read the Plugin API Reference for method signatures