291 lines
12 KiB
HTML
291 lines
12 KiB
HTML
<!doctype html>
|
||
<html>
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||
|
||
<title>L'Event Sourcing en pratique, ça donne quoi ?</title>
|
||
|
||
<link rel="stylesheet" href="css/reveal.css">
|
||
<link rel="stylesheet" href="css/theme/solarized.css">
|
||
<link rel="stylesheet" href="css/font-awesome/css/font-awesome.min.css">
|
||
|
||
<!-- Theme used for syntax highlighting of code -->
|
||
<link rel="stylesheet" href="lib/css/zenburn.css">
|
||
|
||
<!-- Printing and PDF exports -->
|
||
<script>
|
||
var link = document.createElement( 'link' );
|
||
link.rel = 'stylesheet';
|
||
link.type = 'text/css';
|
||
link.href = window.location.search.match( /print-pdf/gi ) ? 'css/print/pdf.css' : 'css/print/paper.css';
|
||
document.getElementsByTagName( 'head' )[0].appendChild( link );
|
||
</script>
|
||
|
||
<style media="screen">
|
||
div.flex-icons {
|
||
width: 60%;
|
||
margin: auto;
|
||
display: flex;
|
||
justify-content: space-around;
|
||
}
|
||
|
||
.flex-icons .fa {
|
||
font-size: 80px;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="reveal">
|
||
<div class="slides">
|
||
<section>
|
||
<h3>L'Event Sourcing en pratique, ça donne quoi ?</h3>
|
||
<h4>Thibaud Dauce</h4>
|
||
<small><a href="https://twitter.com/ThibaudDauce">@ThibaudDauce</a> | <a href="https://thibaud.dauce.fr">https://thibaud.dauce.fr</a></small>
|
||
</section>
|
||
<section>
|
||
<img src="images/quantic-telecom.png" alt="Quantic Telecom" />
|
||
<h4>Co-fondateur de Quantic Telecom</h4>
|
||
<small><a href="https://www.quantic-telecom.net">https://www.quantic-telecom.net</a></small>
|
||
<hr>
|
||
<img src="images/laravel.png" alt="Formations Laravel" />
|
||
<h4>Formations Laravel</h4>
|
||
<small><a href="https://www.formations-laravel.fr">https://www.formations-laravel.fr</a></small>
|
||
</section>
|
||
<section>
|
||
<h3>Qu'est-ce que l'Event Sourcing ?</h3>
|
||
<h4 class="fragment">Stocker l'état de l'application comme une succession d'évènements</h4>
|
||
</section>
|
||
<section>
|
||
<h3>3 concepts importants sur les évènements</h3>
|
||
|
||
<div class="flex-icons">
|
||
<i class="fa fa-database fragment" aria-hidden="true"></i>
|
||
<i class="fa fa-clock-o fragment" aria-hidden="true"></i>
|
||
<i class="fa fa-lock fragment" aria-hidden="true"></i>
|
||
</div>
|
||
</section>
|
||
<section>
|
||
<h3>Portefeuille en ligne</h3>
|
||
<h4 class="fragment" data-fragment-index="2">Que se passe-t-il si je ne suis pas d'accord ?</h4>
|
||
|
||
<div class="fragment" data-fragment-index="1">
|
||
<table style="font-size: 35px">
|
||
<tr><th>Compte</th><th>Montant</th></tr>
|
||
<tr><td>Thibaud</td><td style="text-align: right">42,00€</td></tr>
|
||
<tr><td>Jane</td><td style="text-align: right">1337,00€</td></tr>
|
||
<tr><td>John</td><td style="text-align: right">-123,00€</td></tr>
|
||
</table>
|
||
</div>
|
||
</section>
|
||
<section>
|
||
<h3>Event Sourcing à la rescousse</h3>
|
||
|
||
<table>
|
||
<tr><th>Date</th><th>Compte</th><th>Libellé</th><th>Changement</th></tr>
|
||
<tr><td>20/01/2017</td><td>Thibaud</td><td>Remboursement</td><td style="text-align: right"><strong>+</strong>20,00€</td></tr>
|
||
<tr><td>12/01/2017</td><td>Thibaud</td><td>Facture</td><td style="text-align: right"><strong>-</strong>20,00€</td></tr>
|
||
<tr><td>08/01/2017</td><td>Thibaud</td><td>Courses</td><td style="text-align: right"><strong>-</strong>58,00€</td></tr>
|
||
<tr><td>06/01/2017</td><td>Thibaud</td><td>Virement initial</td><td style="text-align: right"><strong>+</strong>100,00€</td></tr>
|
||
</table>
|
||
</section>
|
||
<section>
|
||
<h3>Avantages</h3>
|
||
<h4>Un audit complet des évènements de notre application</h4>
|
||
<h4>Les nouvelles fonctionnalités profitent des informations passées</h4>
|
||
</section>
|
||
<section>
|
||
<h3>Inconvénients</h3>
|
||
<h4>Impossible d'effectuer des requêtes simplement</h4>
|
||
<h4>Plus long et compliqué à mettre en place</h4>
|
||
</section>
|
||
<section>
|
||
<h3>En pratique</h3>
|
||
</section>
|
||
<section>
|
||
<h3>Les évènements</h3>
|
||
<pre class="fragment"><code data-trim data-noescape>
|
||
<?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;
|
||
}
|
||
}
|
||
</code></pre>
|
||
</section>
|
||
<section>
|
||
<h3>Stockage des évènements</h3>
|
||
<ul>
|
||
<li>NoSQL avec <a href="https://geteventstore.com/">EventStore</a></li>
|
||
<li>SQL avec une table par évènement</li>
|
||
<li>SQL ou NoSQL (type MongoDB) avec une table unique avec un champ JSON libre pour les données additionnelles</li>
|
||
<li>Sérialisés dans un fichier</li>
|
||
</ul>
|
||
</section>
|
||
<section>
|
||
<h3>Aggregates</h3>
|
||
<pre class="fragment"><code data-trim data-noescape>
|
||
<?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)
|
||
);
|
||
}
|
||
}
|
||
</code></pre>
|
||
</section>
|
||
<section>
|
||
<h3>Aggregates</h3>
|
||
<pre><code data-trim data-noescape>
|
||
<?php
|
||
|
||
abstract class Aggregate
|
||
{
|
||
public function raise(Event $event)
|
||
{
|
||
$event->save();
|
||
$this->apply($event);
|
||
dispatch($event);
|
||
}
|
||
}
|
||
</code></pre>
|
||
</section>
|
||
<section>
|
||
<h3>Aggregates</h3>
|
||
<pre><code data-trim data-noescape>
|
||
<?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();
|
||
}
|
||
}
|
||
}
|
||
</code></pre>
|
||
</section>
|
||
<section>
|
||
<h3>Aggregates</h3>
|
||
<pre><code data-trim data-noescape>
|
||
<?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)
|
||
);
|
||
}
|
||
}
|
||
</code></pre>
|
||
</section>
|
||
<section>
|
||
<h3>Aggregates</h3>
|
||
<pre><code data-trim data-noescape>
|
||
<?php
|
||
|
||
abstract class Aggregate
|
||
{
|
||
public static function fromEvents($events)
|
||
{
|
||
$aggregate = new static;
|
||
foreach($events as $event) {
|
||
$aggregate->apply($event);
|
||
}
|
||
|
||
return $aggregate;
|
||
}
|
||
}
|
||
</code></pre>
|
||
</section>
|
||
<section>
|
||
<h3>Read models</h3>
|
||
|
||
<pre class="fragment"><code data-trim data-noescape>
|
||
// Save current amount in SQL database
|
||
public function onTransactionMade($event)
|
||
{
|
||
$account = $this->getAccountByName($event->getAccount());
|
||
$account->update([
|
||
'amount' => $account->getAmount() + $event->getAmount(),
|
||
]);
|
||
}
|
||
</code></pre>
|
||
</section>
|
||
<section>
|
||
<h3>Read models</h3>
|
||
|
||
<pre><code data-trim data-noescape>
|
||
// Save suspicious accounts in Redis
|
||
public function onTransactionMade($event)
|
||
{
|
||
if ($event->getAmount() > 1000) {
|
||
Redis::set('suspicious.' . $event->getAccount(), true);
|
||
}
|
||
}
|
||
</code></pre>
|
||
</section>
|
||
<section>
|
||
<h3>Et en vraie pratique</h3>
|
||
</section>
|
||
<section>
|
||
<h3>En conclusion</h3>
|
||
<div class="fragment">
|
||
<h3 class="flex-icons">
|
||
<i class="fa fa-exclamation-triangle" aria-hidden="true"></i>
|
||
</h3>
|
||
<h3>Ne faites pas d'Event Sourcing…</h3>
|
||
</div>
|
||
</section>
|
||
<section>
|
||
<h3>… mais pensez y</h3>
|
||
<small><a href="https://twitter.com/ThibaudDauce">@ThibaudDauce</a> | <a href="https://thibaud.dauce.fr">https://thibaud.dauce.fr</a></small>
|
||
</section>
|
||
</div>
|
||
</div>
|
||
|
||
<script src="lib/js/head.min.js"></script>
|
||
<script src="js/reveal.js"></script>
|
||
|
||
<script>
|
||
// More info https://github.com/hakimel/reveal.js#configuration
|
||
Reveal.initialize({
|
||
history: true,
|
||
|
||
// More info https://github.com/hakimel/reveal.js#dependencies
|
||
dependencies: [
|
||
{ src: 'plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } }
|
||
]
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|