Upgrade to Pro — share decks privately, control downloads, hide ads and more …

PHPers Trójmiasto #26 - Przekraczania granic Bo...

PHPers Trójmiasto #26 - Przekraczania granic Bounded Contextów

Czy kiedykolwiek zastanawiałeś się, jak efektywnie łączyć kropki pomiędzy różnymi bounded contextami w swojej organizacji/zespole? Pewnie słyszałeś o takich wzorcach jak:
* Partnership,
* Shared Kernel,
* Supplier-Consumer,
* Conformist,
* Anti Corruption Layer,
* Open-Host Service.

Teorie są ciekawe, ale jak wygląda ich zastosowanie w rzeczywistości?Podczas tej prezentacji pokażę, jak w Printify eksperymentowaliśmy z różnymi podejściami do komunikacji między BC. Dowiesz się:
* Jakie techniki sprawdzają się w praktyce?
* Które rozwiązania mają swoje ukryte pułapki?
* Co faktycznie przetrwało próbę czasu?

Jeśli chcesz zobaczyć praktyczne przykłady i podyskutować o wyzwaniach związanych z komunikacją między zespołami i kontekstami, ta sesja jest dla Ciebie.

Nie teoretyzujemy – działamy! 😊

Avatar for Damian Dziaduch

Damian Dziaduch

July 26, 2025
Tweet

More Decks by Damian Dziaduch

Other Decks in Programming

Transcript

  1. namespace Acme\ProjectX\OrderSubmission\Application\CommandBus; final class SendOrderCommandHandler { public function __ construct(

    private readonly \Acme\ProjectX\OrderManagement\Application\Port\Out\OrderRepository $orders, private readonly \Acme\ProjectX\OrderSubmission\Application\Port\Out\SendOrderPort $sendOrder, private readonly \Psr\EventDispatcher\EventDispatcherInterface $eventDispatcher, ) { } public function __ invoke(SendOrderCommand $command): void { $order = $this -> orders -> getById(new OrderId($command -> orderId())); try { $this -> sendOrder - > send($order); } catch (UnableToSendOrder $exception) { $this -> dispatchFailedEvent($order, $exception - > getMessage()); return; } $this -> eventDispatcher -> dispatch(OrderSentToProductionEvent :: fromOrder($order)); }
  2. Plusy Bezpośrednie odwołanie 🤝 • Trywialne w implementacji 🥷 •

    Świetne na początek gdy granice kontekstów nie są jasne 🧐
  3. Minusy Bezpośrednie odwołanie 🤝 • "Betonuje" implementację 🧱 • "Nie

    da się" wyciągnąć kontekstu do mikro serwisu 🔒
  4. $> deptrac analyse --con f ig- f ile=deptrac-horizontal.yaml 1258/1258 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓]

    100% ----------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Reason OrderSubmission ----------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Violation Acme\ProjectX\OrderSubmission\Application\CommandBus\SendOrderCommandHandler must not depend on Acme\ProjectX\OrderManagement\Application\Port\Out\OrderRepository (OrderManagement) /opt/project/srcHexagonal/OrderSubmission/Application/CommandBus/SendOrderCommandHandler.php:19 Violation Acme\ProjectX\OrderSubmission\Application\CommandBus\SendOrderCommandHandler must not depend on Acme\ProjectX\OrderManagement\Application\Port\Out\OrderRepository (OrderManagement) /opt/project/srcHexagonal/OrderSubmission/Application/CommandBus/SendOrderCommandHandler.php:10 ----------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  5. namespace Acme\ProjectX\OrderSubmission\Application\CommandBus; final class SendOrderCommandHandler { public function __ construct(

    private readonly \Acme\ProjectX\SharedKernel\Application\Port\Out\OrderRepository $orders, private readonly \Acme\ProjectX\OrderSubmission\Application\Port\Out\SendOrderPort $sendOrder, private readonly \Psr\EventDispatcher\EventDispatcherInterface $eventDispatcher, ) { } public function __ invoke(SendOrderCommand $command): void { $order = $this -> orders -> getById($command -> orderId()); try { $this -> sendOrder - > send($order); } catch (UnableToSendOrder $exception) { $this -> dispatchFailedEvent($order, $exception - > getMessage()); return; } $this -> eventDispatcher -> dispatch(OrderSentToProductionEvent :: fromOrder($order)); }
  6. • Dość proste w implementacji 🥷 • Brak bezpośredniej integracji

    między kontekstami ✋ Shared Kernel 🏛 Plusy
  7. Minusy • Im więcej klientów kodu z Shared Kernel tym

    trudniej go zmienić 🧱 • Wyciąganie Shared Kernel do biblioteki współdzielonej może być wyzwaniem 😓 Shared Kernel 🏛
  8. { "$schema": "http: / / json-schema.org/draft-07/schema", "$id": "http: // acme.com/events/order/order-created.json",

    "type": "object", "title": "events/order/order-created", "description": "Core orders: order created.", "required": [ "order_id", "merchant_id", "print_provider_id" ], "properties": { "order_id": { "type": "int", "min": 1 }, "merchant_id": { "type": "int", "min": 1 }, "print_provider_id": { "type": "int", "min": 1 }, ... } }
  9. namespace Acme\ProjectX\OrderSubmission\Application\CommandBus; final class SendOrderCommandHandler { public function __ construct(

    private readonly \Acme\ProjectX\OrderSubmission\Application\Port\Out\SendOrderPort $sendOrder, private readonly \Psr\EventDispatcher\EventDispatcherInterface $eventDispatcher, ) { } public function __ invoke(\Acme\EventBus\Orders\OrderCreated $event): void { try { $this -> sendOrder - > send($event -> orderDto); } catch (UnableToSendOrder $exception) { $this -> dispatchFailedEvent($order, $exception - > getMessage()); return; } $this -> eventDispatcher -> dispatch(OrderSentToProductionEvent :: fromOrder($order)); }
  10. Plusy • Komunikacja w pełni asynchroniczna 📮 • Możliwość wyjęcia

    kontekstu do mikro serwisu 🐣 • Odporność na awarie 🏋 • Brak bezpośredniej komunikacji ✋ Zdarzenie asynchroniczne 📮
  11. Minusy Zdarzenie asynchroniczne 📮 • Zdarzenia mogą być "tłuste" 🐷

    • Eventual Consistency 😶🌫 • Potrzebny EventBus i infrastruktura do tego 🏭 • Więcej zachodu niż w przypadku Shared Kernel 😓
  12. namespace Acme\ProjectX\OrderSubmission\Application\CommandBus; final class SendOrderCommandHandler { public function __ construct(

    private readonly \Acme\ProjectX\OrderSubmission\Application\Port\Out\GetOrderPort $orders, private readonly \Acme\ProjectX\OrderSubmission\Application\Port\Out\SendOrderPort $sendOrder, private readonly \Psr\EventDispatcher\EventDispatcherInterface $eventDispatcher, ) { } public function __ invoke(\Acme\EventBus\Orders\OrderCreated $event): void { $order = $this -> orders -> getById($event -> orderId); try { $this -> sendOrder - > send($order); } catch (UnableToSendOrder $exception) { $this -> dispatchFailedEvent($order, $exception - > getMessage()); return; } $this -> eventDispatcher -> dispatch(OrderSentToProductionEvent :: fromOrder($order)); }
  13. final class HttpGetOrder implements GetOrder { public function get(int $orderId):

    OrderDto { $request = $this -> requestFactory - > createRequest(method: 'GET', uri: sprintf('/api/orders/%s', $orderId)) - > withHeader('Accept', 'application/json') - > withHeader('Content-Type', 'application/json'); $response = $this -> client -> sendRequest($request); return $this -> serializer -> deserialize( data: $response -> getBody() -> getContents(), type: OrderDto : : class, format: 'json', ); } }
  14. final class InternalApplicationHttpClient implements ClientInterface { public function sendRequest(PsrRequest $request):

    PsrResponse { $symfonyRequest = $this - > httpFoundationFactory -> createRequest($this -> psrRequestToPsrServerRequest($request)); $this -> urlMatcher -> setContext(new RequestContext($symfonyRequest -> getBaseUrl(), $symfonyRequest - > getMethod())); try { $urlMatch = $this -> urlMatcher -> match($symfonyRequest -> getPathInfo()); } catch (ExceptionInterface $e) { return $this -> responseFactory -> createResponse(404, $e -> getMessage()); } if (!array_key_exists('_controller', $urlMatch)) { return $this -> responseFactory -> createResponse(404, 'Route not found'); } $symfonyRequest - > attributes -> add(['_controller' => $urlMatch['_controller']]); $response = $this -> kernel -> handle($symfonyRequest, HttpKernelInterface : : SUB_REQUEST); return $this -> responseFactory - > createResponse($response -> getStatusCode()) - > withBody($this - > streamFactory -> createStream((string) $response - > getContent())); }
  15. Plusy Sub Request ☎ • Chude zdarzenia 🪶 • Jeden

    runtime, jeden proces 🐇 • Możliwość wyjęcia kontekstu do mikro serwisu 🐣 • Odporność na awarie 🏋
  16. Minusy Sub Request ☎ • Dużo roboty - potrzebne zdarzenie

    oraz endpoint 😓 • Potrzebny EventBus i infrastruktura do tego 🏭
  17. Podsumowanie • Integruj BC tak by dało się je wyciągnąć

    do mikro serwisów 🐣 • Zacznij od Bezpośredniego odwołania 🤝 a następnie spróbuj • Shared Kernel 🏛 • Zdarzenie asynchroniczne 📮 • Sub Request ☎ • Modularny monolit f irst by mieć pewność że granice są poprawne ✅
  18. O mnie W branży od 2011 Senior software engineer Public

    speaker Trener Zapraszam do siebie: