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

Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...Cizo Technology Services
 
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...Matt Ray
 
Folding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesFolding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesPhilip Schwarz
 
Cyber security and its impact on E commerce
Cyber security and its impact on E commerceCyber security and its impact on E commerce
Cyber security and its impact on E commercemanigoyal112
 
Xen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdfXen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdfStefano Stabellini
 
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
 
Intelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmIntelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmSujith Sukumaran
 
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
 
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEBATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEOrtus Solutions, Corp
 
What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...Technogeeks
 
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...Natan Silnitsky
 
Buds n Tech IT Solutions: Top-Notch Web Services in Noida
Buds n Tech IT Solutions: Top-Notch Web Services in NoidaBuds n Tech IT Solutions: Top-Notch Web Services in Noida
Buds n Tech IT Solutions: Top-Notch Web Services in Noidabntitsolutionsrishis
 
What are the key points to focus on before starting to learn ETL Development....
What are the key points to focus on before starting to learn ETL Development....What are the key points to focus on before starting to learn ETL Development....
What are the key points to focus on before starting to learn ETL Development....kzayra69
 
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanySuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanyChristoph Pohl
 
Unveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsUnveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsAhmed Mohamed
 
英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作qr0udbr0
 
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company OdishaBalasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odishasmiwainfosol
 
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...stazi3110
 

Último (20)

Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
 
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
 
Folding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesFolding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a series
 
Cyber security and its impact on E commerce
Cyber security and its impact on E commerceCyber security and its impact on E commerce
Cyber security and its impact on E commerce
 
Xen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdfXen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdf
 
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...
 
Intelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmIntelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalm
 
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
 
Advantages of Odoo ERP 17 for Your Business
Advantages of Odoo ERP 17 for Your BusinessAdvantages of Odoo ERP 17 for Your Business
Advantages of Odoo ERP 17 for Your Business
 
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEBATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
 
What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...
 
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
 
Buds n Tech IT Solutions: Top-Notch Web Services in Noida
Buds n Tech IT Solutions: Top-Notch Web Services in NoidaBuds n Tech IT Solutions: Top-Notch Web Services in Noida
Buds n Tech IT Solutions: Top-Notch Web Services in Noida
 
What are the key points to focus on before starting to learn ETL Development....
What are the key points to focus on before starting to learn ETL Development....What are the key points to focus on before starting to learn ETL Development....
What are the key points to focus on before starting to learn ETL Development....
 
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanySuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
 
Unveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsUnveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML Diagrams
 
英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作
 
Hot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort Service
Hot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort ServiceHot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort Service
Hot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort Service
 
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company OdishaBalasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
 
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
 

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