Qu'est-ce que l'Event Sourcing ?
Stocker l'état de l'application comme une succession d'évènements
3 concepts importants sur les évènements
Portefeuille en ligne
Que se passe-t-il si je ne suis pas d'accord ?
Compte | Montant |
Thibaud | 42,00€ |
Jane | 1337,00€ |
John | -123,00€ |
Event Sourcing à la rescousse
Date | Compte | Libellé | Changement |
20/01/2017 | Thibaud | Remboursement | +20,00€ |
12/01/2017 | Thibaud | Facture | -20,00€ |
08/01/2017 | Thibaud | Courses | -58,00€ |
06/01/2017 | Thibaud | Virement initial | +100,00€ |
Avantages
Un audit complet des évènements de notre application
Les nouvelles fonctionnalités profitent des informations passées
Inconvénients
Impossible d'effectuer des requêtes simplement
Plus long et compliqué à mettre en place
Les évènements
<?php
class TransactionMade extends Event
{
private $account;
private $label;
private $amount;
public function __construct($account, $label, $amount)
{
$this->account = $account;
$this->label = $label;
$this->amount = $amount;
}
}
Stockage des évènements
- NoSQL avec EventStore
- SQL avec une table par évènement
- SQL ou NoSQL (type MongoDB) avec une table unique avec un champ JSON libre pour les données additionnelles
- Sérialisés dans un fichier
Aggregates
<?php
class Account extends Aggregate
{
public static function new($name, $amount)
{
if ($amount < 0) {
throw new InitialAccountTransactionMustBePositive;
}
$account = new self;
$account->raise(
new TransactionMade($name, 'Virement initial', $amount)
);
}
}
Aggregates
<?php
abstract class Aggregate
{
public function raise(Event $event)
{
$event->save();
$this->apply($event);
dispatch($event);
}
}
Aggregates
<?php
class Account extends Aggregate
{
public function apply(Event $event)
{
if ($event instanceof TransactionMade) {
if (! isset($this->name)) {
$this->name = $event->getAccount();
}
$this->amount += $event->getAmount();
}
}
}
Aggregates
<?php
class Account extends Aggregate
{
public function pay($label, $amount)
{
if ($amount > $this->amount) {
throw new NotEnoughMoney;
}
$this->raise(
new TransactionMade($this->name, $label, -1 * $amount)
);
}
}
Aggregates
<?php
abstract class Aggregate
{
public static function fromEvents($events)
{
$aggregate = new static;
foreach($events as $event) {
$aggregate->apply($event);
}
return $aggregate;
}
}
Read models
// Save current amount in SQL database
public function onTransactionMade($event)
{
$account = $this->getAccountByName($event->getAccount());
$account->update([
'amount' => $account->getAmount() + $event->getAmount(),
]);
}
Read models
// Save suspicious accounts in Redis
public function onTransactionMade($event)
{
if ($event->getAmount() > 1000) {
Redis::set('suspicious.' . $event->getAccount(), true);
}
}
En conclusion
Ne faites pas d'Event Sourcing…