SlideShare a Scribd company logo
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

More Related Content

What's hot

Database Design Patterns
Database Design PatternsDatabase Design Patterns
Database Design Patterns
Hugo 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 Pimple
Hugo 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 PHPersistence
Hugo 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 cliente
Leonardo 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 Hobgoblins
Ross Tuck
 
Min-Maxing Software Costs
Min-Maxing Software CostsMin-Maxing Software Costs
Min-Maxing Software Costs
Konstantin Kudryashov
 
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
Konstantin 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 application
Samuel ROZE
 
Doctrine fixtures
Doctrine fixturesDoctrine fixtures
Doctrine fixtures
Bill 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 mocking
Konstantin Kudryashov
 
Command Bus To Awesome Town
Command Bus To Awesome TownCommand Bus To Awesome Town
Command Bus To Awesome Town
Ross Tuck
 
Introduction to Zend Framework web services
Introduction to Zend Framework web servicesIntroduction to Zend Framework web services
Introduction to Zend Framework web services
Michelangelo van Dam
 
Adding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy ApplicationsAdding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy Applications
Sam Hennessy
 
Zend framework service
Zend framework serviceZend framework service
Zend framework service
Michelangelo van Dam
 
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
Konstantin 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 Old
Ross Tuck
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For Beginners
Jonathan 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
 

What's hot (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)
 

Viewers also liked

Refactorizando Pccomponentes.com con Symfony
Refactorizando Pccomponentes.com con SymfonyRefactorizando Pccomponentes.com con Symfony
Refactorizando Pccomponentes.com con Symfony
Mario Marín
 
Introduction to hexagonal architecture
Introduction to hexagonal architectureIntroduction to hexagonal architecture
Introduction to hexagonal architecture
Manel Sellés
 
Introduction to testing
Introduction to testingIntroduction to testing
Introduction to testing
Manel Sellés
 
Symfony 2 : Performances et Optimisations
Symfony 2 : Performances et OptimisationsSymfony 2 : Performances et Optimisations
Symfony 2 : Performances et Optimisations
Les-Tilleuls.coop
 
CQRS & event sourcing in the wild
CQRS & event sourcing in the wildCQRS & event sourcing in the wild
CQRS & event sourcing in the wild
Michiel 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 CQRS
Steve Pember
 
CQRS + Event Sourcing
CQRS + Event SourcingCQRS + Event Sourcing
CQRS + Event Sourcing
Mike 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 Sourcing
Ben 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 stack
Rubén Sospedra
 
Microservices
MicroservicesMicroservices
Cqrs from the trenches
Cqrs from the trenchesCqrs from the trenches
Cqrs from the trenches
nextbuild
 
Greg young’s simple cqrs sample
Greg young’s simple cqrs sampleGreg young’s simple cqrs sample
Greg young’s simple cqrs sample
Leonardo 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 @codelytv
CodelyTV
 
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
 
Matters of State
Matters of StateMatters of State
Matters of State
Kris Wallsmith
 
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
 
DDD 준비 서문래
DDD 준비 서문래DDD 준비 서문래
DDD 준비 서문래
beom kyun choi
 

Viewers also liked (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 to 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 scenarios
Divante
 
Modularity and Layered Data Model
Modularity and Layered Data ModelModularity and Layered Data Model
Modularity and Layered Data Model
Attila 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 Apps
Kris Wallsmith
 
Building Lithium Apps
Building Lithium AppsBuilding Lithium Apps
Building Lithium Apps
Nate Abele
 
#pugMi - DDD - Value objects
#pugMi - DDD - Value objects#pugMi - DDD - Value objects
#pugMi - DDD - Value objects
Simone Gentili
 
Drupal 8 Services And Dependency Injection
Drupal 8 Services And Dependency InjectionDrupal 8 Services And Dependency Injection
Drupal 8 Services And Dependency Injection
Philip Norton
 
Practical Event Sourcing
Practical Event SourcingPractical Event Sourcing
Practical Event Sourcing
Mathias Verraes
 
Understanding backbonejs
Understanding backbonejsUnderstanding backbonejs
Understanding backbonejs
Nick Lee
 
2024 eCommerceDays Toulouse - Sylius 2.0.pdf
2024 eCommerceDays Toulouse - Sylius 2.0.pdf2024 eCommerceDays Toulouse - Sylius 2.0.pdf
2024 eCommerceDays Toulouse - Sylius 2.0.pdf
Łukasz Chruściel
 
My Development Story
My Development StoryMy Development Story
My Development Story
Takahiro Fujiwara
 
The command dispatcher pattern
The command dispatcher patternThe command dispatcher pattern
The command dispatcher pattern
olvlvl
 
Lazy evaluation drupal camp moscow 2014
Lazy evaluation drupal camp moscow 2014Lazy evaluation drupal camp moscow 2014
Lazy evaluation drupal camp moscow 2014
Evgeny Nikitin
 
Design Patterns
Design PatternsDesign Patterns
Design Patterns
Lorna Mitchell
 
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
CODEiD 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 Developers
Christopher Batey
 
Zendcon 2007 Api Design
Zendcon 2007 Api DesignZendcon 2007 Api Design
Zendcon 2007 Api Design
unodelostrece
 
Advanced symfony Techniques
Advanced symfony TechniquesAdvanced symfony Techniques
Advanced symfony Techniques
Kris Wallsmith
 
Custom entities in d8
Custom entities in d8Custom entities in d8
Custom entities in d8
Samuel Solís Fuentes
 
Event Sourcing with php
Event Sourcing with phpEvent Sourcing with php
Event Sourcing with php
Sébastien Houzé
 

Similar to 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
 
2024 eCommerceDays Toulouse - Sylius 2.0.pdf
2024 eCommerceDays Toulouse - Sylius 2.0.pdf2024 eCommerceDays Toulouse - Sylius 2.0.pdf
2024 eCommerceDays Toulouse - Sylius 2.0.pdf
 
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
 

Recently uploaded

Hand Rolled Applicative User Validation Code Kata
Hand Rolled Applicative User ValidationCode KataHand Rolled Applicative User ValidationCode Kata
Hand Rolled Applicative User Validation Code Kata
Philip Schwarz
 
Webinar On-Demand: Using Flutter for Embedded
Webinar On-Demand: Using Flutter for EmbeddedWebinar On-Demand: Using Flutter for Embedded
Webinar On-Demand: Using Flutter for Embedded
ICS
 
UI5con 2024 - Bring Your Own Design System
UI5con 2024 - Bring Your Own Design SystemUI5con 2024 - Bring Your Own Design System
UI5con 2024 - Bring Your Own Design System
Peter Muessig
 
Malibou Pitch Deck For Its €3M Seed Round
Malibou Pitch Deck For Its €3M Seed RoundMalibou Pitch Deck For Its €3M Seed Round
Malibou Pitch Deck For Its €3M Seed Round
sjcobrien
 
WWDC 2024 Keynote Review: For CocoaCoders Austin
WWDC 2024 Keynote Review: For CocoaCoders AustinWWDC 2024 Keynote Review: For CocoaCoders Austin
WWDC 2024 Keynote Review: For CocoaCoders Austin
Patrick Weigel
 
Oracle 23c New Features For DBAs and Developers.pptx
Oracle 23c New Features For DBAs and Developers.pptxOracle 23c New Features For DBAs and Developers.pptx
Oracle 23c New Features For DBAs and Developers.pptx
Remote DBA Services
 
E-commerce Development Services- Hornet Dynamics
E-commerce Development Services- Hornet DynamicsE-commerce Development Services- Hornet Dynamics
E-commerce Development Services- Hornet Dynamics
Hornet Dynamics
 
UI5con 2024 - Keynote: Latest News about UI5 and it’s Ecosystem
UI5con 2024 - Keynote: Latest News about UI5 and it’s EcosystemUI5con 2024 - Keynote: Latest News about UI5 and it’s Ecosystem
UI5con 2024 - Keynote: Latest News about UI5 and it’s Ecosystem
Peter Muessig
 
Unveiling the Advantages of Agile Software Development.pdf
Unveiling the Advantages of Agile Software Development.pdfUnveiling the Advantages of Agile Software Development.pdf
Unveiling the Advantages of Agile Software Development.pdf
brainerhub1
 
zOS Mainframe JES2-JES3 JCL-JECL Differences
zOS Mainframe JES2-JES3 JCL-JECL DifferenceszOS Mainframe JES2-JES3 JCL-JECL Differences
zOS Mainframe JES2-JES3 JCL-JECL Differences
YousufSait3
 
UI5con 2024 - Boost Your Development Experience with UI5 Tooling Extensions
UI5con 2024 - Boost Your Development Experience with UI5 Tooling ExtensionsUI5con 2024 - Boost Your Development Experience with UI5 Tooling Extensions
UI5con 2024 - Boost Your Development Experience with UI5 Tooling Extensions
Peter Muessig
 
GreenCode-A-VSCode-Plugin--Dario-Jurisic
GreenCode-A-VSCode-Plugin--Dario-JurisicGreenCode-A-VSCode-Plugin--Dario-Jurisic
GreenCode-A-VSCode-Plugin--Dario-Jurisic
Green Software Development
 
Top Benefits of Using Salesforce Healthcare CRM for Patient Management.pdf
Top Benefits of Using Salesforce Healthcare CRM for Patient Management.pdfTop Benefits of Using Salesforce Healthcare CRM for Patient Management.pdf
Top Benefits of Using Salesforce Healthcare CRM for Patient Management.pdf
VALiNTRY360
 
Everything You Need to Know About X-Sign: The eSign Functionality of XfilesPr...
Everything You Need to Know About X-Sign: The eSign Functionality of XfilesPr...Everything You Need to Know About X-Sign: The eSign Functionality of XfilesPr...
Everything You Need to Know About X-Sign: The eSign Functionality of XfilesPr...
XfilesPro
 
如何办理(hull学位证书)英国赫尔大学毕业证硕士文凭原版一模一样
如何办理(hull学位证书)英国赫尔大学毕业证硕士文凭原版一模一样如何办理(hull学位证书)英国赫尔大学毕业证硕士文凭原版一模一样
如何办理(hull学位证书)英国赫尔大学毕业证硕士文凭原版一模一样
gapen1
 
J-Spring 2024 - Going serverless with Quarkus, GraalVM native images and AWS ...
J-Spring 2024 - Going serverless with Quarkus, GraalVM native images and AWS ...J-Spring 2024 - Going serverless with Quarkus, GraalVM native images and AWS ...
J-Spring 2024 - Going serverless with Quarkus, GraalVM native images and AWS ...
Bert Jan Schrijver
 
Oracle Database 19c New Features for DBAs and Developers.pptx
Oracle Database 19c New Features for DBAs and Developers.pptxOracle Database 19c New Features for DBAs and Developers.pptx
Oracle Database 19c New Features for DBAs and Developers.pptx
Remote DBA Services
 
Enums On Steroids - let's look at sealed classes !
Enums On Steroids - let's look at sealed classes !Enums On Steroids - let's look at sealed classes !
Enums On Steroids - let's look at sealed classes !
Marcin Chrost
 
Using Query Store in Azure PostgreSQL to Understand Query Performance
Using Query Store in Azure PostgreSQL to Understand Query PerformanceUsing Query Store in Azure PostgreSQL to Understand Query Performance
Using Query Store in Azure PostgreSQL to Understand Query Performance
Grant Fritchey
 
E-Invoicing Implementation: A Step-by-Step Guide for Saudi Arabian Companies
E-Invoicing Implementation: A Step-by-Step Guide for Saudi Arabian CompaniesE-Invoicing Implementation: A Step-by-Step Guide for Saudi Arabian Companies
E-Invoicing Implementation: A Step-by-Step Guide for Saudi Arabian Companies
Quickdice ERP
 

Recently uploaded (20)

Hand Rolled Applicative User Validation Code Kata
Hand Rolled Applicative User ValidationCode KataHand Rolled Applicative User ValidationCode Kata
Hand Rolled Applicative User Validation Code Kata
 
Webinar On-Demand: Using Flutter for Embedded
Webinar On-Demand: Using Flutter for EmbeddedWebinar On-Demand: Using Flutter for Embedded
Webinar On-Demand: Using Flutter for Embedded
 
UI5con 2024 - Bring Your Own Design System
UI5con 2024 - Bring Your Own Design SystemUI5con 2024 - Bring Your Own Design System
UI5con 2024 - Bring Your Own Design System
 
Malibou Pitch Deck For Its €3M Seed Round
Malibou Pitch Deck For Its €3M Seed RoundMalibou Pitch Deck For Its €3M Seed Round
Malibou Pitch Deck For Its €3M Seed Round
 
WWDC 2024 Keynote Review: For CocoaCoders Austin
WWDC 2024 Keynote Review: For CocoaCoders AustinWWDC 2024 Keynote Review: For CocoaCoders Austin
WWDC 2024 Keynote Review: For CocoaCoders Austin
 
Oracle 23c New Features For DBAs and Developers.pptx
Oracle 23c New Features For DBAs and Developers.pptxOracle 23c New Features For DBAs and Developers.pptx
Oracle 23c New Features For DBAs and Developers.pptx
 
E-commerce Development Services- Hornet Dynamics
E-commerce Development Services- Hornet DynamicsE-commerce Development Services- Hornet Dynamics
E-commerce Development Services- Hornet Dynamics
 
UI5con 2024 - Keynote: Latest News about UI5 and it’s Ecosystem
UI5con 2024 - Keynote: Latest News about UI5 and it’s EcosystemUI5con 2024 - Keynote: Latest News about UI5 and it’s Ecosystem
UI5con 2024 - Keynote: Latest News about UI5 and it’s Ecosystem
 
Unveiling the Advantages of Agile Software Development.pdf
Unveiling the Advantages of Agile Software Development.pdfUnveiling the Advantages of Agile Software Development.pdf
Unveiling the Advantages of Agile Software Development.pdf
 
zOS Mainframe JES2-JES3 JCL-JECL Differences
zOS Mainframe JES2-JES3 JCL-JECL DifferenceszOS Mainframe JES2-JES3 JCL-JECL Differences
zOS Mainframe JES2-JES3 JCL-JECL Differences
 
UI5con 2024 - Boost Your Development Experience with UI5 Tooling Extensions
UI5con 2024 - Boost Your Development Experience with UI5 Tooling ExtensionsUI5con 2024 - Boost Your Development Experience with UI5 Tooling Extensions
UI5con 2024 - Boost Your Development Experience with UI5 Tooling Extensions
 
GreenCode-A-VSCode-Plugin--Dario-Jurisic
GreenCode-A-VSCode-Plugin--Dario-JurisicGreenCode-A-VSCode-Plugin--Dario-Jurisic
GreenCode-A-VSCode-Plugin--Dario-Jurisic
 
Top Benefits of Using Salesforce Healthcare CRM for Patient Management.pdf
Top Benefits of Using Salesforce Healthcare CRM for Patient Management.pdfTop Benefits of Using Salesforce Healthcare CRM for Patient Management.pdf
Top Benefits of Using Salesforce Healthcare CRM for Patient Management.pdf
 
Everything You Need to Know About X-Sign: The eSign Functionality of XfilesPr...
Everything You Need to Know About X-Sign: The eSign Functionality of XfilesPr...Everything You Need to Know About X-Sign: The eSign Functionality of XfilesPr...
Everything You Need to Know About X-Sign: The eSign Functionality of XfilesPr...
 
如何办理(hull学位证书)英国赫尔大学毕业证硕士文凭原版一模一样
如何办理(hull学位证书)英国赫尔大学毕业证硕士文凭原版一模一样如何办理(hull学位证书)英国赫尔大学毕业证硕士文凭原版一模一样
如何办理(hull学位证书)英国赫尔大学毕业证硕士文凭原版一模一样
 
J-Spring 2024 - Going serverless with Quarkus, GraalVM native images and AWS ...
J-Spring 2024 - Going serverless with Quarkus, GraalVM native images and AWS ...J-Spring 2024 - Going serverless with Quarkus, GraalVM native images and AWS ...
J-Spring 2024 - Going serverless with Quarkus, GraalVM native images and AWS ...
 
Oracle Database 19c New Features for DBAs and Developers.pptx
Oracle Database 19c New Features for DBAs and Developers.pptxOracle Database 19c New Features for DBAs and Developers.pptx
Oracle Database 19c New Features for DBAs and Developers.pptx
 
Enums On Steroids - let's look at sealed classes !
Enums On Steroids - let's look at sealed classes !Enums On Steroids - let's look at sealed classes !
Enums On Steroids - let's look at sealed classes !
 
Using Query Store in Azure PostgreSQL to Understand Query Performance
Using Query Store in Azure PostgreSQL to Understand Query PerformanceUsing Query Store in Azure PostgreSQL to Understand Query Performance
Using Query Store in Azure PostgreSQL to Understand Query Performance
 
E-Invoicing Implementation: A Step-by-Step Guide for Saudi Arabian Companies
E-Invoicing Implementation: A Step-by-Step Guide for Saudi Arabian CompaniesE-Invoicing Implementation: A Step-by-Step Guide for Saudi Arabian Companies
E-Invoicing Implementation: A Step-by-Step Guide for Saudi Arabian Companies
 

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