First Store Tutorial
This tutorial gives you the smallest useful Maryk loop:
- define a model,
- create and validate data,
- serialize it,
- store it,
- query it,
- read versioned changes.
Gradle setup
Section titled “Gradle setup”Add the dependencies to your target source set:
repositories { mavenCentral()}
dependencies { implementation("io.maryk:maryk-core:<maryk-version>") implementation("io.maryk:maryk-memory:<maryk-version>") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:<coroutines-version>")}Every stored model extends RootDataModel. Property indexes are the field numbers in the wire/storage contract. Add new property indexes over time; do not reuse old property indexes for different meanings. Query indexes are separate and only needed for extra access paths.
import maryk.core.models.RootDataModelimport maryk.core.properties.definitions.dateimport maryk.core.properties.definitions.stringimport maryk.lib.time.LocalDate
object Person : RootDataModel<Person>() { val firstName by string(index = 1u) val lastName by string(index = 2u) val dateOfBirth by date(index = 3u)}Create and serialize
Section titled “Create and serialize”val jane = Person.create { firstName with "Jane" lastName with "Doe" dateOfBirth with LocalDate(1990, 1, 15)}
Person.validate(jane)
val json = Person.Serializer.writeJson(jane, pretty = true)val sameJane = Person.Serializer.readJson(json)Use the model serializer for JSON directly. The same serializer can also write through JSON-like writers, including YAML. Use ProtoBuf for compact binary transport once both sides already know the model.
Store and query
Section titled “Store and query”import kotlinx.coroutines.runBlockingimport maryk.core.query.requests.addimport maryk.core.query.requests.getimport maryk.core.query.requests.getChangesimport maryk.datastore.memory.InMemoryDataStore
fun main() = runBlocking { InMemoryDataStore.open( keepAllVersions = true, keepUpdateHistoryIndex = true, dataModelsById = mapOf(1u to Person) ).use { val key = Person.key(jane) execute(Person.add(key to jane))
val current = execute(Person.get(key)) val values = current.values.single().values
println(values { firstName }) // Jane }}dataModelsById is the store registry. Keep IDs stable for persistent stores. ID 0u is reserved.
Passing a known key keeps examples and tests easy to read. You can also let the store create keys and inspect add statuses when that better matches your app.
Read history
Section titled “Read history”With keepAllVersions = true, Maryk can answer “what changed?” and “what did this look like at version T?”
Inside the same .use block:
val changes = execute(Person.getChanges(key))val firstVersion = changes.changes.first().changes.first().version
val historic = execute( Person.get( key, toVersion = firstVersion ))Use this for sync, audit views, debugging, and conflict-aware clients.
Complete file
Section titled “Complete file”import kotlinx.coroutines.runBlockingimport maryk.core.models.RootDataModelimport maryk.core.properties.definitions.dateimport maryk.core.properties.definitions.stringimport maryk.core.query.requests.addimport maryk.core.query.requests.getimport maryk.core.query.requests.getChangesimport maryk.datastore.memory.InMemoryDataStoreimport maryk.lib.time.LocalDate
object Person : RootDataModel<Person>() { val firstName by string(index = 1u) val lastName by string(index = 2u) val dateOfBirth by date(index = 3u)}
fun main() = runBlocking { val jane = Person.create { firstName with "Jane" lastName with "Doe" dateOfBirth with LocalDate(1990, 1, 15) }
Person.validate(jane)
val json = Person.Serializer.writeJson(jane, pretty = true) val sameJane = Person.Serializer.readJson(json)
InMemoryDataStore.open( keepAllVersions = true, keepUpdateHistoryIndex = true, dataModelsById = mapOf(1u to Person) ).use { val key = Person.key(sameJane) execute(Person.add(key to sameJane))
val current = execute(Person.get(key)) val values = current.values.single().values println(values { firstName })
val changes = execute(Person.getChanges(key)) val firstStoredChange = changes.changes.first().changes.first()
val historic = execute( Person.get( key, toVersion = firstStoredChange.version ) )
println(historic.values.single().values { lastName }) }}Next choices
Section titled “Next choices”- Keep using Memory Store for tests and examples.
- Use RocksDB when your app needs embedded persistence.
- Use FoundationDB when a server deployment needs distributed ACID storage.
- Use Remote Store when tools, the CLI, or the App need to connect over a network boundary.
What to learn next
Section titled “What to learn next”- Data Design for embed/reference/key choices.
- Querying for add/change/delete/get/scan/update APIs.
- Versioning for history and sync behavior.
- Changing Models Safely before changing models with existing data.
- Property types for validation and modeling options.