Seeding
This page shows how to seed events using the Chronicle Kotlin client. Seeding is sent to the Chronicle Server when the event store connects, and the server applies it once per namespace. See Event Seeding for the concept this page assumes.
Define events
Section titled “Define events”import io.cratis.chronicle.events.EventType
@EventTypedata class AccountOpened( val accountId: String, val ownerName: String, val initialBalance: Double)
@EventTypedata class FundsDeposited(val accountId: String, val amount: Double)Implement a seeder
Section titled “Implement a seeder”Annotate a class with @Seeder, implement ICanSeedEvents, and use
IEventSeedingBuilder.forEventSource to accumulate events:
import io.cratis.chronicle.seeding.ICanSeedEventsimport io.cratis.chronicle.seeding.IEventSeedingBuilderimport io.cratis.chronicle.seeding.Seeder
@Seederclass AccountSeeder : ICanSeedEvents { override fun seed(builder: IEventSeedingBuilder) { builder.forEventSource( "account-1", listOf(AccountOpened("account-1", "Alice", 1000.0)) ) }}Seed mixed event types for one event source
Section titled “Seed mixed event types for one event source”forEventSource takes a list of any event types, so mixing types for the
same event source is the same call:
@Seederclass MixedAccountSeeder : ICanSeedEvents { override fun seed(builder: IEventSeedingBuilder) { builder.forEventSource( "account-1", listOf( AccountOpened("account-1", "Alice", 1000.0), FundsDeposited("account-1", 500.0) ) ) }}Chain multiple calls to seed several event sources:
@Seederclass MultiAccountSeeder : ICanSeedEvents { override fun seed(builder: IEventSeedingBuilder) { builder .forEventSource( "account-1", listOf(AccountOpened("account-1", "Alice", 1000.0)) ) .forEventSource( "account-2", listOf(AccountOpened("account-2", "Bob", 500.0)) ) }}Seed a specific event type
Section titled “Seed a specific event type”forEventType is an alternative to forEventSource that also checks the
event class is annotated with @EventType before accepting it — useful
when a seeder should only ever seed one specific event type and you want
that caught early rather than at the server:
@Seederclass TypedAccountSeeder : ICanSeedEvents { override fun seed(builder: IEventSeedingBuilder) { builder.forEventType( AccountOpened::class, "account-1", listOf(AccountOpened("account-1", "Alice", 1000.0)) ) }}Seed a specific namespace
Section titled “Seed a specific namespace”By default, seed data targets the event store’s own namespace. Call
forNamespace to target a different one instead — it returns a scoped
builder whose forEventSource/forEventType calls all seed into that
namespace, leaving calls made directly on builder targeting the event
store’s own namespace as before:
@Seederclass MultiNamespaceAccountSeeder : ICanSeedEvents { override fun seed(builder: IEventSeedingBuilder) { builder .forEventSource( "account-1", listOf(AccountOpened("account-1", "Alice", 1000.0)) ) .forNamespace("staging") .forEventSource( "account-1", listOf(AccountOpened("account-1", "Staging Alice", 250.0)) ) }}Running seeders
Section titled “Running seeders”Pass seeder instances to the event store’s seeding service:
import io.cratis.chronicle.IEventStore
suspend fun runSeeders(store: IEventStore) { store.seeding.seed(AccountSeeder())}How it runs
Section titled “How it runs”- Seed batches are sent to the Chronicle Server when the event store connects.
- The server deduplicates seeded events and applies them once per namespace.
- Events are appended in a single batch for efficient startup.
Best practices
Section titled “Best practices”- Keep seed data minimal and deterministic.
- Use clear event source IDs to make debugging easier.
- Group seeders by scenario so you can remove or adjust them easily.
- Only call
store.seeding.seed(...)when you want seeding to run — for example, guard it behind a development-only build flag or configuration check.