Skip to content

Commit 506fd3a

Browse files
jlabedogitbook-bot
authored andcommitted
GITBOOK-845: Preparation for Enterprise
1 parent aa0fe88 commit 506fd3a

File tree

13 files changed

+513
-155
lines changed

13 files changed

+513
-155
lines changed

.gitbook/assets/event_stream.png

45.2 KB
Loading

.gitbook/assets/event_stream_2.png

46.3 KB
Loading

.gitbook/assets/image (2).png

16.1 KB
Loading

.gitbook/assets/image (3).png

16.1 KB
Loading

.gitbook/assets/metadata.png

45.8 KB
Loading
20.1 KB
Loading

SUMMARY.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,11 @@
4747
* [Message Headers](modelling/extending-messaging-middlewares/message-headers.md)
4848
* [Interceptors (Middlewares)](modelling/extending-messaging-middlewares/interceptors.md)
4949
* [Event Sourcing](modelling/event-sourcing/README.md)
50-
* [Event Sourcing Introduction](modelling/event-sourcing/event-sourcing-introduction.md)
50+
* [Event Sourcing Introduction](modelling/event-sourcing/event-sourcing-introduction/README.md)
51+
* [Creating New Event Stream](modelling/event-sourcing/event-sourcing-introduction/creating-new-event-stream.md)
52+
* [Applying Events](modelling/event-sourcing/event-sourcing-introduction/applying-events.md)
53+
* [Working with Metadata](modelling/event-sourcing/event-sourcing-introduction/working-with-metadata.md)
54+
* [Event versioning](modelling/event-sourcing/event-sourcing-introduction/event-versioning.md)
5155
* [Setting up Projections](modelling/event-sourcing/setting-up-projections/README.md)
5256
* [Choosing Event Streams for Projection](modelling/event-sourcing/setting-up-projections/choosing-event-streams-for-projection.md)
5357
* [Persistence](modelling/event-sourcing/persistence/README.md)
@@ -59,7 +63,6 @@
5963
* [Access Event Store](modelling/event-sourcing/executing-and-managing/access-event-store.md)
6064
* [Emitting events](modelling/event-sourcing/emitting-events.md)
6165
* [Projections with State](modelling/event-sourcing/projections-with-state.md)
62-
* [Event versioning](modelling/event-sourcing/event-versioning.md)
6366
* [Recovering, Tracing and Monitoring](modelling/recovering-tracing-and-monitoring/README.md)
6467
* [Resiliency](modelling/recovering-tracing-and-monitoring/resiliency/README.md)
6568
* [Retries](modelling/recovering-tracing-and-monitoring/resiliency/retries.md)

modelling/event-sourcing/event-sourcing-introduction.md

Lines changed: 0 additions & 153 deletions
This file was deleted.
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
---
2+
description: Using Event Sourcing in PHP
3+
---
4+
5+
# Event Sourcing Introduction
6+
7+
Before diving into this section be sure to understand how Aggregates works in Ecotone based on [previous sections](../../command-handling/state-stored-aggregate/).
8+
9+
## Difference between Aggregate Types
10+
11+
Ecotone provides higher level abstraction to work with Event Sourcing, which is based on Event Sourced Aggregates. Event Sourced Aggregate just like normal Aggregates protect our business rules, the difference is in how they are stored. 
12+
13+
### State-Stored Aggregates
14+
15+
Normal Aggregates are stored based on their current state:
16+
17+
<figure><img src="../../../.gitbook/assets/state_stored_aggregate.png" alt=""><figcaption><p>State-Stored Aggregate State</p></figcaption></figure>
18+
19+
Yet if we change the state, then our previous history is lost:
20+
21+
<figure><img src="../../../.gitbook/assets/product_aggregate_price_change.png" alt=""><figcaption><p>Price was changed, we don't know what was the previous price anymore</p></figcaption></figure>
22+
23+
Having only the current state may be fine in a lot of cases and in those situation it's perfectly fine to make use of [State-Stored Aggregates](../../command-handling/state-stored-aggregate/#state-stored-aggregate). This is most easy way of dealing with changes, we change and we forget the history, as we are interested only in current state.&#x20;
24+
25+
{% hint style="success" %}
26+
When we actually need to know what was the history of changes, then State-Stored Aggregates are not right path for this. If we will try to adjust them so they are aware of history we will most likely complicate our business code. This is not necessary as there is better solution - **Event Sourced Aggregates**.
27+
{% endhint %}
28+
29+
### Event Sourcing Aggregate&#x20;
30+
31+
When we are interested in history of changes, then Event Sourced Aggregate will help us. \
32+
Event Sourced Aggregates are stored in forms of Events. This way we preserve all the history of given Aggregate:
33+
34+
<figure><img src="../../../.gitbook/assets/es_product.png" alt=""><figcaption><p>Event-Sourced Aggregate</p></figcaption></figure>
35+
36+
When we change the state the previous Event is preserved, yet we add another one to the audit trail (Event Stream).
37+
38+
<figure><img src="../../../.gitbook/assets/es_price_change.png" alt=""><figcaption><p>Price was changed, yet we still have the previous Event in audit trail (Event Stream)</p></figcaption></figure>
39+
40+
This way all changes are preserved and we are able to know what was the historic changes of the Product.
41+
42+
## Event Stream
43+
44+
The audit trail of all the Events that happened for given Aggregate is called **Event Stream**.\
45+
Event Stream contains of all historic Events for all instance of specific Aggregate type, for example al Events for Product Aggregate
46+
47+
<figure><img src="../../../.gitbook/assets/event_strea.png" alt=""><figcaption><p>Product Event Stream</p></figcaption></figure>
48+
49+
Let's now see how we can make use of it in the code.
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Applying Events
2+
3+
As [mentioned earlier](./), Events are stored in form of a Event Stream.\
4+
Event Stream is audit of Events, which happened in the past. \
5+
However to protect our business invariants, we may want to work with current state of the Aggregate to know, if given action is possible or not (business invariants).&#x20;
6+
7+
## Business Invariants
8+
9+
Business Invariants in short are our simple "**if"** statements inside the Command Handler in the Aggregate. Those protect our Aggregate from moving into incorrect state. \
10+
With State-Stored Aggregates, we always have current state of the Aggregate, therefore we can check the invariants right away. \
11+
With Event-Sourcing Aggregates, we store them in form of an Events, therefore we need to rebuild our Aggregate, in order to protect the invariants.
12+
13+
14+
15+
Suppose we have Ticket Event Sourcing Aggregate.
16+
17+
```php
18+
#[EventSourcingAggregate]
19+
class Ticket
20+
{
21+
use WithAggregateVersioning;
22+
23+
#[Identifier]
24+
private string $ticketId;
25+
26+
(...)
27+
28+
#[CommandHandler]
29+
public function assign(AssignPerson $command) : array
30+
{
31+
return [new PersonWasAssigned($this->ticketId, $command->personId)];
32+
}
33+
}
34+
```
35+
36+
For this Ticket we do allow for assigning an Person to handle the Ticket. \
37+
Let's suppose however, that Business asked us to allow only one Person to be assigned to the Ticket at time. With current code we could assign unlimited people to the Ticket, therefore we need to protect this invariant.&#x20;
38+
39+
To check if whatever Ticket was already assigned to a Person, our Aggregate need to have state applied which will tell him whatever the Ticket was already assigned.\
40+
To do this we use **EventSourcingHandler attribute passing as first argument given Event class.** This method will be called on reconstruction of this Aggregate. So when this Aggregate will be loaded, if given Event was recorded in the Event Stream, method will be called:
41+
42+
```php
43+
#[EventSourcingAggregate]
44+
class Ticket
45+
{
46+
use WithAggregateVersioning;
47+
48+
#[Identifier]
49+
private string $ticketId;
50+
private bool $isAssigned;
51+
52+
#[CommandHandler]
53+
public function assign(AssignPerson $command) : array
54+
{
55+
if ($this-isAssigned) {
56+
throw new \InvalidArgumentException("Ticket already assigned");
57+
}
58+
59+
return [new PersonWasAssigned($this->ticketId, $command->personId)];
60+
}
61+
62+
#[EventSourcingHandler]
63+
public function applyPersonWasAssigned(PersonWasAssigned $event) : void
64+
{
65+
$this->isAssigned = true;
66+
}
67+
}
68+
```
69+
70+
Then this state, can be used in the Command Handler to decide whatever we can trigger an action or not:
71+
72+
```php
73+
if ($this-isAssigned) {
74+
throw new \InvalidArgumentException("Ticket already assigned");
75+
}
76+
```
77+
78+
{% hint style="success" %}
79+
As you can see, it make sense to only assign to the state attributes that protect our invariants. This way the Aggregate stays readable and clean of unused information.
80+
{% endhint %}

0 commit comments

Comments
 (0)