Decoupling the Ulabox.com monolith. From CRUD to DDD
-
Upload
aleix-verges -
Category
Software
-
view
818 -
download
3
Transcript of Decoupling the Ulabox.com monolith. From CRUD to DDD
![Page 1: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/1.jpg)
Decoupling Ulabox.com monolith
From CRUD to DDD
![Page 3: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/3.jpg)
1. What’s Ulabox?
![Page 4: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/4.jpg)
![Page 5: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/5.jpg)
![Page 6: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/6.jpg)
![Page 7: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/7.jpg)
![Page 8: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/8.jpg)
2. Decoupling. Why?
![Page 9: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/9.jpg)
/*** @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
![Page 10: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/10.jpg)
WTF!
![Page 11: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/11.jpg)
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
![Page 12: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/12.jpg)
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
subs
cribe
subscribe
subscribe
subscribesubscribe
subscribe
Decoupling Ulabox.com monolith. From CRUD to DDD
![Page 13: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/13.jpg)
3. The tools
![Page 14: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/14.jpg)
● 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
![Page 15: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/15.jpg)
4. A responsability question
![Page 16: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/16.jpg)
Refactoring and manage technical debt is not a choice, but a responsability
Decoupling Ulabox.com monolith. From CRUD to DDD
![Page 17: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/17.jpg)
5. Controllers
![Page 18: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/18.jpg)
REFUND!
![Page 19: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/19.jpg)
5.1. OrderController5. Controllers
![Page 20: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/20.jpg)
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)...
}
![Page 21: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/21.jpg)
Decoupling Ulabox.com monolith. From CRUD to DDD
Problems● Hidden dependencies
● Inheritance.
● Biz logic in the controller.
● Non aggregate root.
● Difficult to test.
![Page 22: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/22.jpg)
* 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
![Page 23: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/23.jpg)
5.2. Controller as a service5. Controllers
![Page 24: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/24.jpg)
# services.yml
imports: - { resource: controllers.yml }
# controllers.yml
ulabox_ulaoffice.controllers.order: class: Ulabox\UlaofficeBundle\Controller\OrderController arguments: - '@refund' - '@router' ...
![Page 25: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/25.jpg)
5. Controllers
5.3. Dependency Injection
![Page 26: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/26.jpg)
/*** @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; ... }}
![Page 27: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/27.jpg)
5. Controllers
5.4. Delegate logic to services
![Page 28: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/28.jpg)
/*** @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]));}
}
![Page 29: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/29.jpg)
5. Controllers
5.5. Unit test
![Page 30: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/30.jpg)
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()
);}
...}
![Page 31: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/31.jpg)
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);}
}
![Page 32: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/32.jpg)
6. Symfony Forms
![Page 33: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/33.jpg)
RESCHEDULE
![Page 34: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/34.jpg)
6. Symfony Forms
6.1. Anemic Model
![Page 35: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/35.jpg)
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()];}
}
![Page 36: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/36.jpg)
Decoupling Ulabox.com monolith. From CRUD to DDD
Problems● Coupling between entities and Symfony Forms.
● Anemic Model.
● Intention?
![Page 37: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/37.jpg)
Decoupling Ulabox.com monolith. From CRUD to DDD
Solutions● Use of DTO/Command
● Reflect the Intention!
● Rich Domain.
● Testing.
![Page 38: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/38.jpg)
6. Symfony Forms
6.2. Command !== CLI Command
![Page 39: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/39.jpg)
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; }}
![Page 40: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/40.jpg)
6. Symfony Forms
6.3. Building the Form
![Page 41: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/41.jpg)
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()];}
}
![Page 42: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/42.jpg)
6. Symfony Forms
6.4. Submitting the Form
![Page 43: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/43.jpg)
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'])); }}
![Page 44: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/44.jpg)
6. Symfony Forms
6.5. Unit test
![Page 45: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/45.jpg)
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);
}}
![Page 46: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/46.jpg)
7. From CRUD to DDD
![Page 47: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/47.jpg)
7. From CRUD to DDD
7.1. Summing...
![Page 48: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/48.jpg)
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'])); }}
![Page 49: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/49.jpg)
7. From CRUD to DDD
7.2. Handling
![Page 50: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/50.jpg)
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());
}}
![Page 51: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/51.jpg)
Decoupling Ulabox.com monolith. From CRUD to DDD
Problems● Biz logic out of domain.
● Aggregate access.
● Aggregate Root?
● Unprotected Domain.
![Page 52: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/52.jpg)
Decoupling Ulabox.com monolith. From CRUD to DDD
Solutions● Aggregate Root. Order or Delivery?
● Unique acces point to the domain.
● Clear intention!!
● Testing.
![Page 53: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/53.jpg)
7. From CRUD to DDD
7.3. Order or Delivery?
![Page 54: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/54.jpg)
![Page 55: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/55.jpg)
7. From CRUD to DDD
7.4. Aggregate access point
![Page 56: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/56.jpg)
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()); }}
![Page 57: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/57.jpg)
7. From CRUD to DDD
7.5. Business logic
![Page 58: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/58.jpg)
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() ) ); }}
![Page 59: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/59.jpg)
7. From CRUD to DDD
7.6. Unit test
![Page 60: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/60.jpg)
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)); }
}
![Page 61: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/61.jpg)
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());
} }
![Page 62: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/62.jpg)
7. From CRUD to DDD
7.7. Domain event
![Page 63: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/63.jpg)
DeliveryWasRescheduled
Delivery Order
Load
Slot
TimeStart
DateOrderLine
Product
Tax
Deliveries Orders
![Page 64: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/64.jpg)
8. Aggregates and Repositories
![Page 65: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/65.jpg)
CREDIT CARDS
![Page 66: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/66.jpg)
8. Aggregates and Repositories
8.1. Entity / Repository
![Page 67: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/67.jpg)
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; }}
![Page 68: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/68.jpg)
Decoupling Ulabox.com monolith. From CRUD to DDD
Problems● Aggregate?
● CreditCardRepository???
● Unprotected Domain.
![Page 69: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/69.jpg)
Decoupling Ulabox.com monolith. From CRUD to DDD
Solutions● Which is the Aggregate?
● What’s the Intention?
● Testing
![Page 70: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/70.jpg)
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
![Page 71: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/71.jpg)
8. Aggregates and Repositories
8.2. RegisterCreditCard
![Page 72: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/72.jpg)
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; }}
![Page 73: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/73.jpg)
8. Aggregates and Repositories
8.3. RegisterCreditCardHandler
![Page 74: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/74.jpg)
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());
}}
![Page 75: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/75.jpg)
8. Aggregates and Repositories
8.4. Business rules
![Page 76: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/76.jpg)
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) { ... }}
![Page 77: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/77.jpg)
9. Learned lessons
![Page 78: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/78.jpg)
This is not a Big-Bang
Decoupling Ulabox.com monolith. From CRUD to DDD
![Page 79: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/79.jpg)
Aggregate Election
Decoupling Ulabox.com monolith. From CRUD to DDD
![Page 80: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/80.jpg)
Communication
Decoupling Ulabox.com monolith. From CRUD to DDD
![Page 81: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/81.jpg)
TeamDecoupling Ulabox.com monolith. From CRUD to DDD
![Page 82: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/82.jpg)
¡¡¡Be a Professional!!!Decoupling Ulabox.com monolith. From CRUD to DDD
![Page 83: Decoupling the Ulabox.com monolith. From CRUD to DDD](https://reader033.fdocuments.in/reader033/viewer/2022051101/5871ff261a28ab176b8b4b9f/html5/thumbnails/83.jpg)
www.linkedin.com/in/avergess
Thank’sQuestions?