Skip to content

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.

import io.cratis.chronicle.events.EventType
@EventType
data class AccountOpened(
val accountId: String,
val ownerName: String,
val initialBalance: Double
)
@EventType
data class FundsDeposited(val accountId: String, val amount: Double)

Annotate a class with @Seeder, implement ICanSeedEvents, and use IEventSeedingBuilder.forEventSource to accumulate events:

import io.cratis.chronicle.seeding.ICanSeedEvents
import io.cratis.chronicle.seeding.IEventSeedingBuilder
import io.cratis.chronicle.seeding.Seeder
@Seeder
class 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:

@Seeder
class 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:

@Seeder
class 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))
)
}
}

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:

@Seeder
class TypedAccountSeeder : ICanSeedEvents {
override fun seed(builder: IEventSeedingBuilder) {
builder.forEventType(
AccountOpened::class,
"account-1",
listOf(AccountOpened("account-1", "Alice", 1000.0))
)
}
}

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:

@Seeder
class 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))
)
}
}

Pass seeder instances to the event store’s seeding service:

import io.cratis.chronicle.IEventStore
suspend fun runSeeders(store: IEventStore) {
store.seeding.seed(AccountSeeder())
}
  • 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.
  • 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.