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:

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