SlideShare a Scribd company logo
1 of 83
Download to read offline
Decoupling Ulabox.com
monolith
From CRUD to DDD
Aleix Vergés
Backend developer
Scrum Master
@avergess
aleix.verges@ulabox.com
1. What’s Ulabox?
2. Decoupling. Why?
/**
* @param Cart $cart
* @return Order $order
*/
public function createOrder(Cart $cart)
{
// Year 2011
$order = $this->moveCartToOrder($cart);
$this->sendConfirmationEmail($order);
$this->reindexSolr($order);
$this->sendToWarehouse($order);
// Year 2012
$this->sendToFinantialErp($order);
$this->sendDonationEmail($order);
// Year 2014
$this->sendToDeliveryRoutingSoftware($order);
// Year 2015
$this->sendToJustInTimeSuppliers($order);
// Year 2016
$this->sendToWarehouseSpoke($order); // WTF!
$this->sendToShipLoadSoftware($order); // WTF!!!!
}
Decoupling Ulabox.com monolith. From CRUD to DDD
Our problem
WTF!
Past
● CRUD doesn’t make sense anymore
● It had sense at the beginning
● Product, Logistic, Delivery, Cart, Customers, ...
● It’s not sustainable.
Decoupling Ulabox.com monolith. From CRUD to DDD
Our solution
/**
* @param Cart $cart
*/
public function createOrder(Cart $cart)
{
$createOrder = new CreateOrderCommand(Cart $cart);
$this->commandBus->dispatch($createOrder)
}
Event bus
CreateOrder command
OrderWasCreated event
subscribe
subscribe
subscribe
subscribe
subscribe
subscribe
subscribe
subscribe
subscribe
Decoupling Ulabox.com monolith. From CRUD to DDD
3. The tools
● Domain
● Aggregate / Aggregate Root
● Repository
● Domain Events
● Service
● Command Bus
● Event Bus
* Domain Drive Design: https://en.wikipedia.org/wiki/Domain-driven_design
Decoupling Ulabox.com monolith. From CRUD to DDD
4. A responsability question
Refactoring and manage technical debt is
not a choice, but a responsability
Decoupling Ulabox.com monolith. From CRUD to DDD
5. Controllers
REFUND!
5.1. OrderController
5. Controllers
class OrderController extends BaseController
{
public function refundAction(Request $request, $id)
{
$em = $this->container->get('doctrine.orm.entity_manager');
$orderPayment = $em->getRepository('UlaboxCoreBundle:OrderPayment')->find($id);
$amount = $request->request->get('refund');
$data = $this->container->get('sermepa')->processRefund($orderPayment, $amount);
$orderRefund = new OrderPayment();
$orderRefund->setAmount($amount);
...
$em->persist($orderRefund);
$em->flush();
return $this->redirectToRoute('order_show', ['id' => $orderPayment->getOrder()->getId()]);
}
public function someOtherAction(Request $request, $id)
...
}
Decoupling Ulabox.com monolith. From CRUD to DDD
Problems
● Hidden dependencies
● Inheritance.
● Biz logic in the controller.
● Non aggregate root.
● Difficult to test.
* Dependency Injection: https://es.wikipedia.org/wiki/Inyecci%C3%B3n_de_dependencias
Decoupling Ulabox.com monolith. From CRUD to DDD
Solutions
● Dependency Injection
● Break inheritance from base controller.
● Application services.
● Testing
5.2. Controller as a service
5. Controllers
# services.yml
imports:
- { resource: controllers.yml }
# controllers.yml
ulabox_ulaoffice.controllers.order:
class: UlaboxUlaofficeBundleControllerOrderController
arguments:
- '@refund'
- '@router'
...
5. Controllers
5.3. Dependency Injection
/**
* @Route("/orders", service="ulabox_ulaoffice.controllers.order")
*/
class OrderController
{
/**
* @param Refund $refund
* @param RouterInterface $router
*/
public function __construct(Refund $refund, RouterInterface $router, ……..)
{
$this->refund = $refund;
$this->router = $router;
...
}
}
5. Controllers
5.4. Delegate logic to services
/**
* @Route("/orders", service="ulabox_ulaoffice.controllers.order")
*/
class OrderController
{
public function refundAction(Request $request, $id)
{
$amount = $request->request->get('refund');
$method = $request->request->get('method');
$orderId = $request->request->get('order_id');
try {
$this->refund->execute($orderId, $id, (float)$amount, $method);
$this->session->getFlashBag()->add('success', 'Refund has been processed correctly');
} catch (Exception $e) {
$this->session->getFlashBag()->add('danger', $e->getMessage());
}
return new RedirectResponse($this->router->generate('order_show', ['id' => $orderId]));
}
}
5. Controllers
5.5. Unit test
class OrderControllerTest extends PHPUnit_Framework_TestCase
{
public function setUp()
{
$this->refund = $this->prophesize(Refund::class);
$this->router = $this->prophesize(RouterInterface::class);
$this->orderController = new OrderController(
$this->refund->reveal(),
$this->router->reveal()
);
}
...
}
class OrderControllerTest extends PHPUnit_Framework_TestCase
{
...
public function testShouldDelegateOrderRefund()
{
$orderPaymentId = 34575;
$amount = 10.95;
$orderId = 12345;
$orderRoute = 'some/route';
$request = $this->mockRequest($orderId, $orderPaymentId, $amount, $orderRoute);
$this->refund
->execute($orderId, $orderPaymentId, $amount, PaymentPlatform::REDSYS)
->shouldBeCalled();
$this->router->generate('order_show', ['id' => $orderId])->willReturn($orderRoute);
$actual = $this->orderController->refundAction($request->reveal(), $orderPaymentId);
$this->assertEquals(new RedirectResponse($orderRoute), $actual);
}
}
6. Symfony Forms
RESCHEDULE
6. Symfony Forms
6.1. Anemic Model
class OrderController extends BaseController
{
public function rescheduleAction(Request $request, $id)
{
$order = $this->container->get('order')->reposition($id);
$form = $this->createForm(new OrderType(), $order);
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($order);
$em->flush();
$request->getSession()->getFlashBag()->add('success', 'Your changes were saved!');
return $this->redirect($this->generateUrl('reschedule_success'));
}
return ['entity' => $entity, 'form' => $form->createView()];
}
}
Decoupling Ulabox.com monolith. From CRUD to DDD
Problems
● Coupling between entities and Symfony Forms.
● Anemic Model.
● Intention?
Decoupling Ulabox.com monolith. From CRUD to DDD
Solutions
● Use of DTO/Command
● Reflect the Intention!
● Rich Domain.
● Testing.
6. Symfony Forms
6.2. Command !== CLI Command
class Reschedule
{
public $orderId;
public $addressId;
public $slotVars;
public $comments;
public function __construct($orderId, $addressId, $slotVars, $comments)
{
$this->orderId = $orderId;
$this->addressId = $addressId;
$this->slotVars = $slotVars;
$this->comments = $comments;
}
}
6. Symfony Forms
6.3. Building the Form
class OrderController extends BaseController
{
public function rescheduleDisplayingAction(Request $request, $id)
{
$order = $this->orderRepository->get($id);
$address = $order->deliveryAddress()->asAddress();
$rescheduleOrder = Reschedule::fromPayload([
'order_id' => $order->getId(),
'address_id' => $address->getId(),
'slot_vars' => $order->deliverySlotVars(),
'comments' => $order->deliveryComments(),
]);
$rescheduleForm = $this->formFactory->create(OrderRescheduleType::class, $rescheduleOrder);
return ['order' => $order, 'form' => $rescheduleForm->createView()];
}
}
6. Symfony Forms
6.4. Submitting the Form
class OrderController extends BaseController
{
public function rescheduleUpdateAction(Request $request, $id)
{
$requestData = $request->get('order_reschedule');
$rescheduleOrder = Reschedule::fromPayload([
'order_id' => $id,
'address_id' => $requestData['addressId'],
'slot_vars' => $requestData['slotVars'],
'comments' => $requestData['comments'],
]);
$rescheduleForm = $this->formFactory->create(OrderRescheduleType::class, $rescheduleOrder);
if ($rescheduleForm->isValid()) {
$this->commandBus->dispatch($rescheduleOrder);
}
return new RedirectResponse($this->router->generate($this->entity Properties['route']));
}
}
6. Symfony Forms
6.5. Unit test
class OrderControllerTest extends PHPUnit_Framework_TestCase
{
public function testShouldDelegateOrderRescheduleToCommandBus()
{
$orderId = 12345;
$addressId = 6789;
$slotVars = '2016-03-25|523|2|15';
$comments = 'some comments';
$expectedRoute = 'http://some.return.url';
$request = $this->mockRequest($orderId, $addressId, $slotVars, $comments);
$form = $this->mockForm();
$form->isValid()->willReturn(true);
$this->router->generate('order')->willReturn($expectedRoute);
$this->commandBus->dispatch(Argument::type(Reschedule::class))->shouldBeCalled();
$actual = $this->orderController->rescheduleUpdateAction($request->reveal(), $orderId);
$this->assertEquals(new RedirectResponse($expectedRoute), $actual);
}
}
7. From CRUD to DDD
7. From CRUD to DDD
7.1. Summing...
class OrderController extends BaseController
{
public function rescheduleUpdateAction(Request $request, $id)
{
$requestData = $request->get('order_reschedule');
$rescheduleOrder = Reschedule::fromPayload([
'order_id' => $id,
'address_id' => $requestData['addressId'],
'slot_vars' => $requestData['slotVars'],
'comments' => $requestData['comments'],
]);
$rescheduleForm = $this->formFactory->create(OrderRescheduleType::class, $rescheduleOrder);
if ($rescheduleForm>isValid()) {
$this->commandBus->dispatch($rescheduleOrder);
}
return new RedirectResponse($this->router->generate($this->entity Properties['route']));
}
}
7. From CRUD to DDD
7.2. Handling
class RescheduleHandler extends CommandHandler
{
public function __construct( ... ) { ... }
public function handleReschedule(Reschedule $rescheduleOrder)
{
$timeLineSlot = $this->slotManager->createTimelineSlotFromVars($rescheduleOrder->slotVars);
$order = $this->orderRepository->get($rescheduleOrder->aggregateId);
$delivery = $order->getOrderDelivery();
$delivery->setSlot($timeLineSlot->getSlot());
$delivery->setLoadTime($timeLineSlot->getLoadTime());
$delivery->setShift($timeLineSlot->getShift()->getShift());
...
$order->rescheduleDelivery($delivery);
$this->orderRepository->save($order);
$this->eventBus->publish($order->getUncommittedEvents());
}
}
Decoupling Ulabox.com monolith. From CRUD to DDD
Problems
● Biz logic out of domain.
● Aggregate access.
● Aggregate Root?
● Unprotected Domain.
Decoupling Ulabox.com monolith. From CRUD to DDD
Solutions
● Aggregate Root. Order or Delivery?
● Unique acces point to the domain.
● Clear intention!!
● Testing.
7. From CRUD to DDD
7.3. Order or Delivery?
7. From CRUD to DDD
7.4. Aggregate access point
class RescheduleHandler extends CommandHandler
{
public function __construct( ... ) { ... }
public function handleReschedule(Reschedule $rescheduleDelivery)
{
$timeLineSlot = $this->slotManager->createTimelineSlotFromVars($rescheduleDelivery->slotVars);
$delivery = $this->deliveryRepository->get($rescheduleDelivery->deliveryId);
$delivery->reschedule($timeLineSlot);
$this->deliveryRepository->save($delivery);
$this->eventBus->publish($delivery->getUncommittedEvents());
}
}
7. From CRUD to DDD
7.5. Business logic
class Delivery implements AggregateRoot
{
public function reschedule(TimelineSlot $timelineSlot)
{
$this->setDate($timelineSlot->getDate());
$this->setLoadTime($timelineSlot->getLoadTime());
$this->setSlot($timelineSlot->getSlot());
$this->setShift($timelineSlot->getShift());
$this->setLoad($timelineSlot->getLoad());
$this->setPreparation($timelineSlot->getPreparationDate());
$this->apply(
new DeliveryWasRescheduled(
$this->getAggregateRootId(),
$this->getProgrammedDate(),
$this->getTimeStart(),
$this->getTimeEnd(),
$this->getLoad()->spokeId()
)
);
}
}
7. From CRUD to DDD
7.6. Unit test
class RescheduleHandlerTest extends PHPUnit_Framework_TestCase
{
public function testShouldRescheduleDelivery()
{
$deliveryId = 12345;
$slotVars = '2016-03-25|523|2|15';
$timeLineSlot = TimelineSlotStub::random();
$delivery = $this->prophesize(Delivery::class);
$this->deliveryRepository->get($deliveryId)->willReturn($delivery);
$this->slotManager->createTimelineSlotFromVars($slotVars)->willReturn($timeLineSlot);
$delivery->reschedule($timeLineSlot)->shouldBeCalled();
$this->deliveryRepository->save($delivery)->shouldBeCalled();
$this->eventBus->publish($this->expectedEvents())->shouldBeCalled();
$this->rescheduleOrderHandler->handleReschedule(new Reschedule($deliveryId, $slotVars));
}
}
class DeliveryTest extends PHPUnit_Framework_TestCase
{
public function testShouldRescheduleDelivery()
{
$delivery = OrderDeliveryStub::random();
$timeLineSlot = TimelineSlotStub::random();
$delivery->reschedule($timeLineSlot);
static::assertEquals($timeLineSlot->getDate(), $delivery->getProgrammedDate());
static::assertEquals($timeLineSlot->getLoadTime(), $delivery->getLoadTime());
static::assertEquals($timeLineSlot->getSlot(), $delivery->getSlot());
static::assertEquals($timeLineSlot->getShift(), $delivery->getShift());
static::assertEquals($timeLineSlot->getPreparationDate(), $delivery->getPreparation());
$messageIterator = $delivery->getUncommittedEvents()->getIterator();
$this->assertInstanceOf(
DeliveryWasRescheduled::class, $messageIterator->current()->getPayload()
);
}
}
7. From CRUD to DDD
7.7. Domain event
DeliveryWasRescheduled
Delivery Order
Load
Slot
TimeStart
Date
OrderLine
Product
Tax
Deliveries Orders
8. Aggregates and Repositories
CREDIT CARDS
8. Aggregates and Repositories
8.1. Entity / Repository
class CustomerCreditcardModel
{
public function add($number, $type, $token = null, $expiryDate = null)
{
$customer = $this->tokenStorage->getToken()->getUser();
$creditCard = new CustomerCreditcard();
$creditCard->setNumber($number);
$creditCard->setCustomer($customer);
$creditCard->setType($type);
$creditCard->setToken($token);
$creditCard->setExpiryDate($expiryDate);
$this->creditCardRepository->add($creditCard);
return $creditCard;
}
}
Decoupling Ulabox.com monolith. From CRUD to DDD
Problems
● Aggregate?
● CreditCardRepository???
● Unprotected Domain.
Decoupling Ulabox.com monolith. From CRUD to DDD
Solutions
● Which is the Aggregate?
● What’s the Intention?
● Testing
Customer
CreditCard
class Customer implements AggregateRoot
{
public function addCreditCard($number, $type, $token = '', $expiryDate = '')
{
$creditCard = CustomerCreditcard::create($number, $type, $token, $expiryDate);
$this->creditCards->add($creditCard);
$this->apply(new CreditCardWasRegistered($this->getAggregateRootId(), $number));
}
}
Decoupling Ulabox.com monolith. From CRUD to DDD
8. Aggregates and Repositories
8.2. RegisterCreditCard
class RegisterCreditCard
{
public $customerId;
public $cardNumber;
public $type;
public $token;
public $expiry;
public function __construct($customerId, $cardNumber, $type, $token, $expiry)
{
$this->customerId = $customerId;
$this->cardNumber = $cardNumber;
$this->type = $type;
$this->token = $token;
$this->expiry = $expiry;
}
}
8. Aggregates and Repositories
8.3. RegisterCreditCardHandler
class RegisterCreditCardHandler extends CommandHandler
{
private $customerRepository;
private $eventBus;
public function __construct( ... ) { ... }
public function handleRegisterCreditCard(RegisterCreditCard $registerCreditCard)
{
$customer = $this->customerRepository->get($registerCreditCard->customerId())
$customer->addCreditCard(
$registerCreditCard->cardNumber(),
$registerCreditCard->type(),
$registerCreditCard->token(),
$registerCreditCard->expiry()
);
$this->customerRepository->save($customer);
$this->eventBus->publish($customer->getUncommittedEvents());
}
}
8. Aggregates and Repositories
8.4. Business rules
class Customer implements AggregateRoot
{
public function addCreditCard($number, $type, $token, $expiryDate)
{
if ($this->creditCardExists($number, $type)) {
$this->renewCreditCard($number, $type, $token, $expiryDate);
return;
}
$creditCard = CustomerCreditcard::create($number, $type, $token, $expiryDate);
$this->creditCards->add($creditCard);
$this->apply(new CreditCardWasRegistered($this->getAggregateRootId(), $number));
}
private function renewCreditCard($number, $type, $token, $expiryDate)
{
...
}
}
9. Learned lessons
This is not a Big-Bang
Decoupling Ulabox.com monolith. From CRUD to DDD
Aggregate Election
Decoupling Ulabox.com monolith. From CRUD to DDD
Communication
Decoupling Ulabox.com monolith. From CRUD to DDD
Team
Decoupling Ulabox.com monolith. From CRUD to DDD
¡¡¡Be a Professional!!!
Decoupling Ulabox.com monolith. From CRUD to DDD
@avergess
aleix.verges@ulabox.com
www.linkedin.com/in/avergess
Thank’s
Questions?

More Related Content

What's hot

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
 
Silex meets SOAP & REST
Silex meets SOAP & RESTSilex meets SOAP & REST
Silex meets SOAP & RESTHugo Hamon
 
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you need
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you needDutch PHP Conference - PHPSpec 2 - The only Design Tool you need
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you needKacper Gunia
 
Symfony CoP: Form component
Symfony CoP: Form componentSymfony CoP: Form component
Symfony CoP: Form componentSamuel ROZE
 
Event sourcing w PHP (by Piotr Kacała)
Event sourcing w PHP (by Piotr Kacała)Event sourcing w PHP (by Piotr Kacała)
Event sourcing w PHP (by Piotr Kacała)GOG.com dev team
 
The IoC Hydra - Dutch PHP Conference 2016
The IoC Hydra - Dutch PHP Conference 2016The IoC Hydra - Dutch PHP Conference 2016
The IoC Hydra - Dutch PHP Conference 2016Kacper Gunia
 
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
 
Database Design Patterns
Database Design PatternsDatabase Design Patterns
Database Design PatternsHugo Hamon
 
The History of PHPersistence
The History of PHPersistenceThe History of PHPersistence
The History of PHPersistenceHugo Hamon
 
Doctrine fixtures
Doctrine fixturesDoctrine fixtures
Doctrine fixturesBill Chang
 
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
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For BeginnersJonathan Wage
 
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
 
How Kris Writes Symfony Apps
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony AppsKris Wallsmith
 
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
 
How I started to love design patterns
How I started to love design patternsHow I started to love design patterns
How I started to love design patternsSamuel ROZE
 
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
 

What's hot (20)

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
 
Min-Maxing Software Costs
Min-Maxing Software CostsMin-Maxing Software Costs
Min-Maxing Software Costs
 
Silex meets SOAP & REST
Silex meets SOAP & RESTSilex meets SOAP & REST
Silex meets SOAP & REST
 
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you need
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you needDutch PHP Conference - PHPSpec 2 - The only Design Tool you need
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you need
 
The IoC Hydra
The IoC HydraThe IoC Hydra
The IoC Hydra
 
Symfony CoP: Form component
Symfony CoP: Form componentSymfony CoP: Form component
Symfony CoP: Form component
 
Event sourcing w PHP (by Piotr Kacała)
Event sourcing w PHP (by Piotr Kacała)Event sourcing w PHP (by Piotr Kacała)
Event sourcing w PHP (by Piotr Kacała)
 
The IoC Hydra - Dutch PHP Conference 2016
The IoC Hydra - Dutch PHP Conference 2016The IoC Hydra - Dutch PHP Conference 2016
The IoC Hydra - Dutch PHP Conference 2016
 
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
 
Database Design Patterns
Database Design PatternsDatabase Design Patterns
Database Design Patterns
 
The History of PHPersistence
The History of PHPersistenceThe History of PHPersistence
The History of PHPersistence
 
Doctrine fixtures
Doctrine fixturesDoctrine fixtures
Doctrine fixtures
 
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!
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For Beginners
 
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
 
How Kris Writes Symfony Apps
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony Apps
 
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
 
How I started to love design patterns
How I started to love design patternsHow I started to love design patterns
How I started to love design patterns
 
Introduction to Zend Framework web services
Introduction to Zend Framework web servicesIntroduction to Zend Framework web services
Introduction to Zend Framework web services
 
Matters of State
Matters of StateMatters of State
Matters of State
 

Viewers also liked

Refactorizando Pccomponentes.com con Symfony
Refactorizando Pccomponentes.com con SymfonyRefactorizando Pccomponentes.com con Symfony
Refactorizando Pccomponentes.com con SymfonyMario Marín
 
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
 
Learning from the Ulabox stack
Learning from the Ulabox stackLearning from the Ulabox stack
Learning from the Ulabox stackRubén Sospedra
 
DDD on example of Symfony (SfCampUA14)
DDD on example of Symfony (SfCampUA14)DDD on example of Symfony (SfCampUA14)
DDD on example of Symfony (SfCampUA14)Oleg Zinchenko
 
Symfony 2 : Performances et Optimisations
Symfony 2 : Performances et OptimisationsSymfony 2 : Performances et Optimisations
Symfony 2 : Performances et OptimisationsLes-Tilleuls.coop
 
Microservices with Apache Camel, DDD, and Kubernetes
Microservices with Apache Camel, DDD, and KubernetesMicroservices with Apache Camel, DDD, and Kubernetes
Microservices with Apache Camel, DDD, and KubernetesChristian Posta
 
Consumer Driven Contracts (DDD Perth 2016)
Consumer Driven Contracts (DDD Perth 2016)Consumer Driven Contracts (DDD Perth 2016)
Consumer Driven Contracts (DDD Perth 2016)Rob Crowley
 
Designing APIs and Microservices Using Domain-Driven Design
Designing APIs and Microservices Using Domain-Driven DesignDesigning APIs and Microservices Using Domain-Driven Design
Designing APIs and Microservices Using Domain-Driven DesignLaunchAny
 
Architecting Microservices in .Net
Architecting Microservices in .NetArchitecting Microservices in .Net
Architecting Microservices in .NetRichard Banks
 
Using the Actor Model with Domain-Driven Design (DDD) in Reactive Systems - w...
Using the Actor Model with Domain-Driven Design (DDD) in Reactive Systems - w...Using the Actor Model with Domain-Driven Design (DDD) in Reactive Systems - w...
Using the Actor Model with Domain-Driven Design (DDD) in Reactive Systems - w...Lightbend
 
Scala/Scrum/DDD 困ったこと50連発ガトリングトーク!!
Scala/Scrum/DDD 困ったこと50連発ガトリングトーク!!Scala/Scrum/DDD 困ったこと50連発ガトリングトーク!!
Scala/Scrum/DDD 困ったこと50連発ガトリングトーク!!Yasuyuki Sugitani
 
RDRA DDD Agile
RDRA DDD AgileRDRA DDD Agile
RDRA DDD Agile増田 亨
 

Viewers also liked (13)

Refactorizando Pccomponentes.com con Symfony
Refactorizando Pccomponentes.com con SymfonyRefactorizando Pccomponentes.com con Symfony
Refactorizando Pccomponentes.com con Symfony
 
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)
 
Microservices
MicroservicesMicroservices
Microservices
 
Learning from the Ulabox stack
Learning from the Ulabox stackLearning from the Ulabox stack
Learning from the Ulabox stack
 
DDD on example of Symfony (SfCampUA14)
DDD on example of Symfony (SfCampUA14)DDD on example of Symfony (SfCampUA14)
DDD on example of Symfony (SfCampUA14)
 
Symfony 2 : Performances et Optimisations
Symfony 2 : Performances et OptimisationsSymfony 2 : Performances et Optimisations
Symfony 2 : Performances et Optimisations
 
Microservices with Apache Camel, DDD, and Kubernetes
Microservices with Apache Camel, DDD, and KubernetesMicroservices with Apache Camel, DDD, and Kubernetes
Microservices with Apache Camel, DDD, and Kubernetes
 
Consumer Driven Contracts (DDD Perth 2016)
Consumer Driven Contracts (DDD Perth 2016)Consumer Driven Contracts (DDD Perth 2016)
Consumer Driven Contracts (DDD Perth 2016)
 
Designing APIs and Microservices Using Domain-Driven Design
Designing APIs and Microservices Using Domain-Driven DesignDesigning APIs and Microservices Using Domain-Driven Design
Designing APIs and Microservices Using Domain-Driven Design
 
Architecting Microservices in .Net
Architecting Microservices in .NetArchitecting Microservices in .Net
Architecting Microservices in .Net
 
Using the Actor Model with Domain-Driven Design (DDD) in Reactive Systems - w...
Using the Actor Model with Domain-Driven Design (DDD) in Reactive Systems - w...Using the Actor Model with Domain-Driven Design (DDD) in Reactive Systems - w...
Using the Actor Model with Domain-Driven Design (DDD) in Reactive Systems - w...
 
Scala/Scrum/DDD 困ったこと50連発ガトリングトーク!!
Scala/Scrum/DDD 困ったこと50連発ガトリングトーク!!Scala/Scrum/DDD 困ったこと50連発ガトリングトーク!!
Scala/Scrum/DDD 困ったこと50連発ガトリングトーク!!
 
RDRA DDD Agile
RDRA DDD AgileRDRA DDD Agile
RDRA DDD Agile
 

Similar to Decoupling Ulabox.com monolith from CRUD to DDD

WordPress REST API hacking
WordPress REST API hackingWordPress REST API hacking
WordPress REST API hackingJeroen van Dijk
 
Refactoring using Codeception
Refactoring using CodeceptionRefactoring using Codeception
Refactoring using CodeceptionJeroen van Dijk
 
Tidy Up Your Code
Tidy Up Your CodeTidy Up Your Code
Tidy Up Your CodeAbbas Ali
 
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...ZFConf Conference
 
Unit testing after Zend Framework 1.8
Unit testing after Zend Framework 1.8Unit testing after Zend Framework 1.8
Unit testing after Zend Framework 1.8Michelangelo van Dam
 
PHPSpec - the only Design Tool you need - 4Developers
PHPSpec - the only Design Tool you need - 4DevelopersPHPSpec - the only Design Tool you need - 4Developers
PHPSpec - the only Design Tool you need - 4DevelopersKacper Gunia
 
Code moi une RH! (PHP tour 2017)
Code moi une RH! (PHP tour 2017)Code moi une RH! (PHP tour 2017)
Code moi une RH! (PHP tour 2017)Arnaud Langlade
 
PHPUnit elevato alla Symfony2
PHPUnit elevato alla Symfony2PHPUnit elevato alla Symfony2
PHPUnit elevato alla Symfony2eugenio pombi
 
Crafting beautiful software
Crafting beautiful softwareCrafting beautiful software
Crafting beautiful softwareJorn Oomen
 
Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)Fabien Potencier
 
14. CodeIgniter adaugarea inregistrarilor
14. CodeIgniter adaugarea inregistrarilor14. CodeIgniter adaugarea inregistrarilor
14. CodeIgniter adaugarea inregistrarilorRazvan Raducanu, PhD
 
WordPress REST API hacking
WordPress REST API hackingWordPress REST API hacking
WordPress REST API hackingJeroen van Dijk
 
Symfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologySymfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologyDaniel Knell
 

Similar to Decoupling Ulabox.com monolith from CRUD to DDD (20)

WordPress REST API hacking
WordPress REST API hackingWordPress REST API hacking
WordPress REST API hacking
 
Refactoring using Codeception
Refactoring using CodeceptionRefactoring using Codeception
Refactoring using Codeception
 
Symfony tips and tricks
Symfony tips and tricksSymfony tips and tricks
Symfony tips and tricks
 
Tidy Up Your Code
Tidy Up Your CodeTidy Up Your Code
Tidy Up Your Code
 
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...
 
Zend framework service
Zend framework serviceZend framework service
Zend framework service
 
Zend framework service
Zend framework serviceZend framework service
Zend framework service
 
Unit testing after Zend Framework 1.8
Unit testing after Zend Framework 1.8Unit testing after Zend Framework 1.8
Unit testing after Zend Framework 1.8
 
Chekout demistified
Chekout demistifiedChekout demistified
Chekout demistified
 
PHPSpec - the only Design Tool you need - 4Developers
PHPSpec - the only Design Tool you need - 4DevelopersPHPSpec - the only Design Tool you need - 4Developers
PHPSpec - the only Design Tool you need - 4Developers
 
Code moi une RH! (PHP tour 2017)
Code moi une RH! (PHP tour 2017)Code moi une RH! (PHP tour 2017)
Code moi une RH! (PHP tour 2017)
 
Angular Workshop_Sarajevo2
Angular Workshop_Sarajevo2Angular Workshop_Sarajevo2
Angular Workshop_Sarajevo2
 
PHPUnit elevato alla Symfony2
PHPUnit elevato alla Symfony2PHPUnit elevato alla Symfony2
PHPUnit elevato alla Symfony2
 
Crafting beautiful software
Crafting beautiful softwareCrafting beautiful software
Crafting beautiful software
 
Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)
 
14. CodeIgniter adaugarea inregistrarilor
14. CodeIgniter adaugarea inregistrarilor14. CodeIgniter adaugarea inregistrarilor
14. CodeIgniter adaugarea inregistrarilor
 
Migrare da symfony 1 a Symfony2
 Migrare da symfony 1 a Symfony2  Migrare da symfony 1 a Symfony2
Migrare da symfony 1 a Symfony2
 
WordPress REST API hacking
WordPress REST API hackingWordPress REST API hacking
WordPress REST API hacking
 
Symfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologySymfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technology
 
Twitter codeigniter library
Twitter codeigniter libraryTwitter codeigniter library
Twitter codeigniter library
 

Recently uploaded

What is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWhat is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWave PLM
 
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...MyIntelliSource, Inc.
 
Implementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureImplementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureDinusha Kumarasiri
 
buds n tech IT solutions
buds n  tech IT                solutionsbuds n  tech IT                solutions
buds n tech IT solutionsmonugehlot87
 
why an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfwhy an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfjoe51371421
 
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
 
Building Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop SlideBuilding Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop SlideChristina Lin
 
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样umasea
 
Unit 1.1 Excite Part 1, class 9, cbse...
Unit 1.1 Excite Part 1, class 9, cbse...Unit 1.1 Excite Part 1, class 9, cbse...
Unit 1.1 Excite Part 1, class 9, cbse...aditisharan08
 
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...kellynguyen01
 
What are the features of Vehicle Tracking System?
What are the features of Vehicle Tracking System?What are the features of Vehicle Tracking System?
What are the features of Vehicle Tracking System?Watsoo Telematics
 
EY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityEY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityNeo4j
 
The Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdf
The Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdfThe Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdf
The Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdfkalichargn70th171
 
Salesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantSalesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantAxelRicardoTrocheRiq
 
The Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdfThe Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdfPower Karaoke
 
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
 
Intelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmIntelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmSujith Sukumaran
 
Project Based Learning (A.I).pptx detail explanation
Project Based Learning (A.I).pptx detail explanationProject Based Learning (A.I).pptx detail explanation
Project Based Learning (A.I).pptx detail explanationkaushalgiri8080
 
What is Binary Language? Computer Number Systems
What is Binary Language?  Computer Number SystemsWhat is Binary Language?  Computer Number Systems
What is Binary Language? Computer Number SystemsJheuzeDellosa
 

Recently uploaded (20)

What is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWhat is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need It
 
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
 
Implementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureImplementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with Azure
 
buds n tech IT solutions
buds n  tech IT                solutionsbuds n  tech IT                solutions
buds n tech IT solutions
 
why an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfwhy an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdf
 
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...
 
Building Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop SlideBuilding Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
 
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
 
Unit 1.1 Excite Part 1, class 9, cbse...
Unit 1.1 Excite Part 1, class 9, cbse...Unit 1.1 Excite Part 1, class 9, cbse...
Unit 1.1 Excite Part 1, class 9, cbse...
 
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
 
What are the features of Vehicle Tracking System?
What are the features of Vehicle Tracking System?What are the features of Vehicle Tracking System?
What are the features of Vehicle Tracking System?
 
EY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityEY_Graph Database Powered Sustainability
EY_Graph Database Powered Sustainability
 
The Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdf
The Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdfThe Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdf
The Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdf
 
Salesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantSalesforce Certified Field Service Consultant
Salesforce Certified Field Service Consultant
 
The Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdfThe Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdf
 
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
 
Intelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmIntelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalm
 
Project Based Learning (A.I).pptx detail explanation
Project Based Learning (A.I).pptx detail explanationProject Based Learning (A.I).pptx detail explanation
Project Based Learning (A.I).pptx detail explanation
 
Call Girls In Mukherjee Nagar 📱 9999965857 🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SE...
Call Girls In Mukherjee Nagar 📱  9999965857  🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SE...Call Girls In Mukherjee Nagar 📱  9999965857  🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SE...
Call Girls In Mukherjee Nagar 📱 9999965857 🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SE...
 
What is Binary Language? Computer Number Systems
What is Binary Language?  Computer Number SystemsWhat is Binary Language?  Computer Number Systems
What is Binary Language? Computer Number Systems
 

Decoupling Ulabox.com monolith from CRUD to DDD

  • 2. Aleix Vergés Backend developer Scrum Master @avergess aleix.verges@ulabox.com
  • 4.
  • 5.
  • 6.
  • 7.
  • 9. /** * @param Cart $cart * @return Order $order */ public function createOrder(Cart $cart) { // Year 2011 $order = $this->moveCartToOrder($cart); $this->sendConfirmationEmail($order); $this->reindexSolr($order); $this->sendToWarehouse($order); // Year 2012 $this->sendToFinantialErp($order); $this->sendDonationEmail($order); // Year 2014 $this->sendToDeliveryRoutingSoftware($order); // Year 2015 $this->sendToJustInTimeSuppliers($order); // Year 2016 $this->sendToWarehouseSpoke($order); // WTF! $this->sendToShipLoadSoftware($order); // WTF!!!! } Decoupling Ulabox.com monolith. From CRUD to DDD Our problem
  • 10. WTF!
  • 11. Past ● CRUD doesn’t make sense anymore ● It had sense at the beginning ● Product, Logistic, Delivery, Cart, Customers, ... ● It’s not sustainable. Decoupling Ulabox.com monolith. From CRUD to DDD
  • 12. Our solution /** * @param Cart $cart */ public function createOrder(Cart $cart) { $createOrder = new CreateOrderCommand(Cart $cart); $this->commandBus->dispatch($createOrder) } Event bus CreateOrder command OrderWasCreated event subscribe subscribe subscribe subscribe subscribe subscribe subscribe subscribe subscribe Decoupling Ulabox.com monolith. From CRUD to DDD
  • 14. ● Domain ● Aggregate / Aggregate Root ● Repository ● Domain Events ● Service ● Command Bus ● Event Bus * Domain Drive Design: https://en.wikipedia.org/wiki/Domain-driven_design Decoupling Ulabox.com monolith. From CRUD to DDD
  • 16. Refactoring and manage technical debt is not a choice, but a responsability Decoupling Ulabox.com monolith. From CRUD to DDD
  • 20. class OrderController extends BaseController { public function refundAction(Request $request, $id) { $em = $this->container->get('doctrine.orm.entity_manager'); $orderPayment = $em->getRepository('UlaboxCoreBundle:OrderPayment')->find($id); $amount = $request->request->get('refund'); $data = $this->container->get('sermepa')->processRefund($orderPayment, $amount); $orderRefund = new OrderPayment(); $orderRefund->setAmount($amount); ... $em->persist($orderRefund); $em->flush(); return $this->redirectToRoute('order_show', ['id' => $orderPayment->getOrder()->getId()]); } public function someOtherAction(Request $request, $id) ... }
  • 21. Decoupling Ulabox.com monolith. From CRUD to DDD Problems ● Hidden dependencies ● Inheritance. ● Biz logic in the controller. ● Non aggregate root. ● Difficult to test.
  • 22. * Dependency Injection: https://es.wikipedia.org/wiki/Inyecci%C3%B3n_de_dependencias Decoupling Ulabox.com monolith. From CRUD to DDD Solutions ● Dependency Injection ● Break inheritance from base controller. ● Application services. ● Testing
  • 23. 5.2. Controller as a service 5. Controllers
  • 24. # services.yml imports: - { resource: controllers.yml } # controllers.yml ulabox_ulaoffice.controllers.order: class: UlaboxUlaofficeBundleControllerOrderController arguments: - '@refund' - '@router' ...
  • 26. /** * @Route("/orders", service="ulabox_ulaoffice.controllers.order") */ class OrderController { /** * @param Refund $refund * @param RouterInterface $router */ public function __construct(Refund $refund, RouterInterface $router, ……..) { $this->refund = $refund; $this->router = $router; ... } }
  • 27. 5. Controllers 5.4. Delegate logic to services
  • 28. /** * @Route("/orders", service="ulabox_ulaoffice.controllers.order") */ class OrderController { public function refundAction(Request $request, $id) { $amount = $request->request->get('refund'); $method = $request->request->get('method'); $orderId = $request->request->get('order_id'); try { $this->refund->execute($orderId, $id, (float)$amount, $method); $this->session->getFlashBag()->add('success', 'Refund has been processed correctly'); } catch (Exception $e) { $this->session->getFlashBag()->add('danger', $e->getMessage()); } return new RedirectResponse($this->router->generate('order_show', ['id' => $orderId])); } }
  • 30. class OrderControllerTest extends PHPUnit_Framework_TestCase { public function setUp() { $this->refund = $this->prophesize(Refund::class); $this->router = $this->prophesize(RouterInterface::class); $this->orderController = new OrderController( $this->refund->reveal(), $this->router->reveal() ); } ... }
  • 31. class OrderControllerTest extends PHPUnit_Framework_TestCase { ... public function testShouldDelegateOrderRefund() { $orderPaymentId = 34575; $amount = 10.95; $orderId = 12345; $orderRoute = 'some/route'; $request = $this->mockRequest($orderId, $orderPaymentId, $amount, $orderRoute); $this->refund ->execute($orderId, $orderPaymentId, $amount, PaymentPlatform::REDSYS) ->shouldBeCalled(); $this->router->generate('order_show', ['id' => $orderId])->willReturn($orderRoute); $actual = $this->orderController->refundAction($request->reveal(), $orderPaymentId); $this->assertEquals(new RedirectResponse($orderRoute), $actual); } }
  • 34. 6. Symfony Forms 6.1. Anemic Model
  • 35. class OrderController extends BaseController { public function rescheduleAction(Request $request, $id) { $order = $this->container->get('order')->reposition($id); $form = $this->createForm(new OrderType(), $order); $form->handleRequest($request); if ($form->isValid()) { $em = $this->getDoctrine()->getManager(); $em->persist($order); $em->flush(); $request->getSession()->getFlashBag()->add('success', 'Your changes were saved!'); return $this->redirect($this->generateUrl('reschedule_success')); } return ['entity' => $entity, 'form' => $form->createView()]; } }
  • 36. Decoupling Ulabox.com monolith. From CRUD to DDD Problems ● Coupling between entities and Symfony Forms. ● Anemic Model. ● Intention?
  • 37. Decoupling Ulabox.com monolith. From CRUD to DDD Solutions ● Use of DTO/Command ● Reflect the Intention! ● Rich Domain. ● Testing.
  • 38. 6. Symfony Forms 6.2. Command !== CLI Command
  • 39. class Reschedule { public $orderId; public $addressId; public $slotVars; public $comments; public function __construct($orderId, $addressId, $slotVars, $comments) { $this->orderId = $orderId; $this->addressId = $addressId; $this->slotVars = $slotVars; $this->comments = $comments; } }
  • 40. 6. Symfony Forms 6.3. Building the Form
  • 41. class OrderController extends BaseController { public function rescheduleDisplayingAction(Request $request, $id) { $order = $this->orderRepository->get($id); $address = $order->deliveryAddress()->asAddress(); $rescheduleOrder = Reschedule::fromPayload([ 'order_id' => $order->getId(), 'address_id' => $address->getId(), 'slot_vars' => $order->deliverySlotVars(), 'comments' => $order->deliveryComments(), ]); $rescheduleForm = $this->formFactory->create(OrderRescheduleType::class, $rescheduleOrder); return ['order' => $order, 'form' => $rescheduleForm->createView()]; } }
  • 42. 6. Symfony Forms 6.4. Submitting the Form
  • 43. class OrderController extends BaseController { public function rescheduleUpdateAction(Request $request, $id) { $requestData = $request->get('order_reschedule'); $rescheduleOrder = Reschedule::fromPayload([ 'order_id' => $id, 'address_id' => $requestData['addressId'], 'slot_vars' => $requestData['slotVars'], 'comments' => $requestData['comments'], ]); $rescheduleForm = $this->formFactory->create(OrderRescheduleType::class, $rescheduleOrder); if ($rescheduleForm->isValid()) { $this->commandBus->dispatch($rescheduleOrder); } return new RedirectResponse($this->router->generate($this->entity Properties['route'])); } }
  • 45. class OrderControllerTest extends PHPUnit_Framework_TestCase { public function testShouldDelegateOrderRescheduleToCommandBus() { $orderId = 12345; $addressId = 6789; $slotVars = '2016-03-25|523|2|15'; $comments = 'some comments'; $expectedRoute = 'http://some.return.url'; $request = $this->mockRequest($orderId, $addressId, $slotVars, $comments); $form = $this->mockForm(); $form->isValid()->willReturn(true); $this->router->generate('order')->willReturn($expectedRoute); $this->commandBus->dispatch(Argument::type(Reschedule::class))->shouldBeCalled(); $actual = $this->orderController->rescheduleUpdateAction($request->reveal(), $orderId); $this->assertEquals(new RedirectResponse($expectedRoute), $actual); } }
  • 46. 7. From CRUD to DDD
  • 47. 7. From CRUD to DDD 7.1. Summing...
  • 48. class OrderController extends BaseController { public function rescheduleUpdateAction(Request $request, $id) { $requestData = $request->get('order_reschedule'); $rescheduleOrder = Reschedule::fromPayload([ 'order_id' => $id, 'address_id' => $requestData['addressId'], 'slot_vars' => $requestData['slotVars'], 'comments' => $requestData['comments'], ]); $rescheduleForm = $this->formFactory->create(OrderRescheduleType::class, $rescheduleOrder); if ($rescheduleForm>isValid()) { $this->commandBus->dispatch($rescheduleOrder); } return new RedirectResponse($this->router->generate($this->entity Properties['route'])); } }
  • 49. 7. From CRUD to DDD 7.2. Handling
  • 50. class RescheduleHandler extends CommandHandler { public function __construct( ... ) { ... } public function handleReschedule(Reschedule $rescheduleOrder) { $timeLineSlot = $this->slotManager->createTimelineSlotFromVars($rescheduleOrder->slotVars); $order = $this->orderRepository->get($rescheduleOrder->aggregateId); $delivery = $order->getOrderDelivery(); $delivery->setSlot($timeLineSlot->getSlot()); $delivery->setLoadTime($timeLineSlot->getLoadTime()); $delivery->setShift($timeLineSlot->getShift()->getShift()); ... $order->rescheduleDelivery($delivery); $this->orderRepository->save($order); $this->eventBus->publish($order->getUncommittedEvents()); } }
  • 51. Decoupling Ulabox.com monolith. From CRUD to DDD Problems ● Biz logic out of domain. ● Aggregate access. ● Aggregate Root? ● Unprotected Domain.
  • 52. Decoupling Ulabox.com monolith. From CRUD to DDD Solutions ● Aggregate Root. Order or Delivery? ● Unique acces point to the domain. ● Clear intention!! ● Testing.
  • 53. 7. From CRUD to DDD 7.3. Order or Delivery?
  • 54.
  • 55. 7. From CRUD to DDD 7.4. Aggregate access point
  • 56. class RescheduleHandler extends CommandHandler { public function __construct( ... ) { ... } public function handleReschedule(Reschedule $rescheduleDelivery) { $timeLineSlot = $this->slotManager->createTimelineSlotFromVars($rescheduleDelivery->slotVars); $delivery = $this->deliveryRepository->get($rescheduleDelivery->deliveryId); $delivery->reschedule($timeLineSlot); $this->deliveryRepository->save($delivery); $this->eventBus->publish($delivery->getUncommittedEvents()); } }
  • 57. 7. From CRUD to DDD 7.5. Business logic
  • 58. class Delivery implements AggregateRoot { public function reschedule(TimelineSlot $timelineSlot) { $this->setDate($timelineSlot->getDate()); $this->setLoadTime($timelineSlot->getLoadTime()); $this->setSlot($timelineSlot->getSlot()); $this->setShift($timelineSlot->getShift()); $this->setLoad($timelineSlot->getLoad()); $this->setPreparation($timelineSlot->getPreparationDate()); $this->apply( new DeliveryWasRescheduled( $this->getAggregateRootId(), $this->getProgrammedDate(), $this->getTimeStart(), $this->getTimeEnd(), $this->getLoad()->spokeId() ) ); } }
  • 59. 7. From CRUD to DDD 7.6. Unit test
  • 60. class RescheduleHandlerTest extends PHPUnit_Framework_TestCase { public function testShouldRescheduleDelivery() { $deliveryId = 12345; $slotVars = '2016-03-25|523|2|15'; $timeLineSlot = TimelineSlotStub::random(); $delivery = $this->prophesize(Delivery::class); $this->deliveryRepository->get($deliveryId)->willReturn($delivery); $this->slotManager->createTimelineSlotFromVars($slotVars)->willReturn($timeLineSlot); $delivery->reschedule($timeLineSlot)->shouldBeCalled(); $this->deliveryRepository->save($delivery)->shouldBeCalled(); $this->eventBus->publish($this->expectedEvents())->shouldBeCalled(); $this->rescheduleOrderHandler->handleReschedule(new Reschedule($deliveryId, $slotVars)); } }
  • 61. class DeliveryTest extends PHPUnit_Framework_TestCase { public function testShouldRescheduleDelivery() { $delivery = OrderDeliveryStub::random(); $timeLineSlot = TimelineSlotStub::random(); $delivery->reschedule($timeLineSlot); static::assertEquals($timeLineSlot->getDate(), $delivery->getProgrammedDate()); static::assertEquals($timeLineSlot->getLoadTime(), $delivery->getLoadTime()); static::assertEquals($timeLineSlot->getSlot(), $delivery->getSlot()); static::assertEquals($timeLineSlot->getShift(), $delivery->getShift()); static::assertEquals($timeLineSlot->getPreparationDate(), $delivery->getPreparation()); $messageIterator = $delivery->getUncommittedEvents()->getIterator(); $this->assertInstanceOf( DeliveryWasRescheduled::class, $messageIterator->current()->getPayload() ); } }
  • 62. 7. From CRUD to DDD 7.7. Domain event
  • 64. 8. Aggregates and Repositories
  • 66. 8. Aggregates and Repositories 8.1. Entity / Repository
  • 67. class CustomerCreditcardModel { public function add($number, $type, $token = null, $expiryDate = null) { $customer = $this->tokenStorage->getToken()->getUser(); $creditCard = new CustomerCreditcard(); $creditCard->setNumber($number); $creditCard->setCustomer($customer); $creditCard->setType($type); $creditCard->setToken($token); $creditCard->setExpiryDate($expiryDate); $this->creditCardRepository->add($creditCard); return $creditCard; } }
  • 68. Decoupling Ulabox.com monolith. From CRUD to DDD Problems ● Aggregate? ● CreditCardRepository??? ● Unprotected Domain.
  • 69. Decoupling Ulabox.com monolith. From CRUD to DDD Solutions ● Which is the Aggregate? ● What’s the Intention? ● Testing
  • 70. Customer CreditCard class Customer implements AggregateRoot { public function addCreditCard($number, $type, $token = '', $expiryDate = '') { $creditCard = CustomerCreditcard::create($number, $type, $token, $expiryDate); $this->creditCards->add($creditCard); $this->apply(new CreditCardWasRegistered($this->getAggregateRootId(), $number)); } } Decoupling Ulabox.com monolith. From CRUD to DDD
  • 71. 8. Aggregates and Repositories 8.2. RegisterCreditCard
  • 72. class RegisterCreditCard { public $customerId; public $cardNumber; public $type; public $token; public $expiry; public function __construct($customerId, $cardNumber, $type, $token, $expiry) { $this->customerId = $customerId; $this->cardNumber = $cardNumber; $this->type = $type; $this->token = $token; $this->expiry = $expiry; } }
  • 73. 8. Aggregates and Repositories 8.3. RegisterCreditCardHandler
  • 74. class RegisterCreditCardHandler extends CommandHandler { private $customerRepository; private $eventBus; public function __construct( ... ) { ... } public function handleRegisterCreditCard(RegisterCreditCard $registerCreditCard) { $customer = $this->customerRepository->get($registerCreditCard->customerId()) $customer->addCreditCard( $registerCreditCard->cardNumber(), $registerCreditCard->type(), $registerCreditCard->token(), $registerCreditCard->expiry() ); $this->customerRepository->save($customer); $this->eventBus->publish($customer->getUncommittedEvents()); } }
  • 75. 8. Aggregates and Repositories 8.4. Business rules
  • 76. class Customer implements AggregateRoot { public function addCreditCard($number, $type, $token, $expiryDate) { if ($this->creditCardExists($number, $type)) { $this->renewCreditCard($number, $type, $token, $expiryDate); return; } $creditCard = CustomerCreditcard::create($number, $type, $token, $expiryDate); $this->creditCards->add($creditCard); $this->apply(new CreditCardWasRegistered($this->getAggregateRootId(), $number)); } private function renewCreditCard($number, $type, $token, $expiryDate) { ... } }
  • 78. This is not a Big-Bang Decoupling Ulabox.com monolith. From CRUD to DDD
  • 79. Aggregate Election Decoupling Ulabox.com monolith. From CRUD to DDD
  • 82. ¡¡¡Be a Professional!!! Decoupling Ulabox.com monolith. From CRUD to DDD