Skip to content

Commit a9a25df

Browse files
committed
Update documentation
1 parent 972986b commit a9a25df

File tree

4 files changed

+153
-44
lines changed

4 files changed

+153
-44
lines changed

docs/.vitepress/config.mts

Lines changed: 34 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,46 +2,41 @@ import {defineConfig} from 'vitepress'
22

33
// https://vitepress.dev/reference/site-config
44
export default defineConfig({
5-
title: "Krescent",
6-
description: "Kotlin Event Sourcing Library",
7-
base: '/krescent/',
8-
cleanUrls: true,
9-
lang: 'en-US',
10-
appearance: 'dark',
11-
lastUpdated: true,
12-
ignoreDeadLinks: 'localhostLinks',
13-
themeConfig: {
14-
// https://vitepress.dev/reference/default-theme-config
15-
nav: [
16-
{ text: 'Home', link: '/' },
17-
{ text: 'Guide', link: '/guide/installation' }
18-
],
19-
search: {
20-
provider: "local"
21-
},
5+
title: "Krescent",
6+
description: "Kotlin Event Sourcing Library",
7+
base: '/krescent/',
8+
cleanUrls: true,
9+
lang: 'en-US',
10+
appearance: 'dark',
11+
lastUpdated: true,
12+
ignoreDeadLinks: 'localhostLinks',
13+
themeConfig: {
14+
// https://vitepress.dev/reference/default-theme-config
15+
nav: [
16+
{text: 'Home', link: '/'},
17+
{text: 'Guide', link: '/guide/installation'}
18+
],
19+
search: {
20+
provider: "local"
21+
},
2222

23-
footer: {
24-
copyright: "Released under the Apache License 2.0",
25-
},
23+
footer: {
24+
copyright: "Released under the Apache License 2.0",
25+
},
2626

27-
sidebar: [
28-
{
29-
text: 'Concepts',
30-
items: [
31-
{ text: 'Events', link: '/concepts/events' },
32-
{text: 'Models', link: '/concepts/models'},
33-
{text: 'Checkpointing', link: '/concepts/checkpointing'}
27+
sidebar: [
28+
{
29+
text: 'Guide',
30+
items: [
31+
{text: 'Installation', link: '/guide/installation'},
32+
{text: 'Events', link: '/guide/events'},
33+
{text: 'Models', link: '/guide/models'},
34+
{text: 'Checkpointing', link: '/guide/checkpointing'}
35+
]
36+
}
37+
],
38+
socialLinks: [
39+
{icon: 'github', link: 'https://github.com/helightdev/krescent'}
3440
]
35-
},
36-
{
37-
text: 'Guide',
38-
items: [
39-
{ text: 'Installation', link: '/guide/installation' },
40-
]
41-
}
42-
],
43-
socialLinks: [
44-
{ icon: 'github', link: 'https://github.com/helightdev/krescent' }
45-
]
46-
}
41+
}
4742
})
Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,34 @@
22
title: Checkpointing
33
---
44

5+
# Checkpointing
6+
57
Checkpointing in Krescent is a crucial optimization mechanism for event models. Its primary purpose is to periodically
68
save the current state of an event model, along with the position (e.g., `StreamingToken`) of the last event processed
79
to reach that state. This avoids the need to reprocess the entire event stream from the beginning every time the model
810
is initialized or restarted.
911

12+
### Example Configuration
13+
14+
```kotlin
15+
val mongoCheckpointStorage = MongoCheckpointStorage(mongoDatabase)
16+
17+
AllAvailableBooksReadModel(
18+
collectionName = "available_books",
19+
database = mongoDatabase
20+
).withConfiguration { // [!code focus:6]
21+
useCheckpoints( // [!code ++:4]
22+
mongoCheckpointStorage,
23+
FixedEventRateCheckpointStrategy(25)
24+
)
25+
}.stream(eventSource)
26+
```
27+
28+
> You can configure checkpoint using the model builder api, in this example using the `withConfiguration` method.
29+
> All model extensions used by this model will now use the configured checkpoint storage and strategy for checkpointing.
30+
> Not using checkpointing will result in the model performing a full replay of the event stream every time it is
31+
> initialized.
32+
1033
## `CheckpointStrategy`
1134

1235
A `CheckpointStrategy` determines *when* a checkpoint should be taken. Krescent offers several built-in strategies:
@@ -25,9 +48,15 @@ A `CheckpointStrategy` determines *when* a checkpoint should be taken. Krescent
2548

2649
The `CheckpointStorage` interface defines *how* and *where* checkpoints are saved and loaded. Implementations of this
2750
interface are responsible for serializing the model's state and the associated `StreamingToken`, persisting them to a
28-
durable store (like a file system, database, or cloud storage), and retrieving them when needed.
51+
durable store (like a file system, database or cloud storage), and retrieving them when needed.
2952

3053
## `CheckpointSupport`
3154

32-
`CheckpointSupport` is an interface that must be implemented by components (typically event models or parts of them)
33-
that can be checkpointed.
55+
`CheckpointSupport` is an interface that may be implemented by event models or model extensions to provide
56+
methods for creating and restoring checkpoints. Data is written to `CheckpointBuckets` which support primitive
57+
types, JSON objects and binary data.
58+
59+
> [!WARNING]
60+
> Some model extensions may only write references to the data in the checkpoint bucket, not the actual data. When
61+
> deleting tables, databases, etc. related to models which are using checkpointing, make sure to also delete the
62+
> corresponding checkpoints if the data is referential.
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
title: Events
33
---
44

5+
# Events
6+
57
Events in Krescent are immutable facts that represent something that has happened in the system. They are the primary
68
building blocks of Krescent's event-driven architecture.
79

@@ -16,7 +18,7 @@ In Krescent, events can be categorized into three types:
1618
- **System Events**: These are just a special case of virtual events which are only emitted by the framework and usually
1719
represent internal state changes, notifications or stream state changes.
1820

19-
#### Example Event Definition
21+
### Example Event Definitions
2022

2123
```kotlin
2224
@Serializable
Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
title: Models
33
---
44

5+
# Models
6+
57
Models in Krescent are the core components of the framework yet relatively loosely defined. In essence, they are
68
end-user flavored event handlers with additional syntactic sugar. While there is a base `EventModelBase`, you will
79
mostly
@@ -144,4 +146,85 @@ BookWriteModel("1", lockProvider).withSource(eventSource) handles { // [!code fo
144146
> `handles` infix function to execute the model build. This will do a transactional catchup replay from the event
145147
> source and then execute the specified block while still in transaction. We use this scope to execute the
146148
> `lend` method to create a new event and emit it to the event source. After this operation, the events will be
147-
> added to the event source and the transaction will be finished, freeing the lock that has been acquired.
149+
> added to the event source and the transaction will be finished, freeing the lock that has been acquired.
150+
151+
## Reducing Models
152+
153+
Krescent also provides a reduce-style syntax for models, which automatically adds serialization for
154+
checkpointing and enforces clearly visible state changes.
155+
156+
### Example Reducing Read Model
157+
158+
```kotlin
159+
class BooksAvailableModel() : ReducingReadModel<BooksAvailableModel.State>(
160+
"book.available", 1, bookstoreEventCatalog
161+
) {
162+
override val initialState: State
163+
get() = State()
164+
165+
override suspend fun reduce(state: State, event: Event) = when (event) {
166+
is BookAddedEvent -> state.copy(
167+
available = state.available + (event.bookId to event.copies)
168+
)
169+
// Other events can be handled here
170+
else -> state
171+
}
172+
173+
@Serializable
174+
data class State(
175+
val available: Map<String, Int> = emptyMap(),
176+
)
177+
}
178+
````
179+
180+
### Example Reducing Write Model
181+
182+
````kotlin
183+
class ReducingBookWriteModel(
184+
val bookId: String,
185+
val lockProvider: KrescentLockProvider,
186+
) : ReducingWriteModel<ReducingBookWriteModel.State>(
187+
"test", 1, bookstoreEventCatalog,
188+
configure = { useTransaction(lockProvider, "book-$bookId") }
189+
) {
190+
override val initialState: State
191+
get() = State()
192+
193+
override suspend fun reduce(state: State, event: Event): State {
194+
if (event !is BookEvent || event.bookId != bookId) return state
195+
return when (event) {
196+
is BookAddedEvent -> state.copy(
197+
book = BookState(
198+
title = event.title, author = event.author,
199+
price = event.price, copies = event.copies
200+
), available = event.copies
201+
)
202+
// Handle other events here
203+
is BookLentEvent -> state.copy(available = state.available - 1)
204+
is BookReturnedEvent -> state.copy(available = state.available + 1)
205+
else -> state
206+
}
207+
}
208+
209+
// [!code focus:7]
210+
suspend fun lend(userId: String) = useState {
211+
if (canBeLent(1)) error("No book available to be lent.")
212+
emitEvent(BookLentEvent(bookId, userId, "2025-1-1"))
213+
214+
// Use .push() to update the internal state of the model
215+
state.copy(available = state.available - 1).push()
216+
}
217+
218+
// [!code focus:4]
219+
// Use useState() to access the current state of the model as "state"
220+
fun canBeLent(count: Int): Boolean = useState {
221+
state.available >= count
222+
}
223+
224+
@Serializable
225+
data class State(
226+
val book: BookState? = null,
227+
val available: Int = 0,
228+
)
229+
}
230+
````

0 commit comments

Comments
 (0)