@@ -44,7 +44,7 @@ An audit trail may be a regulatory or business requirement.
44
44
45
45
We can store all changes to the domain object state as a sequence of events in an append-only event stream.
46
46
Thus, event streams will contain an entire history of changes.
47
- But how can we be sure that this history is authentic and free from errors ?
47
+ But how can we be sure that this history is authentic and error- free?
48
48
We can use event streams as a primary source of truth in a system.
49
49
To get the current state of an object, we have to replay all events in the order of occurrence.
50
50
This pattern is called event sourcing. The database for storing the event streams is called even store.
@@ -91,7 +91,7 @@ This sample uses a simplified domain model of the ride-hailing system.
91
91
### <a id =" 3-1 " ></a >State-oriented persistence
92
92
93
93
State-oriented persistence (CRUD) applications store only the latest version of an entity.
94
- Entities are presented by database records .
94
+ Database records present entities .
95
95
When an entity is updated, the corresponding database record gets updated too.
96
96
SQL ` INSERT ` , ` UPDATE ` and ` DELETE ` statements are used.
97
97
@@ -109,7 +109,7 @@ Events are immutables, so SQL `UPDATE` and `DELETE` statements are not used.
109
109
110
110
![ Event sourcing] ( img/event-sourcing-2.svg )
111
111
112
- Current state of an entity can be restored by replaying all its events.
112
+ The current state of an entity can be restored by replaying all its events.
113
113
114
114
Event sourcing is closely related to domain-driven design (DDD) and shares some terminology.
115
115
@@ -118,14 +118,14 @@ An entity in event sourcing is called an **aggregate**.
118
118
A sequence of events for the same aggregate is called a ** stream** .
119
119
120
120
Event sourcing is best suited for short-living entities with a small total number of
121
- events (e.g. orders).
121
+ events (e.g., orders).
122
122
123
123
Restoring the state of the short-living entity by replaying all its events doesn't have any
124
124
performance impact. Thus, no optimizations for restoring state are required for short-living
125
125
entities.
126
126
127
- For endlessly stored entities (e.g. users, bank accounts) with thousands of events restoring state
128
- by replaying all events is not optimal and snapshotting should be considered.
127
+ For endlessly stored entities (e.g., users, bank accounts) with thousands of events restoring state
128
+ by replaying all events is not optimal, and snapshotting should be considered.
129
129
130
130
### <a id =" 3-3 " ></a >Snapshotting
131
131
@@ -144,8 +144,8 @@ To restore an aggregate state:
144
144
145
145
### <a id =" 3-4 " ></a >Querying the data
146
146
147
- It's easy to find an aggregate by ID but other queries are difficult.
148
- As far as aggregates are stored as append-only lists of immutable events,
147
+ It's easy to find an aggregate by ID, but other queries are difficult.
148
+ Since aggregates are stored as append-only lists of immutable events,
149
149
querying the data using SQL, as we used to, is impossible.
150
150
To find an aggregate by some field, we need to first read all the events and replay them to restore all the aggregates.
151
151
@@ -174,8 +174,8 @@ A command generates zero or more events or results in an error.
174
174
![ CQRS] ( img/cqrs-1.svg )
175
175
176
176
CQRS is a self-sufficient architectural pattern and doesn't require event sourcing.
177
- But in practice event sourcing is usually used in conjunction with CQRS.
178
- Event store is used as a write database and SQL or NoSQL database as a read database.
177
+ But in practice, event sourcing is usually used in conjunction with CQRS.
178
+ Event store is used as a write database, and SQL or NoSQL database as a read database.
179
179
180
180
![ CQRS with event sourcing] ( img/cqrs-2.svg )
181
181
@@ -186,7 +186,7 @@ Event processing is done by **event handles**.
186
186
As a part of event processing, we may need to update projections,
187
187
send a message to a message broker, or make an API call.
188
188
189
- There are 2 types of event handles: ** synchronous** and ** asynchronous** .
189
+ There are two types of event handles: ** synchronous** and ** asynchronous** .
190
190
191
191
Storing the write model and read model in the same database allows for transactional updates of the read model.
192
192
Each time we append a new event, the projection is updated ** synchronously** in the same transaction.
@@ -218,7 +218,7 @@ The integration event represents the current state of an aggregate, not just cha
218
218
219
219
### <a id =" 3-9 " ></a >Advantages of event sourcing
220
220
221
- * Having a true history of the system (audit and traceability).
221
+ * A true history of the system (audit and traceability).
222
222
An industry standard for implementing audit trail.
223
223
* Ability to put the system in any prior state (e.g. for debugging).
224
224
* New read-side projections can be created as needed (later) from events.
@@ -360,7 +360,7 @@ and processes them:
360
360
WHERE SUBSCRIPTION_NAME = :subscriptionName
361
361
FOR UPDATE SKIP LOCKED
362
362
```
363
- 2. fetch new events
363
+ 2. read new events
364
364
```sql
365
365
SELECT e.ID,
366
366
e.TRANSACTION_ID::text,
@@ -391,11 +391,11 @@ The `ID` column of the `ES_EVENT` table is of type `BIGSERIAL`.
391
391
It' s a notational convenience for creating ID columns having their default values assigned from a ` SEQUENCE` generator.
392
392
393
393
PostgreSQL sequences can' t be rolled back.
394
- `SELECT nextval(' ES_EVENT_ID_SEQ' )` increments the sequence value and returns it .
394
+ `SELECT nextval(' ES_EVENT_ID_SEQ' )` increments and returns the sequence value.
395
395
Even if the transaction is not yet committed, the new sequence value becomes visible to other transactions.
396
396
397
397
If transaction #2 started after transaction #1 but committed first,
398
- the event subscription processor can fetch the events created by transaction #2, update the last processed event ID,
398
+ the event subscription processor can read the events created by transaction #2, update the last processed event ID,
399
399
and thus lose the events created by transaction #1.
400
400
401
401

@@ -450,7 +450,7 @@ from the Spring Integration.
450
450
After restarting the backend, existing subscriptions will only process new events after the last processed event
451
451
and not everything from the first one.
452
452
453
- New subscriptions (event handlers) in the first poll will fetch and process all events.
453
+ New subscriptions (event handlers) in the first poll will read and process all events.
454
454
Be careful, if there are too many events, they may take a long time to process.
455
455
456
456
# ## <a id="4-9"></a>Class diagrams
@@ -479,7 +479,7 @@ Using PostgreSQL as an event store has a lot of advantages, but there are also d
479
479
Integration events are delivered with ** at- least- once** delivery guarantee.
480
480
The exactly- once delivery guarantee is hard to achieve due to a dual- write.
481
481
A dual- write describes a situation when you need to atomically update the database and publish messages
482
- and two- phase commit (2PC) is not an option .
482
+ without two- phase commit (2PC).
483
483
Consumers of integration events should be idempotent and filter duplicates and unordered events.
484
484
2 . The asynchronous event handling results in the ** eventual consistency between the write model and sent integration events** .
485
485
The polling database table for new events with a fixed delay introduces a full consistency lag
0 commit comments