event sourcing w php (by piotr kacała)
TRANSCRIPT
POBIERANIE ZAWARTOŚCI KOSZYKA
mysql> select * from cart_products; +---------+------------+ | cart_id | product_id | +---------+------------+ | 1 | 124 | | 1 | 89 | +---------+------------+
PRZYPOMNIJMY
mysql> select * from cart_products;
+---------+------------+
| cart_id | product_id |
+---------+------------+
| 1 | 124 |
| 1 | 89 |
+---------+------------+
A CO JEŚLI?
select * from salda_bankowe; +---------------------+----------------+ | rachunek | ile_pieniazkow | +---------------------+----------------+ | 4444 3333 2222 1111 | 1000 | +---------------------+----------------+
TRUDNO SIĘ KŁÓCIĆ
+---------------------+----------------+ | rachunek | ile_pieniazkow | +---------------------+----------------+ | 4444 3333 2222 1111 | 1000 | +---------------------+----------------+
EVENT STORE
Cart #1 Was Created
Product #B Added
To The Cart
Product #A Removed
From The Cart
Product #A Added
To The CartCart #1 Was Checked Out
EVENT STORE
Cart #1 Was Created
Product #B Added
To The Cart
Product #A Removed
From The Cart
Product #A Added
To The CartCart #1 Was Checked Out
TRADYCYJNY KOSZYK
class Cart { private $id; private $products = []; public function add($productId) { // changing the state of the cart $this->products[] = $productId; } }
KOSZYK PRODUKUJĄCY EVENTY
class Cart { private $id; private $products = []; private $raisedEvents = []; public function add($productId) { // changing the state of the cart $this->products[] = $productId; // let's raise an event $this->raisedEvents[] = new ProductAddedToCart($this->id, $productId); } }
EVENT STORE
Cart Was Created
Product #2 Added
To The Cart
Product #1 Added
To The CartCart Was
Checked OutProduct #1 Removed
From The Cart
EVENT STORE
mysql> select * from event_store; +----+--------------+-------------------------------------------+ | id | aggregate_id | event | +----+--------------+-------------------------------------------+ | 1 | 1 | CartWasCreated({"cartId":"1"}) | | 2 | 1 | ProductAddedToCart({"productId":"A"}) | | 3 | 1 | ProductRemovedFromCart({"productId":"A"}) | | 4 | 1 | ProductAddedToCart({"productId":"B"}) | +----+--------------+-------------------------------------------+
ODTWARZANIE AGREGATU KOSZYKA W REPOZYTORIUM
class EventSourcedCartRepository implements CartRepository { public function find($aggregateId) { $events = $this->eventStore->findEvents($aggregateId); $cart = new Cart(); // inicjalizacja pustego agregatu // "nagrywanie" eventów na agregacie cartu foreach($events as $event) { // metoda apply() woła odpowiednie metody prywatne // na podstawie nazwy przekazywanego eventu, np. // ProductWasAddedToCart => applyProductWasAddedToCart $cart->apply($event); } } }
ENCJA
class Cart{ // this method is called by event store to replay event on the cart protected function applyProductAddedToCart(ProductAddedToCart $event) { $this->products[] = $event->getProductId(); } }
OBIE METODY
class Cart { public function add($productId) { if ($this->products > 2) { throw new CartLimitExceeded(); } // raise the event $event = new ProductAddedToCart($this->id, $productId); $this->raisedEvents[] = $event; // change the state $this->applyProductAddedToCart($event); } // change the state, this is called by Event Store public function applyProductAddedToCart(ProductAddedToCart $event) { $this->products[] = $event->getProductId(); } }
EVENT STORE
mysql> select * from event_store; +----+--------------+-------------------------------------------+ | id | aggregate_id | event | +----+--------------+-------------------------------------------+ | 1 | 1 | CartWasCreated({"cartId":"1"}) | | 2 | 1 | ProductAddedToCart({"productId":"A"}) | | 3 | 1 | ProductRemovedFromCart({"productId":"A"}) | | 4 | 1 | ProductAddedToCart({"productId":"B"}) | +----+--------------+-------------------------------------------+
PROJEKCJE
class CurrentCartProductsProjector { public function applyProductAddedToCart(ProductAddedToCart $event) { $this->connection->query('insert into cart_products ...'); } public function applyProductRemovedFromCart(ProductRemovedFromCart $event) { $this->connection->query('delete from cart_products ...'); } }
POBIERANIE ZAWARTOŚCI KOSZYKA
mysql> select * from cart_products; +---------+------------+ | cart_id | product_id | +---------+------------+ | 1 | 124 | | 1 | 89 | +---------+------------+
CQRS W PEŁNI SWOJEJ CHWAŁY
Cart::add
Zapis w Event Store
Product Was Added To Cart
Cart State Projector
SQL
Redis
PLUSY EVENT SOURCINGU
- HISTORYCZNY STAN APLIKACJI
- DEBUGOWANIE
- PROSTY MODEL ZAPISU
- ODPORNOŚĆ NA KREATYWNOŚĆ BIZNESU
- ŁATWA SKALOWALNOŚĆ
- WSPÓŁGRA Z MIKROSERWISAMI
MINUSY EVENT SOURCINGU
- PRÓG WEJŚCIA (ZMIANA PRZYZWYCZAJEŃ)
- KONIECZNOŚĆ SHARDOWANIA EVENT STORE’U*
- DŁUŻSZY ŁADOWANIE OBIEKTÓW DOMENOWYCH**
- ZWIĘKSZA ZŁOŻONOŚĆ APLIKACJI (VS CRUD)
* NIE DOTYCZY WSZYSTKICH APLIKACJI
** ROZWIĄZANY PRZEZ SNAPSHOTY