Skip to content

Commit 5cd02ad

Browse files
committed
update documentation
1 parent e0edc32 commit 5cd02ad

1 file changed

Lines changed: 45 additions & 64 deletions

File tree

docs/pages/dynamic_consistency_boundary.md

Lines changed: 45 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,85 +1,83 @@
11
# Dynamic Consistency Boundary
22

3-
In our little DCB getting-started example, we decide things atomically across multiple event streams without loading aggregates.
4-
We keep the example small and show how to validate a course subscription and how to generate invoice numbers using the DCB API.
3+
??? example "Experimental"
4+
5+
This feature is still experimental and may change in the future.
6+
Use it with caution.
7+
8+
Dynamic Consistency Boundary (DCB) is an event‑sourcing approach for making consistent,
9+
cross‑stream decisions without loading full aggregates.
10+
For each decision, it builds a minimal, purpose‑built state from a targeted subset of events selected via tags.
11+
Lightweight projections compute just the values needed to validate commands and derive new events.
12+
The decision evaluation and event append are coupled by an optimistic append condition to prevent race conditions;
13+
if the queried subset changes concurrently, the write is rejected and can be retried.
14+
This makes handlers simple, fast, and scalable, since only relevant events are processed.
15+
DCB is a great fit when business rules span multiple streams.
516

617
!!! note
718

8-
DCB is marked as experimental. APIs may change.
19+
You can read more about Dynamic Consistency Boundary on page [dcb.events](https://dcb.events/).
20+
21+
Since this approach differs slightly from the standard "aggregate" event sourcing principle,
22+
we will use the [Getting Started](./getting_started.md) example and build it as a DCB variant.
23+
24+
In our litt le getting started example, we manage hotels.
25+
We keep the example small, so we can only create hotels and let guests check in and check out.
926

1027
## Define some events
1128

1229
First we define the events that happen in our system.
1330

14-
A course can be defined with a capacity:
31+
A hotel can be created with a `name` and a `id`:
1532

1633
```php
34+
use Patchlevel\EventSourcing\Aggregate\Uuid;
1735
use Patchlevel\EventSourcing\Attribute\Event;
1836
use Patchlevel\EventSourcing\Attribute\EventTag;
1937

20-
#[Event('course.defined')]
21-
final class CourseDefined
38+
#[Event('hotel.created')]
39+
final class HotelCreated
2240
{
2341
public function __construct(
24-
#[EventTag(prefix: 'course')]
25-
public CourseId $courseId,
26-
public int $capacity,
42+
#[EventTag(prefix: 'hotel')]
43+
public readonly Uuid $hotelId,
44+
public readonly string $hotelName,
2745
) {
2846
}
2947
}
3048
```
31-
A course capacity can change later:
49+
A guest can check in by `name`:
3250

3351
```php
3452
use Patchlevel\EventSourcing\Attribute\Event;
35-
use Patchlevel\EventSourcing\Attribute\EventTag;
3653

37-
#[Event('course.capacity_changed')]
38-
final class CourseCapacityChanged
54+
#[Event('hotel.guest_checked_in')]
55+
final class GuestIsCheckedIn
3956
{
4057
public function __construct(
41-
#[EventTag(prefix: 'course')]
42-
public CourseId $courseId,
43-
public int $capacity,
58+
public readonly string $guestName,
4459
) {
4560
}
4661
}
4762
```
48-
A student can subscribe to a course. We tag the event with both student and course, so that DCB projections can select the exact subset of events:
63+
And also check out again:
4964

5065
```php
5166
use Patchlevel\EventSourcing\Attribute\Event;
52-
use Patchlevel\EventSourcing\Attribute\EventTag;
5367

54-
#[Event('course.student_subscribed')]
55-
final class StudentSubscribedToCourse
68+
#[Event('hotel.guest_checked_out')]
69+
final class GuestIsCheckedOut
5670
{
5771
public function __construct(
58-
#[EventTag(prefix: 'student')]
59-
public readonly StudentId $studentId,
60-
#[EventTag(prefix: 'course')]
61-
public readonly CourseId $courseId,
72+
public readonly string $guestName,
6273
) {
6374
}
6475
}
6576
```
66-
And finally, an invoice can be created with a monotonically increasing number:
77+
!!! note
6778

68-
```php
69-
use Patchlevel\EventSourcing\Attribute\Event;
70-
use Patchlevel\EventSourcing\Attribute\EventTag;
79+
You can find out more about events [here](events.md).
7180

72-
#[Event('invoice.created')]
73-
final class InvoiceCreated
74-
{
75-
public function __construct(
76-
#[EventTag(prefix: 'invoice')]
77-
public readonly int $invoiceNumber,
78-
public readonly int $money,
79-
) {
80-
}
81-
}
82-
```
8381

8482
## Define projections
8583

@@ -93,13 +91,10 @@ Course exists:
9391
```php
9492
use Patchlevel\EventSourcing\Attribute\Apply;
9593
use Patchlevel\EventSourcing\DCB\BasicProjection;
96-
use Patchlevel\EventSourcing\DCB\Projection;
9794

98-
/** @implements Projection<bool> */
99-
final class CourseExists implements Projection
95+
/** @extends BasicProjection<bool> */
96+
final class CourseExists extends BasicProjection
10097
{
101-
use BasicProjection;
102-
10398
public function __construct(private readonly CourseId $courseId) {}
10499

105100
public function initialState(): bool
@@ -126,12 +121,9 @@ Current capacity of a course:
126121
```php
127122
use Patchlevel\EventSourcing\Attribute\Apply;
128123
use Patchlevel\EventSourcing\DCB\BasicProjection;
129-
use Patchlevel\EventSourcing\DCB\Projection;
130124

131-
final class CourseCapacityProjection implements Projection
125+
final class CourseCapacityProjection extends BasicProjection
132126
{
133-
use BasicProjection;
134-
135127
public function __construct(private readonly CourseId $courseId) {}
136128

137129
public function initialState(): int
@@ -164,12 +156,9 @@ Count subscriptions of a course:
164156
```php
165157
use Patchlevel\EventSourcing\Attribute\Apply;
166158
use Patchlevel\EventSourcing\DCB\BasicProjection;
167-
use Patchlevel\EventSourcing\DCB\Projection;
168159

169-
final class NumberOfCourseSubscriptionsProjection implements Projection
160+
final class NumberOfCourseSubscriptionsProjection extends BasicProjection
170161
{
171-
use BasicProjection;
172-
173162
public function __construct(private readonly CourseId $courseId) {}
174163

175164
public function initialState(): int
@@ -198,10 +187,8 @@ use Patchlevel\EventSourcing\Attribute\Apply;
198187
use Patchlevel\EventSourcing\DCB\BasicProjection;
199188
use Patchlevel\EventSourcing\DCB\Projection;
200189

201-
final class NumberOfStudentSubscriptionsProjection implements Projection
190+
final class NumberOfStudentSubscriptionsProjection extends BasicProjection
202191
{
203-
use BasicProjection;
204-
205192
public function __construct(private readonly StudentId $studentId) {}
206193

207194
public function initialState(): int
@@ -228,13 +215,10 @@ Has the student already subscribed to this course:
228215
```php
229216
use Patchlevel\EventSourcing\Attribute\Apply;
230217
use Patchlevel\EventSourcing\DCB\BasicProjection;
231-
use Patchlevel\EventSourcing\DCB\Projection;
232218

233-
/** @implements Projection<bool> */
234-
final class StudentAlreadySubscribedProjection implements Projection
219+
/** @extends BasicProjection<bool> */
220+
final class StudentAlreadySubscribedProjection extends BasicProjection
235221
{
236-
use BasicProjection;
237-
238222
public function __construct(
239223
private readonly StudentId $studentId,
240224
private readonly CourseId $courseId,
@@ -267,12 +251,9 @@ Next invoice number from the last event only:
267251
```php
268252
use Patchlevel\EventSourcing\Attribute\Apply;
269253
use Patchlevel\EventSourcing\DCB\BasicProjection;
270-
use Patchlevel\EventSourcing\DCB\Projection;
271254

272-
final class NextInvoiceNumberProjection implements Projection
255+
final class NextInvoiceNumberProjection extends BasicProjection
273256
{
274-
use BasicProjection;
275-
276257
public function initialState(): int
277258
{
278259
return 1;

0 commit comments

Comments
 (0)