SlideShare una empresa de Scribd logo
1 de 45
Descargar para leer sin conexión
When CQRS meets Event Sourcing
A warehouse management system done in PHP
When CQRS meets Event Sourcing / Ulabox
ULABOX
About me
● @manelselles
● Backend at Ulabox
● Symfony Expert
Certified by Sensiolabs
● DDD-TDD fan
When CQRS meets Event Sourcing / Warehouse
Warehouse management system
● PHP and framework agnostic
○ (almost) all of us love Symfony
● Independent of other systems
○ Ulabox ecosystem is complex -> Microservices
● Extensible and maintainable
○ Testing
● The system must log every action
○ Event driven architecture
When CQRS meets Event Sourcing / Warehouse
Please test!
Good practices
When CQRS meets Event Sourcing / Good practices
Outside-in TDD
● Behat features
● Describe behaviour with PhpSpec
● Testing integration with database of repository methods with Phpunit
When CQRS meets Event Sourcing / Good practices
Continuous integration
When CQRS meets Event Sourcing / Good practices
Other good practices
● SOLID
● Coding Style
● Pair programming
● Refactor
DDD-Hexagonal architecture
When CQRS meets Event Sourcing / DDD-Hexagonal
DDD Basics
● Strategic
○ Ubiquitous language
○ Bounded contexts
● Tactical
○ Value objects
○ Aggregates and entities
○ Repositories
○ Domain events
○ Domain and application services
When CQRS meets Event Sourcing / DDD-Hexagonal
Aggregate
When CQRS meets Event Sourcing / DDD-Hexagonal
Hexagonal architecture
namespace UlaboxChangoInfrastructureUiHttpController;
class ReceptionController
{
public function addContainerAction(JsonApiRequest $request, $receptionId)
{
$containerPayload = $this->jsonApiTransformer->fromPayload($request->jsonData(), 'container');
$this->receptionService->addContainer(ReceptionId::fromString($receptionId), $containerPayload);
return JsonApiResponse::createJsonApiData(200, null, []);
}
}
namespace UlaboxChangoInfrastructureUiAmqpConsumer;
class ContainerAddedToReceptionConsumer extends Consumer
{
public function execute(AMQPMessage $rabbitMessage)
{
$message = $this->messageBody($rabbitMessage);
$containerPayload = $this->amqpTransformer->fromPayload($message, 'container');
$this->receptionService->addContainer(ReceptionId::fromString($message['reception_id']), $containerPayload);
return ConsumerInterface::MSG_ACK;
}
}
namespace UlaboxChangoApplicationService;
class ReceptionService
{
public function addContainer(ReceptionId $receptionId, ContainerPayload $payload)
{
$reception = $this->receptionRepository->get($receptionId);
$reception->addContainer($payload->temperature(), $payload->lines());
$this->receptionRepository->save($reception);
$this->eventBus->dispatch($reception->recordedEvents());
}
}
When CQRS meets Event Sourcing / DDD-Hexagonal
Why application service?
● Same entry point
● Coordinate tasks on model
● Early checks
● User authentication
namespace UlaboxChangoDomainModelReception;
class Reception extends Aggregate
{
public function addContainer(Temperature $temperature, array $containerLines)
{
Assertion::allIsInstanceOf($containerLines, ContainerLinePayload::class);
$containerId = ContainerId::create($this->id(), $temperature, count($this->containers));
$this->containers->set((string) $containerId, new Container($containerId, $temperature));
$this->recordThat(new ContainerWasAdded($this->id, $containerId, $temperature));
foreach ($containerLines as $line) {
$this->addLine($containerId, $line->label(), $line->quantity(), $line->type());
}
}
public function addLine(ContainerId $containerId, Label $label, LineQuantity $quantity, ItemType $type)
{
if (!$container = $this->containers->get((string) $containerId)) {
throw new EntityNotFoundException("Container not found");
}
$container->addLine(ContainerLine::create($label, $quantity, $type));
$this->recordThat(new ContainerLineWasAdded($this->id, $containerId, $label, $quantity, $type));
}
}
namespace UlaboxChangoDomainModelReceptionContainer;
class Container
{
public function __construct(ContainerId $id, Temperature $temperature)
{
$this->id = $id;
$this->temperature = $temperature;
$this->lines = new ArrayCollection();
$this->status = ContainerStatus::PENDING();
}
public function addLine(ContainerLine $line)
{
if ($this->containsLine($line->label())) {
throw new AlreadyRegisteredException("Line already exists");
}
$this->lines->set((string) $line->label(), $line);
}
}
namespace UlaboxChangoInfrastructurePersistenceDoctrineReception;
class DoctrineReceptionRepository implements ReceptionRepository
{
public function get(ReceptionId $id)
{
return $this->find($id);
}
public function save(Reception $reception)
{
$this->_em->persist($reception);
}
}
Let’s apply Command and Query
Responsibility Segregation
When CQRS meets Event Sourcing / CQRS
CQRS
Separate:
● Command: do something
● Query: ask for something
Different source of data for read and write:
● Write model with DDD tactical patterns
● Read model with listeners to events
When CQRS meets Event Sourcing / CQRS
Command bus
● Finds handler for each action
● Decoupled command creator and handler
● Middlewares
○ Transactional
○ Logging
● Asynchronous actions
● Separation of concerns
When CQRS meets Event Sourcing / CQRS
Event bus
● Posted events are delivered to matching event handlers
● Decouples event producers and reactors
● Middlewares
○ Rabbit
○ Add correlation id
● Asynchronous actions
● Separation of concerns
When CQRS meets Event Sourcing / CQRS
namespace UlaboxChangoApplicationService;
class ReceptionService
{
public function addContainer(ReceptionId $receptionId, ContainerPayload $payload)
{
$command = new AddContainer($receptionId, $payload->temperature(), $payload->containerLines());
$this->commandBus->handle($command);
}
}
namespace UlaboxChangoDomainCommandReception;
class ReceptionCommandHandler extends CommandHandler
{
public function handleAddContainer(AddContainer $command)
{
$reception = $this->receptionRepository->get($command->aggregateId());
$reception->addContainer($command->temperature(), $command->lines());
$this->receptionRepository->save($reception);
$this->eventBus->dispatch($reception->recordedEvents());
}
}
namespace UlaboxChangoDomainReadModelReception;
class ReceptionProjector extends ReadModelProcessor
{
public function applyContainerWasAdded(ContainerWasAdded $event)
{
$reception = $this->receptionInfoView->receptionOfId($event->aggregateId());
$container = new ContainerProjection($event->containerId(), $event->temperature());
$this->receptionInfoView->save($reception->addContainer($container));
}
public function applyContainerLineWasAdded(ContainerLineWasAdded $event)
{
$reception = $this->receptionInfoView->receptionOfId($event->aggregateId());
$line = ContainerLineProjection($event->label(), $event->quantity(), $event->itemType());
$this->receptionInfoView->save($reception->addContainerLine($event->containerId(), $line));
}
}
namespace UlaboxChangoDomainReadModelReception;
interface ReceptionView
{
public function save(ReceptionProjection $reception);
public function receptionOfId(ReceptionId $receptionId);
public function find(Query $query);
}
namespace UlaboxChangoApplicationService;
class ReceptionQueryService
{
public function byId(ReceptionId $receptionId)
{
return $this->receptionView->receptionOfId($receptionId);
}
public function byContainer(ContainerId $containerId)
{
return $this->receptionView->find(new byContainer($containerId));
}
public function search($filters, Paging $paging = null, Sorting $sorting = null)
{
return $this->receptionView->find(new ByFilters($filters, $sorting, $paging));
}
}
Let’s get crazy: event sourcing
When CQRS meets Event Sourcing / Event sourcing
Event sourcing
● Entities are reconstructed with events
● No state
● No database to update manually
● No joins
When CQRS meets Event Sourcing / Event sourcing
Why event sourcing?
● Get state of an aggregate at any moment in time
● Append-only model storing events is easier to scale
● Forces to log because everything is an event
● No coupling between current state in the domain and in storage
● Simulate business suppositions
○ Change picking algorithm
When CQRS meets Event Sourcing / Event sourcing
Event Store
● PostgreSQL
● jsonb
● DBAL
namespace UlaboxChangoInfrastructurePersistenceEventStore;
class PDOEventStore implements EventStore
{
public function append(AggregateId $id, EventStream $eventStream)
{
$stmt = $this->connection->prepare("INSERT INTO event_store (data) VALUES (:message)");
$this->connection->beginTransaction();
foreach ($eventStream as $event) {
if (!$stmt->execute(['message' => $this->eventSerializer->serialize($event)])) {
$this->connection->rollBack();
}
}
$this->connection->commit();
}
public function load(AggregateId $id)
{
$stmt = $this->connection->prepare("SELECT data FROM event_store WHERE data->'payload'->>'aggregate_id' = :id");
$stmt->execute(['id' => (string) $id]);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
$events = [];
foreach ($rows as $row) {
$events[] = $this->eventSerializer->deserialize($row['data']);
}
return new EventStream($events);
}
}
When CQRS meets Event Sourcing / Event sourcing
namespace UlaboxChangoInfrastructurePersistenceModelReception;
class EventSourcingReceptionRepository implements ReceptionRepository
{
public function save(Reception $reception)
{
$events = $reception->recordedEvents();
$this->eventStore->append($reception->id(), $events);
foreach ($events as $event) {
$this->eventBus->dispatch($event);
}
}
public function load(ReceptionId $id)
{
$eventStream = $this->eventStore->load($id);
return Reception::reconstituteFromEvents(
new AggregateHistory($id, $eventStream)
);
}
}
namespace UlaboxChangoDomainModelReception;
class Reception extends EventSourcedAggregate
{
public static function create(
ReceptionId $id, DateTime $receptionDate, SupplierId $supplierId
) {
$instance = new self($id);
$instance->recordThat(
new ReceptionWasScheduled($id, $receptionDate, $supplierId)
);
return $instance;
}
protected function applyReceptionWasScheduled(ReceptionWasScheduled $event)
{
$this->receptionDate = $event->receptionDate();
$this->supplierId = $event->supplierId();
$this->status = ReceptionStatus::PENDING();
$this->containers = new ArrayCollection();
}
}
namespace UlaboxChangoDomainModel;
abstract class EventSourcedAggregate implements AggregateRoot, EventRecorder
{
protected function __construct()
{
$this->version = 0;
$this->eventStream = new EventStream();
}
protected function recordThat(Event $event)
{
$this->apply($event);
$this->eventStream->append($event);
}
protected function apply(Event $event)
{
$classParts = explode('', get_class($event));
$methodName = 'apply'.end($classParts);
if (method_exists($this, $methodName)) {
$this->$methodName($event);
}
$this->version++;
}
}
namespace UlaboxChangoDomainModelReception;
class Reception extends EventSourcedAggregate
{
public static function reconstituteFromEvents(AggregateHistory $history)
{
$instance = new self($history->aggregateId());
foreach ($history->events() as $event) {
$instance->apply($event);
}
return $instance;
}
public function addContainer(Temperature $temperature, array $containerLines)
{
$containerId = ContainerId::create(
$this->id(), $temperature, count($this->containers)
);
$this->recordThat(
new ContainerWasAdded($this->id, $containerId, $temperature)
);
foreach ($containerLines as $line) {
$this->addLine(
$containerId, $line->label(), $line->quantity(), $line->type()
);
}
}
protected function applyContainerWasAdded(ContainerWasAdded $event)
{
$container = new Container($event->containerId(), $event->temperature());
$this->containers->set((string) $event->containerId(), $container);
}
}
Conclusions
When CQRS meets Event Sourcing / Conclusions
Benefits
● Decoupling
● Performance in Read Model
● Scalability
● No joins
● Async with internal events and consumers
● Communicate other bounded contexts with events
When CQRS meets Event Sourcing / Conclusions
Problems found
● With DDD
○ Decide aggregates => talk a LOT with the domain experts
○ Boilerplate => generate as much boilerplate as possible
● With CQRS
○ Forgetting listeners in read model
○ Repeated code structure
● With event sourcing
○ Adapting your mindset
○ Forgetting applying the event to the entity
○ Retro compatibility with old events
● Concurrency/eventual consistency
Work with us!
When CQRS meets Event Sourcing / Work with us
Work with us
Thanks to...
When CQRS meets Event Sourcing / Conclusions
Thank you!
Questions?
@manelselles
manelselles@gmail.com

Más contenido relacionado

La actualidad más candente

Database Design Patterns
Database Design PatternsDatabase Design Patterns
Database Design PatternsHugo Hamon
 
Design Patterns avec PHP 5.3, Symfony et Pimple
Design Patterns avec PHP 5.3, Symfony et PimpleDesign Patterns avec PHP 5.3, Symfony et Pimple
Design Patterns avec PHP 5.3, Symfony et PimpleHugo Hamon
 
Forget about index.php and build you applications around HTTP!
Forget about index.php and build you applications around HTTP!Forget about index.php and build you applications around HTTP!
Forget about index.php and build you applications around HTTP!Kacper Gunia
 
The History of PHPersistence
The History of PHPersistenceThe History of PHPersistence
The History of PHPersistenceHugo Hamon
 
Symfony2, creare bundle e valore per il cliente
Symfony2, creare bundle e valore per il clienteSymfony2, creare bundle e valore per il cliente
Symfony2, creare bundle e valore per il clienteLeonardo Proietti
 
Models and Service Layers, Hemoglobin and Hobgoblins
Models and Service Layers, Hemoglobin and HobgoblinsModels and Service Layers, Hemoglobin and Hobgoblins
Models and Service Layers, Hemoglobin and HobgoblinsRoss Tuck
 
Decoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DICDecoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DICKonstantin Kudryashov
 
CQRS and Event Sourcing in a Symfony application
CQRS and Event Sourcing in a Symfony applicationCQRS and Event Sourcing in a Symfony application
CQRS and Event Sourcing in a Symfony applicationSamuel ROZE
 
Doctrine fixtures
Doctrine fixturesDoctrine fixtures
Doctrine fixturesBill Chang
 
Design how your objects talk through mocking
Design how your objects talk through mockingDesign how your objects talk through mocking
Design how your objects talk through mockingKonstantin Kudryashov
 
Command Bus To Awesome Town
Command Bus To Awesome TownCommand Bus To Awesome Town
Command Bus To Awesome TownRoss Tuck
 
Introduction to Zend Framework web services
Introduction to Zend Framework web servicesIntroduction to Zend Framework web services
Introduction to Zend Framework web servicesMichelangelo van Dam
 
Adding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy ApplicationsAdding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy ApplicationsSam Hennessy
 
Min-Maxing Software Costs - Laracon EU 2015
Min-Maxing Software Costs - Laracon EU 2015Min-Maxing Software Costs - Laracon EU 2015
Min-Maxing Software Costs - Laracon EU 2015Konstantin Kudryashov
 
Things I Believe Now That I'm Old
Things I Believe Now That I'm OldThings I Believe Now That I'm Old
Things I Believe Now That I'm OldRoss Tuck
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For BeginnersJonathan Wage
 
Symfony World - Symfony components and design patterns
Symfony World - Symfony components and design patternsSymfony World - Symfony components and design patterns
Symfony World - Symfony components and design patternsŁukasz Chruściel
 
CQRS & Event Sourcing in the wild (ScotlandPHP 2016)
CQRS & Event Sourcing in the wild (ScotlandPHP 2016)CQRS & Event Sourcing in the wild (ScotlandPHP 2016)
CQRS & Event Sourcing in the wild (ScotlandPHP 2016)Michiel Rook
 

La actualidad más candente (20)

Database Design Patterns
Database Design PatternsDatabase Design Patterns
Database Design Patterns
 
Design Patterns avec PHP 5.3, Symfony et Pimple
Design Patterns avec PHP 5.3, Symfony et PimpleDesign Patterns avec PHP 5.3, Symfony et Pimple
Design Patterns avec PHP 5.3, Symfony et Pimple
 
Forget about index.php and build you applications around HTTP!
Forget about index.php and build you applications around HTTP!Forget about index.php and build you applications around HTTP!
Forget about index.php and build you applications around HTTP!
 
The History of PHPersistence
The History of PHPersistenceThe History of PHPersistence
The History of PHPersistence
 
Symfony2, creare bundle e valore per il cliente
Symfony2, creare bundle e valore per il clienteSymfony2, creare bundle e valore per il cliente
Symfony2, creare bundle e valore per il cliente
 
Models and Service Layers, Hemoglobin and Hobgoblins
Models and Service Layers, Hemoglobin and HobgoblinsModels and Service Layers, Hemoglobin and Hobgoblins
Models and Service Layers, Hemoglobin and Hobgoblins
 
Min-Maxing Software Costs
Min-Maxing Software CostsMin-Maxing Software Costs
Min-Maxing Software Costs
 
Decoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DICDecoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DIC
 
CQRS and Event Sourcing in a Symfony application
CQRS and Event Sourcing in a Symfony applicationCQRS and Event Sourcing in a Symfony application
CQRS and Event Sourcing in a Symfony application
 
Doctrine fixtures
Doctrine fixturesDoctrine fixtures
Doctrine fixtures
 
Design how your objects talk through mocking
Design how your objects talk through mockingDesign how your objects talk through mocking
Design how your objects talk through mocking
 
Command Bus To Awesome Town
Command Bus To Awesome TownCommand Bus To Awesome Town
Command Bus To Awesome Town
 
Introduction to Zend Framework web services
Introduction to Zend Framework web servicesIntroduction to Zend Framework web services
Introduction to Zend Framework web services
 
Adding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy ApplicationsAdding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy Applications
 
Zend framework service
Zend framework serviceZend framework service
Zend framework service
 
Min-Maxing Software Costs - Laracon EU 2015
Min-Maxing Software Costs - Laracon EU 2015Min-Maxing Software Costs - Laracon EU 2015
Min-Maxing Software Costs - Laracon EU 2015
 
Things I Believe Now That I'm Old
Things I Believe Now That I'm OldThings I Believe Now That I'm Old
Things I Believe Now That I'm Old
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For Beginners
 
Symfony World - Symfony components and design patterns
Symfony World - Symfony components and design patternsSymfony World - Symfony components and design patterns
Symfony World - Symfony components and design patterns
 
CQRS & Event Sourcing in the wild (ScotlandPHP 2016)
CQRS & Event Sourcing in the wild (ScotlandPHP 2016)CQRS & Event Sourcing in the wild (ScotlandPHP 2016)
CQRS & Event Sourcing in the wild (ScotlandPHP 2016)
 

Destacado

Refactorizando Pccomponentes.com con Symfony
Refactorizando Pccomponentes.com con SymfonyRefactorizando Pccomponentes.com con Symfony
Refactorizando Pccomponentes.com con SymfonyMario Marín
 
Introduction to hexagonal architecture
Introduction to hexagonal architectureIntroduction to hexagonal architecture
Introduction to hexagonal architectureManel Sellés
 
Introduction to testing
Introduction to testingIntroduction to testing
Introduction to testingManel Sellés
 
Symfony 2 : Performances et Optimisations
Symfony 2 : Performances et OptimisationsSymfony 2 : Performances et Optimisations
Symfony 2 : Performances et OptimisationsLes-Tilleuls.coop
 
CQRS & event sourcing in the wild
CQRS & event sourcing in the wildCQRS & event sourcing in the wild
CQRS & event sourcing in the wildMichiel Rook
 
Integrando React.js en aplicaciones Symfony (deSymfony 2016)
Integrando React.js en aplicaciones Symfony (deSymfony 2016)Integrando React.js en aplicaciones Symfony (deSymfony 2016)
Integrando React.js en aplicaciones Symfony (deSymfony 2016)Ignacio Martín
 
A year with event sourcing and CQRS
A year with event sourcing and CQRSA year with event sourcing and CQRS
A year with event sourcing and CQRSSteve Pember
 
CQRS + Event Sourcing
CQRS + Event SourcingCQRS + Event Sourcing
CQRS + Event SourcingMike Bild
 
Microservice Architecture with CQRS and Event Sourcing
Microservice Architecture with CQRS and Event SourcingMicroservice Architecture with CQRS and Event Sourcing
Microservice Architecture with CQRS and Event SourcingBen Wilcock
 
Developing event-driven microservices with event sourcing and CQRS (svcc, sv...
Developing event-driven microservices with event sourcing and CQRS  (svcc, sv...Developing event-driven microservices with event sourcing and CQRS  (svcc, sv...
Developing event-driven microservices with event sourcing and CQRS (svcc, sv...Chris Richardson
 
Learning from the Ulabox stack
Learning from the Ulabox stackLearning from the Ulabox stack
Learning from the Ulabox stackRubén Sospedra
 
Cqrs from the trenches
Cqrs from the trenchesCqrs from the trenches
Cqrs from the trenchesnextbuild
 
Greg young’s simple cqrs sample
Greg young’s simple cqrs sampleGreg young’s simple cqrs sample
Greg young’s simple cqrs sampleLeonardo Rosales
 
From framework coupled code to #microservices through #DDD /by @codelytv
From framework coupled code to #microservices through #DDD /by @codelytvFrom framework coupled code to #microservices through #DDD /by @codelytv
From framework coupled code to #microservices through #DDD /by @codelytvCodelyTV
 
JPHP - О проекте на простом языке
JPHP - О проекте на простом языкеJPHP - О проекте на простом языке
JPHP - О проекте на простом языкеDmitry Zaytsev
 
Hexagonal architecture - message-oriented software design (PHP Barcelona 2015)
Hexagonal architecture - message-oriented software design (PHP Barcelona 2015)Hexagonal architecture - message-oriented software design (PHP Barcelona 2015)
Hexagonal architecture - message-oriented software design (PHP Barcelona 2015)Matthias Noback
 
Webinar - What's new in Axon 3
Webinar - What's new in Axon 3 Webinar - What's new in Axon 3
Webinar - What's new in Axon 3 Allard Buijze
 

Destacado (20)

Refactorizando Pccomponentes.com con Symfony
Refactorizando Pccomponentes.com con SymfonyRefactorizando Pccomponentes.com con Symfony
Refactorizando Pccomponentes.com con Symfony
 
Introduction to hexagonal architecture
Introduction to hexagonal architectureIntroduction to hexagonal architecture
Introduction to hexagonal architecture
 
Introduction to testing
Introduction to testingIntroduction to testing
Introduction to testing
 
Symfony 2 : Performances et Optimisations
Symfony 2 : Performances et OptimisationsSymfony 2 : Performances et Optimisations
Symfony 2 : Performances et Optimisations
 
CQRS & event sourcing in the wild
CQRS & event sourcing in the wildCQRS & event sourcing in the wild
CQRS & event sourcing in the wild
 
Integrando React.js en aplicaciones Symfony (deSymfony 2016)
Integrando React.js en aplicaciones Symfony (deSymfony 2016)Integrando React.js en aplicaciones Symfony (deSymfony 2016)
Integrando React.js en aplicaciones Symfony (deSymfony 2016)
 
A year with event sourcing and CQRS
A year with event sourcing and CQRSA year with event sourcing and CQRS
A year with event sourcing and CQRS
 
CQRS + Event Sourcing
CQRS + Event SourcingCQRS + Event Sourcing
CQRS + Event Sourcing
 
Microservice Architecture with CQRS and Event Sourcing
Microservice Architecture with CQRS and Event SourcingMicroservice Architecture with CQRS and Event Sourcing
Microservice Architecture with CQRS and Event Sourcing
 
Developing event-driven microservices with event sourcing and CQRS (svcc, sv...
Developing event-driven microservices with event sourcing and CQRS  (svcc, sv...Developing event-driven microservices with event sourcing and CQRS  (svcc, sv...
Developing event-driven microservices with event sourcing and CQRS (svcc, sv...
 
Learning from the Ulabox stack
Learning from the Ulabox stackLearning from the Ulabox stack
Learning from the Ulabox stack
 
Microservices
MicroservicesMicroservices
Microservices
 
Cqrs from the trenches
Cqrs from the trenchesCqrs from the trenches
Cqrs from the trenches
 
Greg young’s simple cqrs sample
Greg young’s simple cqrs sampleGreg young’s simple cqrs sample
Greg young’s simple cqrs sample
 
From framework coupled code to #microservices through #DDD /by @codelytv
From framework coupled code to #microservices through #DDD /by @codelytvFrom framework coupled code to #microservices through #DDD /by @codelytv
From framework coupled code to #microservices through #DDD /by @codelytv
 
JPHP - О проекте на простом языке
JPHP - О проекте на простом языкеJPHP - О проекте на простом языке
JPHP - О проекте на простом языке
 
Hexagonal architecture - message-oriented software design (PHP Barcelona 2015)
Hexagonal architecture - message-oriented software design (PHP Barcelona 2015)Hexagonal architecture - message-oriented software design (PHP Barcelona 2015)
Hexagonal architecture - message-oriented software design (PHP Barcelona 2015)
 
Matters of State
Matters of StateMatters of State
Matters of State
 
Webinar - What's new in Axon 3
Webinar - What's new in Axon 3 Webinar - What's new in Axon 3
Webinar - What's new in Axon 3
 
DDD 준비 서문래
DDD 준비 서문래DDD 준비 서문래
DDD 준비 서문래
 

Similar a When cqrs meets event sourcing

Why is crud a bad idea - focus on real scenarios
Why is crud a bad idea - focus on real scenariosWhy is crud a bad idea - focus on real scenarios
Why is crud a bad idea - focus on real scenariosDivante
 
Modularity and Layered Data Model
Modularity and Layered Data ModelModularity and Layered Data Model
Modularity and Layered Data ModelAttila Jenei
 
Singletons in PHP - Why they are bad and how you can eliminate them from your...
Singletons in PHP - Why they are bad and how you can eliminate them from your...Singletons in PHP - Why they are bad and how you can eliminate them from your...
Singletons in PHP - Why they are bad and how you can eliminate them from your...go_oh
 
How Kris Writes Symfony Apps
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony AppsKris Wallsmith
 
Building Lithium Apps
Building Lithium AppsBuilding Lithium Apps
Building Lithium AppsNate Abele
 
#pugMi - DDD - Value objects
#pugMi - DDD - Value objects#pugMi - DDD - Value objects
#pugMi - DDD - Value objectsSimone Gentili
 
Drupal 8 Services And Dependency Injection
Drupal 8 Services And Dependency InjectionDrupal 8 Services And Dependency Injection
Drupal 8 Services And Dependency InjectionPhilip Norton
 
Practical Event Sourcing
Practical Event SourcingPractical Event Sourcing
Practical Event SourcingMathias Verraes
 
Understanding backbonejs
Understanding backbonejsUnderstanding backbonejs
Understanding backbonejsNick Lee
 
The command dispatcher pattern
The command dispatcher patternThe command dispatcher pattern
The command dispatcher patternolvlvl
 
Lazy evaluation drupal camp moscow 2014
Lazy evaluation drupal camp moscow 2014Lazy evaluation drupal camp moscow 2014
Lazy evaluation drupal camp moscow 2014Evgeny Nikitin
 
Ioc container | Hannes Van De Vreken | CODEiD
Ioc container | Hannes Van De Vreken | CODEiDIoc container | Hannes Van De Vreken | CODEiD
Ioc container | Hannes Van De Vreken | CODEiDCODEiD PHP Community
 
LJC Conference 2014 Cassandra for Java Developers
LJC Conference 2014 Cassandra for Java DevelopersLJC Conference 2014 Cassandra for Java Developers
LJC Conference 2014 Cassandra for Java DevelopersChristopher Batey
 
Zendcon 2007 Api Design
Zendcon 2007 Api DesignZendcon 2007 Api Design
Zendcon 2007 Api Designunodelostrece
 
Advanced symfony Techniques
Advanced symfony TechniquesAdvanced symfony Techniques
Advanced symfony TechniquesKris Wallsmith
 
Intro to advanced caching in WordPress
Intro to advanced caching in WordPressIntro to advanced caching in WordPress
Intro to advanced caching in WordPressMaor Chasen
 

Similar a When cqrs meets event sourcing (20)

Why is crud a bad idea - focus on real scenarios
Why is crud a bad idea - focus on real scenariosWhy is crud a bad idea - focus on real scenarios
Why is crud a bad idea - focus on real scenarios
 
Modularity and Layered Data Model
Modularity and Layered Data ModelModularity and Layered Data Model
Modularity and Layered Data Model
 
Singletons in PHP - Why they are bad and how you can eliminate them from your...
Singletons in PHP - Why they are bad and how you can eliminate them from your...Singletons in PHP - Why they are bad and how you can eliminate them from your...
Singletons in PHP - Why they are bad and how you can eliminate them from your...
 
How Kris Writes Symfony Apps
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony Apps
 
Building Lithium Apps
Building Lithium AppsBuilding Lithium Apps
Building Lithium Apps
 
#pugMi - DDD - Value objects
#pugMi - DDD - Value objects#pugMi - DDD - Value objects
#pugMi - DDD - Value objects
 
Drupal 8 Services And Dependency Injection
Drupal 8 Services And Dependency InjectionDrupal 8 Services And Dependency Injection
Drupal 8 Services And Dependency Injection
 
Practical Event Sourcing
Practical Event SourcingPractical Event Sourcing
Practical Event Sourcing
 
Understanding backbonejs
Understanding backbonejsUnderstanding backbonejs
Understanding backbonejs
 
My Development Story
My Development StoryMy Development Story
My Development Story
 
The command dispatcher pattern
The command dispatcher patternThe command dispatcher pattern
The command dispatcher pattern
 
Lazy evaluation drupal camp moscow 2014
Lazy evaluation drupal camp moscow 2014Lazy evaluation drupal camp moscow 2014
Lazy evaluation drupal camp moscow 2014
 
Design Patterns
Design PatternsDesign Patterns
Design Patterns
 
Ioc container | Hannes Van De Vreken | CODEiD
Ioc container | Hannes Van De Vreken | CODEiDIoc container | Hannes Van De Vreken | CODEiD
Ioc container | Hannes Van De Vreken | CODEiD
 
LJC Conference 2014 Cassandra for Java Developers
LJC Conference 2014 Cassandra for Java DevelopersLJC Conference 2014 Cassandra for Java Developers
LJC Conference 2014 Cassandra for Java Developers
 
Zendcon 2007 Api Design
Zendcon 2007 Api DesignZendcon 2007 Api Design
Zendcon 2007 Api Design
 
Advanced symfony Techniques
Advanced symfony TechniquesAdvanced symfony Techniques
Advanced symfony Techniques
 
Custom entities in d8
Custom entities in d8Custom entities in d8
Custom entities in d8
 
Event Sourcing with php
Event Sourcing with phpEvent Sourcing with php
Event Sourcing with php
 
Intro to advanced caching in WordPress
Intro to advanced caching in WordPressIntro to advanced caching in WordPress
Intro to advanced caching in WordPress
 

Último

OpenChain AI Study Group - Europe and Asia Recap - 2024-04-11 - Full Recording
OpenChain AI Study Group - Europe and Asia Recap - 2024-04-11 - Full RecordingOpenChain AI Study Group - Europe and Asia Recap - 2024-04-11 - Full Recording
OpenChain AI Study Group - Europe and Asia Recap - 2024-04-11 - Full RecordingShane Coughlan
 
Post Quantum Cryptography – The Impact on Identity
Post Quantum Cryptography – The Impact on IdentityPost Quantum Cryptography – The Impact on Identity
Post Quantum Cryptography – The Impact on Identityteam-WIBU
 
A healthy diet for your Java application Devoxx France.pdf
A healthy diet for your Java application Devoxx France.pdfA healthy diet for your Java application Devoxx France.pdf
A healthy diet for your Java application Devoxx France.pdfMarharyta Nedzelska
 
Understanding Flamingo - DeepMind's VLM Architecture
Understanding Flamingo - DeepMind's VLM ArchitectureUnderstanding Flamingo - DeepMind's VLM Architecture
Understanding Flamingo - DeepMind's VLM Architecturerahul_net
 
SoftTeco - Software Development Company Profile
SoftTeco - Software Development Company ProfileSoftTeco - Software Development Company Profile
SoftTeco - Software Development Company Profileakrivarotava
 
VictoriaMetrics Q1 Meet Up '24 - Community & News Update
VictoriaMetrics Q1 Meet Up '24 - Community & News UpdateVictoriaMetrics Q1 Meet Up '24 - Community & News Update
VictoriaMetrics Q1 Meet Up '24 - Community & News UpdateVictoriaMetrics
 
Introduction to Firebase Workshop Slides
Introduction to Firebase Workshop SlidesIntroduction to Firebase Workshop Slides
Introduction to Firebase Workshop Slidesvaideheekore1
 
How to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationHow to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationBradBedford3
 
Tech Tuesday Slides - Introduction to Project Management with OnePlan's Work ...
Tech Tuesday Slides - Introduction to Project Management with OnePlan's Work ...Tech Tuesday Slides - Introduction to Project Management with OnePlan's Work ...
Tech Tuesday Slides - Introduction to Project Management with OnePlan's Work ...OnePlan Solutions
 
SpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at RuntimeSpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at Runtimeandrehoraa
 
Best Angular 17 Classroom & Online training - Naresh IT
Best Angular 17 Classroom & Online training - Naresh ITBest Angular 17 Classroom & Online training - Naresh IT
Best Angular 17 Classroom & Online training - Naresh ITmanoharjgpsolutions
 
2024 DevNexus Patterns for Resiliency: Shuffle shards
2024 DevNexus Patterns for Resiliency: Shuffle shards2024 DevNexus Patterns for Resiliency: Shuffle shards
2024 DevNexus Patterns for Resiliency: Shuffle shardsChristopher Curtin
 
Leveraging AI for Mobile App Testing on Real Devices | Applitools + Kobiton
Leveraging AI for Mobile App Testing on Real Devices | Applitools + KobitonLeveraging AI for Mobile App Testing on Real Devices | Applitools + Kobiton
Leveraging AI for Mobile App Testing on Real Devices | Applitools + KobitonApplitools
 
Salesforce Implementation Services PPT By ABSYZ
Salesforce Implementation Services PPT By ABSYZSalesforce Implementation Services PPT By ABSYZ
Salesforce Implementation Services PPT By ABSYZABSYZ Inc
 
Exploring Selenium_Appium Frameworks for Seamless Integration with HeadSpin.pdf
Exploring Selenium_Appium Frameworks for Seamless Integration with HeadSpin.pdfExploring Selenium_Appium Frameworks for Seamless Integration with HeadSpin.pdf
Exploring Selenium_Appium Frameworks for Seamless Integration with HeadSpin.pdfkalichargn70th171
 
Precise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive GoalPrecise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive GoalLionel Briand
 
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...confluent
 
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...OnePlan Solutions
 
Machine Learning Software Engineering Patterns and Their Engineering
Machine Learning Software Engineering Patterns and Their EngineeringMachine Learning Software Engineering Patterns and Their Engineering
Machine Learning Software Engineering Patterns and Their EngineeringHironori Washizaki
 
Patterns for automating API delivery. API conference
Patterns for automating API delivery. API conferencePatterns for automating API delivery. API conference
Patterns for automating API delivery. API conferencessuser9e7c64
 

Último (20)

OpenChain AI Study Group - Europe and Asia Recap - 2024-04-11 - Full Recording
OpenChain AI Study Group - Europe and Asia Recap - 2024-04-11 - Full RecordingOpenChain AI Study Group - Europe and Asia Recap - 2024-04-11 - Full Recording
OpenChain AI Study Group - Europe and Asia Recap - 2024-04-11 - Full Recording
 
Post Quantum Cryptography – The Impact on Identity
Post Quantum Cryptography – The Impact on IdentityPost Quantum Cryptography – The Impact on Identity
Post Quantum Cryptography – The Impact on Identity
 
A healthy diet for your Java application Devoxx France.pdf
A healthy diet for your Java application Devoxx France.pdfA healthy diet for your Java application Devoxx France.pdf
A healthy diet for your Java application Devoxx France.pdf
 
Understanding Flamingo - DeepMind's VLM Architecture
Understanding Flamingo - DeepMind's VLM ArchitectureUnderstanding Flamingo - DeepMind's VLM Architecture
Understanding Flamingo - DeepMind's VLM Architecture
 
SoftTeco - Software Development Company Profile
SoftTeco - Software Development Company ProfileSoftTeco - Software Development Company Profile
SoftTeco - Software Development Company Profile
 
VictoriaMetrics Q1 Meet Up '24 - Community & News Update
VictoriaMetrics Q1 Meet Up '24 - Community & News UpdateVictoriaMetrics Q1 Meet Up '24 - Community & News Update
VictoriaMetrics Q1 Meet Up '24 - Community & News Update
 
Introduction to Firebase Workshop Slides
Introduction to Firebase Workshop SlidesIntroduction to Firebase Workshop Slides
Introduction to Firebase Workshop Slides
 
How to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationHow to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion Application
 
Tech Tuesday Slides - Introduction to Project Management with OnePlan's Work ...
Tech Tuesday Slides - Introduction to Project Management with OnePlan's Work ...Tech Tuesday Slides - Introduction to Project Management with OnePlan's Work ...
Tech Tuesday Slides - Introduction to Project Management with OnePlan's Work ...
 
SpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at RuntimeSpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at Runtime
 
Best Angular 17 Classroom & Online training - Naresh IT
Best Angular 17 Classroom & Online training - Naresh ITBest Angular 17 Classroom & Online training - Naresh IT
Best Angular 17 Classroom & Online training - Naresh IT
 
2024 DevNexus Patterns for Resiliency: Shuffle shards
2024 DevNexus Patterns for Resiliency: Shuffle shards2024 DevNexus Patterns for Resiliency: Shuffle shards
2024 DevNexus Patterns for Resiliency: Shuffle shards
 
Leveraging AI for Mobile App Testing on Real Devices | Applitools + Kobiton
Leveraging AI for Mobile App Testing on Real Devices | Applitools + KobitonLeveraging AI for Mobile App Testing on Real Devices | Applitools + Kobiton
Leveraging AI for Mobile App Testing on Real Devices | Applitools + Kobiton
 
Salesforce Implementation Services PPT By ABSYZ
Salesforce Implementation Services PPT By ABSYZSalesforce Implementation Services PPT By ABSYZ
Salesforce Implementation Services PPT By ABSYZ
 
Exploring Selenium_Appium Frameworks for Seamless Integration with HeadSpin.pdf
Exploring Selenium_Appium Frameworks for Seamless Integration with HeadSpin.pdfExploring Selenium_Appium Frameworks for Seamless Integration with HeadSpin.pdf
Exploring Selenium_Appium Frameworks for Seamless Integration with HeadSpin.pdf
 
Precise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive GoalPrecise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive Goal
 
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
 
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
 
Machine Learning Software Engineering Patterns and Their Engineering
Machine Learning Software Engineering Patterns and Their EngineeringMachine Learning Software Engineering Patterns and Their Engineering
Machine Learning Software Engineering Patterns and Their Engineering
 
Patterns for automating API delivery. API conference
Patterns for automating API delivery. API conferencePatterns for automating API delivery. API conference
Patterns for automating API delivery. API conference
 

When cqrs meets event sourcing

  • 1. When CQRS meets Event Sourcing A warehouse management system done in PHP
  • 2. When CQRS meets Event Sourcing / Ulabox ULABOX
  • 3. About me ● @manelselles ● Backend at Ulabox ● Symfony Expert Certified by Sensiolabs ● DDD-TDD fan
  • 4. When CQRS meets Event Sourcing / Warehouse Warehouse management system ● PHP and framework agnostic ○ (almost) all of us love Symfony ● Independent of other systems ○ Ulabox ecosystem is complex -> Microservices ● Extensible and maintainable ○ Testing ● The system must log every action ○ Event driven architecture
  • 5. When CQRS meets Event Sourcing / Warehouse
  • 7. When CQRS meets Event Sourcing / Good practices Outside-in TDD ● Behat features ● Describe behaviour with PhpSpec ● Testing integration with database of repository methods with Phpunit
  • 8. When CQRS meets Event Sourcing / Good practices Continuous integration
  • 9. When CQRS meets Event Sourcing / Good practices Other good practices ● SOLID ● Coding Style ● Pair programming ● Refactor
  • 11. When CQRS meets Event Sourcing / DDD-Hexagonal DDD Basics ● Strategic ○ Ubiquitous language ○ Bounded contexts ● Tactical ○ Value objects ○ Aggregates and entities ○ Repositories ○ Domain events ○ Domain and application services
  • 12. When CQRS meets Event Sourcing / DDD-Hexagonal Aggregate
  • 13. When CQRS meets Event Sourcing / DDD-Hexagonal Hexagonal architecture
  • 14. namespace UlaboxChangoInfrastructureUiHttpController; class ReceptionController { public function addContainerAction(JsonApiRequest $request, $receptionId) { $containerPayload = $this->jsonApiTransformer->fromPayload($request->jsonData(), 'container'); $this->receptionService->addContainer(ReceptionId::fromString($receptionId), $containerPayload); return JsonApiResponse::createJsonApiData(200, null, []); } } namespace UlaboxChangoInfrastructureUiAmqpConsumer; class ContainerAddedToReceptionConsumer extends Consumer { public function execute(AMQPMessage $rabbitMessage) { $message = $this->messageBody($rabbitMessage); $containerPayload = $this->amqpTransformer->fromPayload($message, 'container'); $this->receptionService->addContainer(ReceptionId::fromString($message['reception_id']), $containerPayload); return ConsumerInterface::MSG_ACK; } }
  • 15. namespace UlaboxChangoApplicationService; class ReceptionService { public function addContainer(ReceptionId $receptionId, ContainerPayload $payload) { $reception = $this->receptionRepository->get($receptionId); $reception->addContainer($payload->temperature(), $payload->lines()); $this->receptionRepository->save($reception); $this->eventBus->dispatch($reception->recordedEvents()); } }
  • 16. When CQRS meets Event Sourcing / DDD-Hexagonal Why application service? ● Same entry point ● Coordinate tasks on model ● Early checks ● User authentication
  • 17. namespace UlaboxChangoDomainModelReception; class Reception extends Aggregate { public function addContainer(Temperature $temperature, array $containerLines) { Assertion::allIsInstanceOf($containerLines, ContainerLinePayload::class); $containerId = ContainerId::create($this->id(), $temperature, count($this->containers)); $this->containers->set((string) $containerId, new Container($containerId, $temperature)); $this->recordThat(new ContainerWasAdded($this->id, $containerId, $temperature)); foreach ($containerLines as $line) { $this->addLine($containerId, $line->label(), $line->quantity(), $line->type()); } } public function addLine(ContainerId $containerId, Label $label, LineQuantity $quantity, ItemType $type) { if (!$container = $this->containers->get((string) $containerId)) { throw new EntityNotFoundException("Container not found"); } $container->addLine(ContainerLine::create($label, $quantity, $type)); $this->recordThat(new ContainerLineWasAdded($this->id, $containerId, $label, $quantity, $type)); } }
  • 18. namespace UlaboxChangoDomainModelReceptionContainer; class Container { public function __construct(ContainerId $id, Temperature $temperature) { $this->id = $id; $this->temperature = $temperature; $this->lines = new ArrayCollection(); $this->status = ContainerStatus::PENDING(); } public function addLine(ContainerLine $line) { if ($this->containsLine($line->label())) { throw new AlreadyRegisteredException("Line already exists"); } $this->lines->set((string) $line->label(), $line); } }
  • 19. namespace UlaboxChangoInfrastructurePersistenceDoctrineReception; class DoctrineReceptionRepository implements ReceptionRepository { public function get(ReceptionId $id) { return $this->find($id); } public function save(Reception $reception) { $this->_em->persist($reception); } }
  • 20. Let’s apply Command and Query Responsibility Segregation
  • 21. When CQRS meets Event Sourcing / CQRS CQRS Separate: ● Command: do something ● Query: ask for something Different source of data for read and write: ● Write model with DDD tactical patterns ● Read model with listeners to events
  • 22. When CQRS meets Event Sourcing / CQRS Command bus ● Finds handler for each action ● Decoupled command creator and handler ● Middlewares ○ Transactional ○ Logging ● Asynchronous actions ● Separation of concerns
  • 23. When CQRS meets Event Sourcing / CQRS Event bus ● Posted events are delivered to matching event handlers ● Decouples event producers and reactors ● Middlewares ○ Rabbit ○ Add correlation id ● Asynchronous actions ● Separation of concerns
  • 24. When CQRS meets Event Sourcing / CQRS
  • 25. namespace UlaboxChangoApplicationService; class ReceptionService { public function addContainer(ReceptionId $receptionId, ContainerPayload $payload) { $command = new AddContainer($receptionId, $payload->temperature(), $payload->containerLines()); $this->commandBus->handle($command); } } namespace UlaboxChangoDomainCommandReception; class ReceptionCommandHandler extends CommandHandler { public function handleAddContainer(AddContainer $command) { $reception = $this->receptionRepository->get($command->aggregateId()); $reception->addContainer($command->temperature(), $command->lines()); $this->receptionRepository->save($reception); $this->eventBus->dispatch($reception->recordedEvents()); } }
  • 26. namespace UlaboxChangoDomainReadModelReception; class ReceptionProjector extends ReadModelProcessor { public function applyContainerWasAdded(ContainerWasAdded $event) { $reception = $this->receptionInfoView->receptionOfId($event->aggregateId()); $container = new ContainerProjection($event->containerId(), $event->temperature()); $this->receptionInfoView->save($reception->addContainer($container)); } public function applyContainerLineWasAdded(ContainerLineWasAdded $event) { $reception = $this->receptionInfoView->receptionOfId($event->aggregateId()); $line = ContainerLineProjection($event->label(), $event->quantity(), $event->itemType()); $this->receptionInfoView->save($reception->addContainerLine($event->containerId(), $line)); } } namespace UlaboxChangoDomainReadModelReception; interface ReceptionView { public function save(ReceptionProjection $reception); public function receptionOfId(ReceptionId $receptionId); public function find(Query $query); }
  • 27. namespace UlaboxChangoApplicationService; class ReceptionQueryService { public function byId(ReceptionId $receptionId) { return $this->receptionView->receptionOfId($receptionId); } public function byContainer(ContainerId $containerId) { return $this->receptionView->find(new byContainer($containerId)); } public function search($filters, Paging $paging = null, Sorting $sorting = null) { return $this->receptionView->find(new ByFilters($filters, $sorting, $paging)); } }
  • 28. Let’s get crazy: event sourcing
  • 29. When CQRS meets Event Sourcing / Event sourcing Event sourcing ● Entities are reconstructed with events ● No state ● No database to update manually ● No joins
  • 30. When CQRS meets Event Sourcing / Event sourcing Why event sourcing? ● Get state of an aggregate at any moment in time ● Append-only model storing events is easier to scale ● Forces to log because everything is an event ● No coupling between current state in the domain and in storage ● Simulate business suppositions ○ Change picking algorithm
  • 31. When CQRS meets Event Sourcing / Event sourcing Event Store ● PostgreSQL ● jsonb ● DBAL
  • 32. namespace UlaboxChangoInfrastructurePersistenceEventStore; class PDOEventStore implements EventStore { public function append(AggregateId $id, EventStream $eventStream) { $stmt = $this->connection->prepare("INSERT INTO event_store (data) VALUES (:message)"); $this->connection->beginTransaction(); foreach ($eventStream as $event) { if (!$stmt->execute(['message' => $this->eventSerializer->serialize($event)])) { $this->connection->rollBack(); } } $this->connection->commit(); } public function load(AggregateId $id) { $stmt = $this->connection->prepare("SELECT data FROM event_store WHERE data->'payload'->>'aggregate_id' = :id"); $stmt->execute(['id' => (string) $id]); $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); $events = []; foreach ($rows as $row) { $events[] = $this->eventSerializer->deserialize($row['data']); } return new EventStream($events); } }
  • 33. When CQRS meets Event Sourcing / Event sourcing
  • 34. namespace UlaboxChangoInfrastructurePersistenceModelReception; class EventSourcingReceptionRepository implements ReceptionRepository { public function save(Reception $reception) { $events = $reception->recordedEvents(); $this->eventStore->append($reception->id(), $events); foreach ($events as $event) { $this->eventBus->dispatch($event); } } public function load(ReceptionId $id) { $eventStream = $this->eventStore->load($id); return Reception::reconstituteFromEvents( new AggregateHistory($id, $eventStream) ); } }
  • 35. namespace UlaboxChangoDomainModelReception; class Reception extends EventSourcedAggregate { public static function create( ReceptionId $id, DateTime $receptionDate, SupplierId $supplierId ) { $instance = new self($id); $instance->recordThat( new ReceptionWasScheduled($id, $receptionDate, $supplierId) ); return $instance; } protected function applyReceptionWasScheduled(ReceptionWasScheduled $event) { $this->receptionDate = $event->receptionDate(); $this->supplierId = $event->supplierId(); $this->status = ReceptionStatus::PENDING(); $this->containers = new ArrayCollection(); } }
  • 36. namespace UlaboxChangoDomainModel; abstract class EventSourcedAggregate implements AggregateRoot, EventRecorder { protected function __construct() { $this->version = 0; $this->eventStream = new EventStream(); } protected function recordThat(Event $event) { $this->apply($event); $this->eventStream->append($event); } protected function apply(Event $event) { $classParts = explode('', get_class($event)); $methodName = 'apply'.end($classParts); if (method_exists($this, $methodName)) { $this->$methodName($event); } $this->version++; } }
  • 37. namespace UlaboxChangoDomainModelReception; class Reception extends EventSourcedAggregate { public static function reconstituteFromEvents(AggregateHistory $history) { $instance = new self($history->aggregateId()); foreach ($history->events() as $event) { $instance->apply($event); } return $instance; } public function addContainer(Temperature $temperature, array $containerLines) { $containerId = ContainerId::create( $this->id(), $temperature, count($this->containers) ); $this->recordThat( new ContainerWasAdded($this->id, $containerId, $temperature) ); foreach ($containerLines as $line) { $this->addLine( $containerId, $line->label(), $line->quantity(), $line->type() ); } } protected function applyContainerWasAdded(ContainerWasAdded $event) { $container = new Container($event->containerId(), $event->temperature()); $this->containers->set((string) $event->containerId(), $container); } }
  • 39. When CQRS meets Event Sourcing / Conclusions Benefits ● Decoupling ● Performance in Read Model ● Scalability ● No joins ● Async with internal events and consumers ● Communicate other bounded contexts with events
  • 40. When CQRS meets Event Sourcing / Conclusions Problems found ● With DDD ○ Decide aggregates => talk a LOT with the domain experts ○ Boilerplate => generate as much boilerplate as possible ● With CQRS ○ Forgetting listeners in read model ○ Repeated code structure ● With event sourcing ○ Adapting your mindset ○ Forgetting applying the event to the entity ○ Retro compatibility with old events ● Concurrency/eventual consistency
  • 42. When CQRS meets Event Sourcing / Work with us Work with us
  • 44. When CQRS meets Event Sourcing / Conclusions