Chronicle.Seeding (cratis_chronicle v3.16.1)

Copy Markdown View Source

Accumulates and registers seed events with Chronicle.

This module provides a builder API for accumulating events during seeder discovery, then organizing and sending them to the Chronicle server.

Usage

Typically used within a Chronicle.Seeding.Seeder.seed/1 callback:

def seed(builder) do
  builder
  |> Chronicle.Seeding.for(MyEvent, "aggregate-1", [%MyEvent{...}])
  |> Chronicle.Seeding.for_event_source("aggregate-2", [%Event1{...}, %Event2{...}])
end

Lifecycle

  1. Discovery — discover/2 finds all seeder modules
  2. Accumulation — Each seeder's seed/1 is called, populating the builder
  3. Registration — register/1 organizes entries and sends them to Chronicle

Events are organized by:

  • Global vs. namespaced
  • Event type and event source (dual organization for server efficiency)

Summary

Functions

Builds the kernel's SeedEventsRequest from the accumulated entries: global entries grouped by event type and by event source, namespaced entries grouped per namespace.

Discovers seeder modules and invokes them to populate the builder.

Seeds events of a specific type for an event source.

Seeds multiple event types for a single event source.

Scopes subsequent events to a specific namespace.

Registers all accumulated seed events with Chronicle.

Types

append_many_fun()

@type append_many_fun() :: (event_source_id(), [struct()], keyword() ->
                        :ok | {:error, term()})

event_source_id()

@type event_source_id() :: String.t()

has_events_for_fun()

@type has_events_for_fun() :: (event_source_id(), keyword() ->
                           {:ok, boolean()} | {:error, term()})

namespace()

@type namespace() :: String.t()

seeding_entry()

@type seeding_entry() :: %{
  event_source_id: event_source_id(),
  event_type_id: String.t(),
  event: struct(),
  tags: [String.t()],
  is_global: boolean(),
  target_namespace: namespace() | nil
}

t()

@type t() :: %Chronicle.Seeding{
  append_many: append_many_fun() | nil,
  client: atom() | nil,
  connection: atom(),
  entries: [seeding_entry()],
  event_store: String.t(),
  event_types: module(),
  has_events_for: has_events_for_fun() | nil,
  namespace: String.t(),
  seed_events: (map() -> :ok | {:error, term()}) | nil
}

Functions

build_seed_request(builder)

@spec build_seed_request(t()) :: map()

Builds the kernel's SeedEventsRequest from the accumulated entries: global entries grouped by event type and by event source, namespaced entries grouped per namespace.

discover(builder, seeder_modules)

@spec discover(t(), [module()]) :: t()

Discovers seeder modules and invokes them to populate the builder.

Iterates through all provided seeder modules, instantiates each, calls seed/1, and accumulates their events.

Returns the populated builder struct.

for(builder, event_type, event_source_id, events)

@spec for(t(), module(), event_source_id(), [struct()]) :: t()

Seeds events of a specific type for an event source.

Parameters

  • builder — the seeding builder
  • event_type — the event type module (must use Chronicle.Events.EventType)
  • event_source_id — the event source (aggregate) ID
  • events — list of event structs

Example

builder
|> Chronicle.Seeding.for(MyApp.Events.AccountOpened, "account-1", [
  %MyApp.Events.AccountOpened{account_id: "account-1", owner_name: "Alice"}
])

for_event_source(builder, event_source_id, events)

@spec for_event_source(t(), event_source_id(), [struct()]) :: t()

Seeds multiple event types for a single event source.

Parameters

  • builder — the seeding builder
  • event_source_id — the event source (aggregate) ID
  • events — list of event structs (can be different types)

Example

builder
|> Chronicle.Seeding.for_event_source("account-1", [
  %MyApp.Events.AccountOpened{account_id: "account-1", owner_name: "Alice"},
  %MyApp.Events.FundsDeposited{account_id: "account-1", amount: 500}
])

for_namespace(builder, namespace, fun)

@spec for_namespace(t(), namespace(), (t() -> t())) :: t()

Scopes subsequent events to a specific namespace.

Returns a scoped builder that targets the given namespace.

Parameters

  • builder — the seeding builder
  • namespace — the target namespace
  • fun — a function receiving a scoped builder

Example

builder
|> Chronicle.Seeding.for_namespace("production", fn scoped ->
  scoped
  |> Chronicle.Seeding.for(MyEvent, "aggregate-1", [%MyEvent{...}])
end)

register(builder)

@spec register(t()) :: :ok | {:error, term()}

Registers all accumulated seed events with Chronicle.

Organizes entries by global/namespaced, then by event type and event source, and hands them to the kernel's seeding service, which owns the rest: it tracks what it has already applied (by event source, event type, content and tags), so a new entry added to an event source that was seeded before is applied, an entry that was applied is not applied again, and global entries reach every namespace the kernel knows - exactly as with the .NET client. Re-running on every start is therefore safe.

A seeder whose seed/1 raises is logged as an error and contributes none of its entries (a seeder is applied whole or not at all); the other seeders still run. Tags declared on the event type (tags:) are sent with each entry.

This is typically called automatically by Chronicle.Client during startup.