SlideShare a Scribd company logo
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 Pimple
Hugo Hamon
 
Min-Maxing Software Costs
Min-Maxing Software CostsMin-Maxing Software Costs
Min-Maxing Software Costs
Konstantin Kudryashov
 
Silex meets SOAP & REST
Silex meets SOAP & RESTSilex meets SOAP & REST
Silex meets SOAP & REST
Hugo 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 need
Kacper Gunia
 
The IoC Hydra
The IoC HydraThe IoC Hydra
The IoC Hydra
Kacper Gunia
 
Symfony CoP: Form component
Symfony CoP: Form componentSymfony CoP: Form component
Symfony CoP: Form component
Samuel 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 2016
Kacper 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 @codelytv
CodelyTV
 
Database Design Patterns
Database Design PatternsDatabase Design Patterns
Database Design Patterns
Hugo Hamon
 
The History of PHPersistence
The History of PHPersistenceThe History of PHPersistence
The History of PHPersistence
Hugo 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 Beginners
Jonathan 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 2015
Konstantin Kudryashov
 
How Kris Writes Symfony Apps
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony Apps
Kris 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 Hobgoblins
Ross 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 patterns
Samuel ROZE
 
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
 
Matters of State
Matters of StateMatters of State
Matters of State
Kris Wallsmith
 

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 Symfony
Mario 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
 
Microservices
MicroservicesMicroservices
Learning from the Ulabox stack
Learning from the Ulabox stackLearning from the Ulabox stack
Learning from the Ulabox stack
Rubé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 Optimisations
Les-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 Kubernetes
Christian 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 Design
LaunchAny
 
Architecting Microservices in .Net
Architecting Microservices in .NetArchitecting Microservices in .Net
Architecting Microservices in .Net
Richard 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 the Ulabox.com monolith. From CRUD to DDD

WordPress REST API hacking
WordPress REST API hackingWordPress REST API hacking
WordPress REST API hacking
Jeroen van Dijk
 
Refactoring using Codeception
Refactoring using CodeceptionRefactoring using Codeception
Refactoring using Codeception
Jeroen van Dijk
 
Symfony tips and tricks
Symfony tips and tricksSymfony tips and tricks
Symfony tips and tricks
Mariusz Kozłowski
 
Tidy Up Your Code
Tidy Up Your CodeTidy Up Your Code
Tidy Up Your Code
Abbas 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
 
Zend framework service
Zend framework serviceZend framework service
Zend framework service
Michelangelo van Dam
 
Zend framework service
Zend framework serviceZend framework service
Zend framework service
Michelangelo van Dam
 
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
Michelangelo van Dam
 
Chekout demistified
Chekout demistifiedChekout demistified
Chekout demistified
Damijan Ćavar
 
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
Kacper 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 Symfony2
eugenio pombi
 
Crafting beautiful software
Crafting beautiful softwareCrafting beautiful software
Crafting beautiful software
Jorn 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 inregistrarilor
Razvan Raducanu, PhD
 
WordPress REST API hacking
WordPress REST API hackingWordPress REST API hacking
WordPress REST API hacking
Jeroen van Dijk
 
Symfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologySymfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technology
Daniel Knell
 

Similar to Decoupling the 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

Developing Distributed High-performance Computing Capabilities of an Open Sci...
Developing Distributed High-performance Computing Capabilities of an Open Sci...Developing Distributed High-performance Computing Capabilities of an Open Sci...
Developing Distributed High-performance Computing Capabilities of an Open Sci...
Globus
 
Corporate Management | Session 3 of 3 | Tendenci AMS
Corporate Management | Session 3 of 3 | Tendenci AMSCorporate Management | Session 3 of 3 | Tendenci AMS
Corporate Management | Session 3 of 3 | Tendenci AMS
Tendenci - The Open Source AMS (Association Management Software)
 
Field Employee Tracking System| MiTrack App| Best Employee Tracking Solution|...
Field Employee Tracking System| MiTrack App| Best Employee Tracking Solution|...Field Employee Tracking System| MiTrack App| Best Employee Tracking Solution|...
Field Employee Tracking System| MiTrack App| Best Employee Tracking Solution|...
informapgpstrackings
 
Explore Modern SharePoint Templates for 2024
Explore Modern SharePoint Templates for 2024Explore Modern SharePoint Templates for 2024
Explore Modern SharePoint Templates for 2024
Sharepoint Designs
 
SOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar Research Team: Latest Activities of IntelBrokerSOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar
 
Lecture 1 Introduction to games development
Lecture 1 Introduction to games developmentLecture 1 Introduction to games development
Lecture 1 Introduction to games development
abdulrafaychaudhry
 
Large Language Models and the End of Programming
Large Language Models and the End of ProgrammingLarge Language Models and the End of Programming
Large Language Models and the End of Programming
Matt Welsh
 
Understanding Globus Data Transfers with NetSage
Understanding Globus Data Transfers with NetSageUnderstanding Globus Data Transfers with NetSage
Understanding Globus Data Transfers with NetSage
Globus
 
Prosigns: Transforming Business with Tailored Technology Solutions
Prosigns: Transforming Business with Tailored Technology SolutionsProsigns: Transforming Business with Tailored Technology Solutions
Prosigns: Transforming Business with Tailored Technology Solutions
Prosigns
 
How to Position Your Globus Data Portal for Success Ten Good Practices
How to Position Your Globus Data Portal for Success Ten Good PracticesHow to Position Your Globus Data Portal for Success Ten Good Practices
How to Position Your Globus Data Portal for Success Ten Good Practices
Globus
 
How Recreation Management Software Can Streamline Your Operations.pptx
How Recreation Management Software Can Streamline Your Operations.pptxHow Recreation Management Software Can Streamline Your Operations.pptx
How Recreation Management Software Can Streamline Your Operations.pptx
wottaspaceseo
 
De mooiste recreatieve routes ontdekken met RouteYou en FME
De mooiste recreatieve routes ontdekken met RouteYou en FMEDe mooiste recreatieve routes ontdekken met RouteYou en FME
De mooiste recreatieve routes ontdekken met RouteYou en FME
Jelle | Nordend
 
Globus Connect Server Deep Dive - GlobusWorld 2024
Globus Connect Server Deep Dive - GlobusWorld 2024Globus Connect Server Deep Dive - GlobusWorld 2024
Globus Connect Server Deep Dive - GlobusWorld 2024
Globus
 
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.ILBeyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
Natan Silnitsky
 
Accelerate Enterprise Software Engineering with Platformless
Accelerate Enterprise Software Engineering with PlatformlessAccelerate Enterprise Software Engineering with Platformless
Accelerate Enterprise Software Engineering with Platformless
WSO2
 
Enhancing Research Orchestration Capabilities at ORNL.pdf
Enhancing Research Orchestration Capabilities at ORNL.pdfEnhancing Research Orchestration Capabilities at ORNL.pdf
Enhancing Research Orchestration Capabilities at ORNL.pdf
Globus
 
A Comprehensive Look at Generative AI in Retail App Testing.pdf
A Comprehensive Look at Generative AI in Retail App Testing.pdfA Comprehensive Look at Generative AI in Retail App Testing.pdf
A Comprehensive Look at Generative AI in Retail App Testing.pdf
kalichargn70th171
 
Software Testing Exam imp Ques Notes.pdf
Software Testing Exam imp Ques Notes.pdfSoftware Testing Exam imp Ques Notes.pdf
Software Testing Exam imp Ques Notes.pdf
MayankTawar1
 
How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?
How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?
How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?
XfilesPro
 
Why React Native as a Strategic Advantage for Startup Innovation.pdf
Why React Native as a Strategic Advantage for Startup Innovation.pdfWhy React Native as a Strategic Advantage for Startup Innovation.pdf
Why React Native as a Strategic Advantage for Startup Innovation.pdf
ayushiqss
 

Recently uploaded (20)

Developing Distributed High-performance Computing Capabilities of an Open Sci...
Developing Distributed High-performance Computing Capabilities of an Open Sci...Developing Distributed High-performance Computing Capabilities of an Open Sci...
Developing Distributed High-performance Computing Capabilities of an Open Sci...
 
Corporate Management | Session 3 of 3 | Tendenci AMS
Corporate Management | Session 3 of 3 | Tendenci AMSCorporate Management | Session 3 of 3 | Tendenci AMS
Corporate Management | Session 3 of 3 | Tendenci AMS
 
Field Employee Tracking System| MiTrack App| Best Employee Tracking Solution|...
Field Employee Tracking System| MiTrack App| Best Employee Tracking Solution|...Field Employee Tracking System| MiTrack App| Best Employee Tracking Solution|...
Field Employee Tracking System| MiTrack App| Best Employee Tracking Solution|...
 
Explore Modern SharePoint Templates for 2024
Explore Modern SharePoint Templates for 2024Explore Modern SharePoint Templates for 2024
Explore Modern SharePoint Templates for 2024
 
SOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar Research Team: Latest Activities of IntelBrokerSOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar Research Team: Latest Activities of IntelBroker
 
Lecture 1 Introduction to games development
Lecture 1 Introduction to games developmentLecture 1 Introduction to games development
Lecture 1 Introduction to games development
 
Large Language Models and the End of Programming
Large Language Models and the End of ProgrammingLarge Language Models and the End of Programming
Large Language Models and the End of Programming
 
Understanding Globus Data Transfers with NetSage
Understanding Globus Data Transfers with NetSageUnderstanding Globus Data Transfers with NetSage
Understanding Globus Data Transfers with NetSage
 
Prosigns: Transforming Business with Tailored Technology Solutions
Prosigns: Transforming Business with Tailored Technology SolutionsProsigns: Transforming Business with Tailored Technology Solutions
Prosigns: Transforming Business with Tailored Technology Solutions
 
How to Position Your Globus Data Portal for Success Ten Good Practices
How to Position Your Globus Data Portal for Success Ten Good PracticesHow to Position Your Globus Data Portal for Success Ten Good Practices
How to Position Your Globus Data Portal for Success Ten Good Practices
 
How Recreation Management Software Can Streamline Your Operations.pptx
How Recreation Management Software Can Streamline Your Operations.pptxHow Recreation Management Software Can Streamline Your Operations.pptx
How Recreation Management Software Can Streamline Your Operations.pptx
 
De mooiste recreatieve routes ontdekken met RouteYou en FME
De mooiste recreatieve routes ontdekken met RouteYou en FMEDe mooiste recreatieve routes ontdekken met RouteYou en FME
De mooiste recreatieve routes ontdekken met RouteYou en FME
 
Globus Connect Server Deep Dive - GlobusWorld 2024
Globus Connect Server Deep Dive - GlobusWorld 2024Globus Connect Server Deep Dive - GlobusWorld 2024
Globus Connect Server Deep Dive - GlobusWorld 2024
 
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.ILBeyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
 
Accelerate Enterprise Software Engineering with Platformless
Accelerate Enterprise Software Engineering with PlatformlessAccelerate Enterprise Software Engineering with Platformless
Accelerate Enterprise Software Engineering with Platformless
 
Enhancing Research Orchestration Capabilities at ORNL.pdf
Enhancing Research Orchestration Capabilities at ORNL.pdfEnhancing Research Orchestration Capabilities at ORNL.pdf
Enhancing Research Orchestration Capabilities at ORNL.pdf
 
A Comprehensive Look at Generative AI in Retail App Testing.pdf
A Comprehensive Look at Generative AI in Retail App Testing.pdfA Comprehensive Look at Generative AI in Retail App Testing.pdf
A Comprehensive Look at Generative AI in Retail App Testing.pdf
 
Software Testing Exam imp Ques Notes.pdf
Software Testing Exam imp Ques Notes.pdfSoftware Testing Exam imp Ques Notes.pdf
Software Testing Exam imp Ques Notes.pdf
 
How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?
How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?
How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?
 
Why React Native as a Strategic Advantage for Startup Innovation.pdf
Why React Native as a Strategic Advantage for Startup Innovation.pdfWhy React Native as a Strategic Advantage for Startup Innovation.pdf
Why React Native as a Strategic Advantage for Startup Innovation.pdf
 

Decoupling the 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