Skip to content

Commit e6fd534

Browse files
committed
Update README.md
1 parent b692576 commit e6fd534

File tree

1 file changed

+17
-17
lines changed

1 file changed

+17
-17
lines changed

README.md

+17-17
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ An audit trail may be a regulatory or business requirement.
4444

4545
We can store all changes to the domain object state as a sequence of events in an append-only event stream.
4646
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?
4848
We can use event streams as a primary source of truth in a system.
4949
To get the current state of an object, we have to replay all events in the order of occurrence.
5050
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.
9191
### <a id="3-1"></a>State-oriented persistence
9292

9393
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.
9595
When an entity is updated, the corresponding database record gets updated too.
9696
SQL `INSERT`, `UPDATE` and `DELETE` statements are used.
9797

@@ -109,7 +109,7 @@ Events are immutables, so SQL `UPDATE` and `DELETE` statements are not used.
109109

110110
![Event sourcing](img/event-sourcing-2.svg)
111111

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.
113113

114114
Event sourcing is closely related to domain-driven design (DDD) and shares some terminology.
115115

@@ -118,14 +118,14 @@ An entity in event sourcing is called an **aggregate**.
118118
A sequence of events for the same aggregate is called a **stream**.
119119

120120
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).
122122

123123
Restoring the state of the short-living entity by replaying all its events doesn't have any
124124
performance impact. Thus, no optimizations for restoring state are required for short-living
125125
entities.
126126

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.
129129

130130
### <a id="3-3"></a>Snapshotting
131131

@@ -144,8 +144,8 @@ To restore an aggregate state:
144144

145145
### <a id="3-4"></a>Querying the data
146146

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,
149149
querying the data using SQL, as we used to, is impossible.
150150
To find an aggregate by some field, we need to first read all the events and replay them to restore all the aggregates.
151151

@@ -174,8 +174,8 @@ A command generates zero or more events or results in an error.
174174
![CQRS](img/cqrs-1.svg)
175175

176176
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.
179179

180180
![CQRS with event sourcing](img/cqrs-2.svg)
181181

@@ -186,7 +186,7 @@ Event processing is done by **event handles**.
186186
As a part of event processing, we may need to update projections,
187187
send a message to a message broker, or make an API call.
188188

189-
There are 2 types of event handles: **synchronous** and **asynchronous**.
189+
There are two types of event handles: **synchronous** and **asynchronous**.
190190

191191
Storing the write model and read model in the same database allows for transactional updates of the read model.
192192
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
218218

219219
### <a id="3-9"></a>Advantages of event sourcing
220220

221-
* Having a true history of the system (audit and traceability).
221+
* A true history of the system (audit and traceability).
222222
An industry standard for implementing audit trail.
223223
* Ability to put the system in any prior state (e.g. for debugging).
224224
* New read-side projections can be created as needed (later) from events.
@@ -360,7 +360,7 @@ and processes them:
360360
WHERE SUBSCRIPTION_NAME = :subscriptionName
361361
FOR UPDATE SKIP LOCKED
362362
```
363-
2. fetch new events
363+
2. read new events
364364
```sql
365365
SELECT e.ID,
366366
e.TRANSACTION_ID::text,
@@ -391,11 +391,11 @@ The `ID` column of the `ES_EVENT` table is of type `BIGSERIAL`.
391391
It's a notational convenience for creating ID columns having their default values assigned from a `SEQUENCE` generator.
392392

393393
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.
395395
Even if the transaction is not yet committed, the new sequence value becomes visible to other transactions.
396396
397397
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,
399399
and thus lose the events created by transaction #1.
400400
401401
![PostgreSQL naive transactional outbox](img/postgresql-naive-outbox.svg)
@@ -450,7 +450,7 @@ from the Spring Integration.
450450
After restarting the backend, existing subscriptions will only process new events after the last processed event
451451
and not everything from the first one.
452452

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.
454454
Be careful, if there are too many events, they may take a long time to process.
455455

456456
### <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
479479
Integration events are delivered with **at-least-once** delivery guarantee.
480480
The exactly-once delivery guarantee is hard to achieve due to a dual-write.
481481
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).
483483
Consumers of integration events should be idempotent and filter duplicates and unordered events.
484484
2. The asynchronous event handling results in the **eventual consistency between the write model and sent integration events**.
485485
The polling database table for new events with a fixed delay introduces a full consistency lag

0 commit comments

Comments
 (0)