Slide 1

Slide 1 text

Modularising the Monolith PHP Conference Japan 2022 Ryuta Hamasaki ᖛཽ࡚ଠ

Slide 2

Slide 2 text

RYUTA HAMASAKI @avosalmon

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

Verification Solutions Service Provider >1M Accredified documents 9 markets 3 industries >9M verifications >500 Active issuers A B O U T A C C R E D I F Y

Slide 5

Slide 5 text

Modularising the Monolith

Slide 6

Slide 6 text

"HFOEB w ϞϊϦεͱϚΠΫϩαʔϏεͷϝϦοτɾσϝϦοτ w ϞδϡϥʔϞϊϦεͱ͸ w -BSBWFMΞϓϦέʔγϣϯͷϞδϡʔϧԽ w Ϟδϡʔϧؒͷίϛϡχέʔγϣϯ w ϞδϡϥʔϞϊϦεͳΞϓϦέʔγϣϯͷςετ w ੩తίʔυղੳͰυϝΠϯڥքΛڧ੍

Slide 7

Slide 7 text

-BSBDPO0OMJOF https://youtu.be/0Rq-yHAwYjQ?t=4061

Slide 8

Slide 8 text

.POPMJUIWT.JDSPTFSWJDFT

Slide 9

Slide 9 text

4)*15)&130%6$5 505)&."3,&5'*345

Slide 10

Slide 10 text

ϞϊϦεͷϝϦοτ w ͭͷϦϙδτϦ w ͭͷ$*$%ύΠϓϥΠϯ w ηοτͷΠϯϑϥ w %#τϥϯβΫγϣϯ͕γϯϓϧ

Slide 11

Slide 11 text

#JH#BMMPG.VE

Slide 12

Slide 12 text

w ػೳؒͷີ݁߹ w υϝΠϯڥք͕ͳ͍ w ͢΂ͯͷίʔυΛͲ͔͜ΒͰ΋ݺͼग़ͤΔ w ϦϙδτϦશମͷίʔυΛཧղ͢Δඞཁ͕͋Δ #JH#BMMPG.VE

Slide 13

Slide 13 text

.JDSPTFSWJDFT"SDIJUFDUVSF HTTP HTTP HTTP

Slide 14

Slide 14 text

ϚΠΫϩαʔϏεͷϝϦοτ w υϝΠϯؒͷڥք͕໌֬ w ֤αʔϏεΛݸผʹσϓϩΠͰ͖Δ w αʔϏε͝ͱʹεέʔϧΞ΢τͰ͖Δ w αʔϏε͝ͱʹҟͳΔٕज़ελοΫΛ࢖͑Δ

Slide 15

Slide 15 text

ϚΠΫϩαʔϏεͷ՝୊ w ෳ਺ϦϙδτϦͷ؅ཧ w ෳ਺ͷ$*$%ύΠϓϥΠϯ w αʔϏεؒ௨৴ͷωοτϫʔΫΦʔόʔϔου w αʔϏεΛ·͍ͨͩτϥϯβΫγϣϯ

Slide 16

Slide 16 text

ٕज़తҙࢥܾఆτϨʔυΦϑ

Slide 17

Slide 17 text

.PEVMBS.POPMJUI

Slide 18

Slide 18 text

.PEVMBS.POPMJUI Contracts Implementation Tests Contracts Implementation Tests Contracts Implementation Tests Inventory module Order module Payment module

Slide 19

Slide 19 text

Contracts Implementation Tests Contracts Implementation Tests Contracts Implementation Tests Inventory module Order module Payment module .PEVMBS.POPMJUI

Slide 20

Slide 20 text

Contracts Implementation Tests Contracts Implementation Tests Contracts Implementation Tests Inventory service Order module Payment module ஈ֊తͳϚΠΫϩαʔϏεԽ

Slide 21

Slide 21 text

%JTUSJCVUFE#JH#BMMTPG.VE

Slide 22

Slide 22 text

ϚΠΫϩαʔϏε͸มߋίετ͕ߴ͍ HTTP HTTP HTTP

Slide 23

Slide 23 text

ϞδϡϥʔϞϊϦε͸มߋίετ͕௿͍ Contracts Implementation Tests Contracts Implementation Tests Contracts Implementation Tests Inventory module Order module Payment module

Slide 24

Slide 24 text

ϞδϡʔϧͱνʔϜߏ੒ Order Module Payment Module Shipping Module Campaign Module Auth Module Inventory Module

Slide 25

Slide 25 text

ϞδϡϥʔϞϊϦε͸ େن໛։ൃʹ΋༗ޮʁ

Slide 26

Slide 26 text

https://shopify.engineering/deconstructing-monolith-designing-software-maximizes-developer-productivity

Slide 27

Slide 27 text

ϞδϡϥʔϞϊϦεʹదͨ͠ΞϓϦέʔγϣϯ w ෳ਺ͷϏδωευϝΠϯΛ΋ͭϞϊϦε w νʔϜͷ෼ׂΛݕ౼͍ͯ͠Δ w ϚΠΫϩαʔϏεͷલஈ֊ͱͯ͠

Slide 28

Slide 28 text

ϞδϡϥʔϞϊϦεͷσϝϦοτ w ςετͷ࣮ߦ͕࣌ؒ௕͘ͳΔ w (JUSFCBTFͷස౓͕ߴ͘ͳΔ w Ϟδϡʔϧ͝ͱʹεέʔϧΞ΢τͰ͖ͳ͍

Slide 29

Slide 29 text

.PEVMBSJTJOH-BSBWFM

Slide 30

Slide 30 text

%&'"6-5 4536$563&

Slide 31

Slide 31 text

.0%6-"3 4536$563&

Slide 32

Slide 32 text

.0%6-"3 4536$563&

Slide 33

Slide 33 text

No content

Slide 34

Slide 34 text

No content

Slide 35

Slide 35 text

No content

Slide 36

Slide 36 text

No content

Slide 37

Slide 37 text

No content

Slide 38

Slide 38 text

middleware(['api', 'auth:sanctum']) ->group(function () { Route::apiResource('orders', OrderController::class); }); // src/Order/routes.php

Slide 39

Slide 39 text

namespace Phpcon\Order\Providers; class OrderServiceProvider extends ServiceProvider { public function boot() { $this->loadRoutesFrom(__DIR__.'/../routes.php'); $this->loadMigrationsFrom(__DIR__.'/../Infrastructure/Database/Migrations'); $this->loadTranslationsFrom(__DIR__.'/../Resources/lang', 'order'); $this->loadViewsFrom(__DIR__.'/../Resources/views', 'order'); } public function register() { $this->mergeConfigFrom(__DIR__.'/../config/order.php', 'order'); $this->commands([DoSomething::class]); $this->app->register(AuthServiceProvider::class); $this->app->register(EventServiceProvider::class); $this->app->register(RouteServiceProvider::class); } }

Slide 40

Slide 40 text

namespace Phpcon\Order\Providers; class OrderServiceProvider extends ServiceProvider { public function boot() { $this->loadRoutesFrom(__DIR__.'/../routes.php'); $this->loadMigrationsFrom(__DIR__.'/../Infrastructure/Database/Migrations'); $this->loadTranslationsFrom(__DIR__.'/../Resources/lang', 'order'); $this->loadViewsFrom(__DIR__.'/../Resources/views', 'order'); } public function register() { $this->mergeConfigFrom(__DIR__.'/../config/order.php', 'order'); $this->commands([DoSomething::class]); $this->app->register(AuthServiceProvider::class); $this->app->register(EventServiceProvider::class); $this->app->register(RouteServiceProvider::class); } }

Slide 41

Slide 41 text

namespace Phpcon\Order\Providers; class OrderServiceProvider extends ServiceProvider { public function boot() { $this->loadRoutesFrom(__DIR__.'/../routes.php'); $this->loadMigrationsFrom(__DIR__.'/../Infrastructure/Database/Migrations'); $this->loadTranslationsFrom(__DIR__.'/../Resources/lang', 'order'); $this->loadViewsFrom(__DIR__.'/../Resources/views', 'order'); } public function register() { $this->mergeConfigFrom(__DIR__.'/../config/order.php', 'order'); $this->commands([DoSomething::class]); $this->app->register(AuthServiceProvider::class); $this->app->register(EventServiceProvider::class); $this->app->register(RouteServiceProvider::class); } }

Slide 42

Slide 42 text

namespace Phpcon\Order\Providers; class OrderServiceProvider extends ServiceProvider { public function boot() { $this->loadRoutesFrom(__DIR__.'/../routes.php'); $this->loadMigrationsFrom(__DIR__.'/../Infrastructure/Database/Migrations'); $this->loadTranslationsFrom(__DIR__.'/../Resources/lang', 'order'); $this->loadViewsFrom(__DIR__.'/../Resources/views', 'order'); } public function register() { $this->mergeConfigFrom(__DIR__.'/../config/order.php', 'order'); $this->commands([DoSomething::class]); $this->app->register(AuthServiceProvider::class); $this->app->register(EventServiceProvider::class); $this->app->register(RouteServiceProvider::class); } }

Slide 43

Slide 43 text

namespace Phpcon\Order\Providers; class OrderServiceProvider extends ServiceProvider { public function boot() { $this->loadRoutesFrom(__DIR__.'/../routes.php'); $this->loadMigrationsFrom(__DIR__.'/../Infrastructure/Database/Migrations'); $this->loadTranslationsFrom(__DIR__.'/../Resources/lang', 'order'); $this->loadViewsFrom(__DIR__.'/../Resources/views', 'order'); } public function register() { $this->mergeConfigFrom(__DIR__.'/../config/order.php', 'order'); $this->commands([DoSomething::class]); $this->app->register(AuthServiceProvider::class); $this->app->register(EventServiceProvider::class); $this->app->register(RouteServiceProvider::class); } }

Slide 44

Slide 44 text

namespace Phpcon\Order\Providers; class OrderServiceProvider extends ServiceProvider { public function boot() { $this->loadRoutesFrom(__DIR__.'/../routes.php'); $this->loadMigrationsFrom(__DIR__.'/../Infrastructure/Database/Migrations'); $this->loadTranslationsFrom(__DIR__.'/../Resources/lang', 'order'); $this->loadViewsFrom(__DIR__.'/../Resources/views', 'order'); } public function register() { $this->mergeConfigFrom(__DIR__.'/../config/order.php', 'order'); $this->commands([DoSomething::class]); $this->app->register(AuthServiceProvider::class); $this->app->register(EventServiceProvider::class); $this->app->register(RouteServiceProvider::class); } }

Slide 45

Slide 45 text

namespace Phpcon\Order\Providers; class OrderServiceProvider extends ServiceProvider { public function boot() { $this->loadRoutesFrom(__DIR__.'/../routes.php'); $this->loadMigrationsFrom(__DIR__.'/../Infrastructure/Database/Migrations'); $this->loadTranslationsFrom(__DIR__.'/../Resources/lang', 'order'); $this->loadViewsFrom(__DIR__.'/../Resources/views', 'order'); } public function register() { $this->mergeConfigFrom(__DIR__.'/../config/order.php', 'order'); $this->commands([DoSomething::class]); $this->app->register(AuthServiceProvider::class); $this->app->register(EventServiceProvider::class); $this->app->register(RouteServiceProvider::class); } }

Slide 46

Slide 46 text

namespace Phpcon\Order\Providers; class OrderServiceProvider extends ServiceProvider { public function boot() { $this->loadRoutesFrom(__DIR__.'/../routes.php'); $this->loadMigrationsFrom(__DIR__.'/../Infrastructure/Database/Migrations'); $this->loadTranslationsFrom(__DIR__.'/../Resources/lang', 'order'); $this->loadViewsFrom(__DIR__.'/../Resources/views', 'order'); } public function register() { $this->mergeConfigFrom(__DIR__.'/../config/order.php', 'order'); $this->commands([DoSomething::class]); $this->app->register(AuthServiceProvider::class); $this->app->register(EventServiceProvider::class); $this->app->register(RouteServiceProvider::class); } }

Slide 47

Slide 47 text

namespace Phpcon\Order\Providers; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; use Laracon\Order\Application\Policies\OrderPolicy; use Laracon\Order\Domain\Models\Order; class AuthServiceProvider extends ServiceProvider { protected $policies = [ Order::class => OrderPolicy::class, ]; public function boot() { $this->registerPolicies(); } }

Slide 48

Slide 48 text

namespace Phpcon\Order\Providers; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; use Laracon\Order\Domain\Listeners\HandleOrderShipment; use Laracon\Shipping\Contracts\Events\ParcelShipped; class EventServiceProvider extends ServiceProvider { /** * The event listener mappings for the application. * * @var array */ protected $listen = [ ParcelShipped::class => [ HandleOrderShipment::class, ], ]; }

Slide 49

Slide 49 text

namespace Phpcon\Order\Providers; use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider; use Illuminate\Support\Facades\Route; use Phpcon\Order\Application\Http\Middleware\SomeMiddleware; use Phpcon\Order\Domain\Models\Order; class RouteServiceProvider extends ServiceProvider { public function boot() { Route::bind('order', function ($value) { return Order::with('orderLines')->findOrFail($value); }); $this->app[‘router']->aliasMiddleware('do-something', SomeMiddleware::class); } }

Slide 50

Slide 50 text

'providers' => [ // ... \Phpcon\Inventory\Providers\InventoryServiceProvider::class, \Phpcon\Order\Providers\OrderServiceProvider::class, \Phpcon\Payment\Providers\PaymentServiceProvider::class, \Phpcon\Shipping\Providers\ShippingServiceProvider::class, ], // config/app.php

Slide 51

Slide 51 text

{ "autoload": { "psr-4": { "App\\": "app/", "Phpcon\\": "src/", "Database\\Factories\\": "database/factories/", "Database\\Seeders\\": "database/seeders/" } } } // composer.json

Slide 52

Slide 52 text

{ "autoload": { "psr-4": { "App\\": "app/", "Phpcon\\": "src/", "Database\\Factories\\": "database/factories/", "Database\\Seeders\\": "database/seeders/" } } } // composer.json

Slide 53

Slide 53 text

άϩʔόϧͳΫϥε͸Ͳ͜ʹஔ͘ʁ

Slide 54

Slide 54 text

Ϟδϡʔϧؒͷίϛϡχέʔγϣϯ

Slide 55

Slide 55 text

ίϯτϥΫτʹΑΔίϛϡχέʔγϣϯ Module A Module B Contract Implementation Downstream Upstream

Slide 56

Slide 56 text

Module A Module B ίϯτϥΫτແ͠ͷίϛϡχέʔγϣϯ Downstream Upstream Implementation

Slide 57

Slide 57 text

Order Inventory Payment Shipping Payment Provider Warehouse System Cart Cart Item Order Order Line

Slide 58

Slide 58 text

Order Inventory Payment Shipping Payment Provider Warehouse System Cart Cart Item Order Order Line 1. Checkout

Slide 59

Slide 59 text

Order Inventory Payment Shipping Payment Provider Warehouse System Cart Cart Item Order Order Line 2. Update inventory 1. Checkout

Slide 60

Slide 60 text

Order Inventory Payment Shipping Payment Provider Warehouse System Cart Cart Item Order Order Line 2. Update inventory 3. Process payment 1. Checkout

Slide 61

Slide 61 text

Order Inventory Payment Shipping Payment Provider Warehouse System Cart Cart Item Order Order Line 2. Update inventory 3. Process payment 4. Save order 1. Checkout

Slide 62

Slide 62 text

Order Inventory Payment Shipping Payment Provider Warehouse System Cart Cart Item Order Order Line 2. Update inventory 3. Process payment 5. Notify warehouse 4. Save order 1. Checkout

Slide 63

Slide 63 text

class OrderController extends Controller { public function store(StoreOrderRequest $request) { $cart = Cart::with('cartItems')->findOrFail($request->cart_id); $order = new Order(['user_id' => $request->user()->id]); try { DB::transaction(function () use ($order, $cart) { $cart->cartItems->each(function (CartItem $cartItem) use ($order) { $cartItem->product->decrement('stock', $cartItem->quantity); $order->addOrderLine($cartItem->product, $cartItem->quantity); }); $order->checkout(); $stripe = new StripePayment(config(‘services.stripe.key')); $stripe->charge($order->id, $order->total_amount); }); } catch (\Exception $e) { abort(Response::HTTP_BAD_REQUEST, trans('order::errors.failed')); } return new OrderResource($order); } }

Slide 64

Slide 64 text

class OrderController extends Controller { public function store(StoreOrderRequest $request) { $cart = Cart::with('cartItems')->findOrFail($request->cart_id); $order = new Order(['user_id' => $request->user()->id]); try { DB::transaction(function () use ($order, $cart) { $cart->cartItems->each(function (CartItem $cartItem) use ($order) { $cartItem->product->decrement('stock', $cartItem->quantity); $order->addOrderLine($cartItem->product, $cartItem->quantity); }); $order->checkout(); $stripe = new StripePayment(config(‘services.stripe.key')); $stripe->charge($order->id, $order->total_amount); }); } catch (\Exception $e) { abort(Response::HTTP_BAD_REQUEST, trans('order::errors.failed')); } return new OrderResource($order); } }

Slide 65

Slide 65 text

class OrderController extends Controller { public function store(StoreOrderRequest $request) { $cart = Cart::with('cartItems')->findOrFail($request->cart_id); $order = new Order(['user_id' => $request->user()->id]); try { DB::transaction(function () use ($order, $cart) { $cart->cartItems->each(function (CartItem $cartItem) use ($order) { $cartItem->product->decrement('stock', $cartItem->quantity); $order->addOrderLine($cartItem->product, $cartItem->quantity); }); $order->checkout(); $stripe = new StripePayment(config(‘services.stripe.key')); $stripe->charge($order->id, $order->total_amount); }); } catch (\Exception $e) { abort(Response::HTTP_BAD_REQUEST, trans('order::errors.failed')); } return new OrderResource($order); } }

Slide 66

Slide 66 text

class OrderController extends Controller { public function store(StoreOrderRequest $request) { $cart = Cart::with('cartItems')->findOrFail($request->cart_id); $order = new Order(['user_id' => $request->user()->id]); try { DB::transaction(function () use ($order, $cart) { $cart->cartItems->each(function (CartItem $cartItem) use ($order) { $cartItem->product->decrement('stock', $cartItem->quantity); $order->addOrderLine($cartItem->product, $cartItem->quantity); }); $order->checkout(); $stripe = new StripePayment(config(‘services.stripe.key')); $stripe->charge($order->id, $order->total_amount); }); } catch (\Exception $e) { abort(Response::HTTP_BAD_REQUEST, trans('order::errors.failed')); } return new OrderResource($order); } }

Slide 67

Slide 67 text

class OrderController extends Controller { public function store(StoreOrderRequest $request) { $cart = Cart::with('cartItems')->findOrFail($request->cart_id); $order = new Order(['user_id' => $request->user()->id]); try { DB::transaction(function () use ($order, $cart) { $cart->cartItems->each(function (CartItem $cartItem) use ($order) { $cartItem->product->decrement('stock', $cartItem->quantity); $order->addOrderLine($cartItem->product, $cartItem->quantity); }); $order->checkout(); $stripe = new StripePayment(config(‘services.stripe.key')); $stripe->charge($order->id, $order->total_amount); }); } catch (\Exception $e) { abort(Response::HTTP_BAD_REQUEST, trans('order::errors.failed')); } return new OrderResource($order); } }

Slide 68

Slide 68 text

class OrderController extends Controller { public function store(StoreOrderRequest $request) { $cart = Cart::with('cartItems')->findOrFail($request->cart_id); $order = new Order(['user_id' => $request->user()->id]); try { DB::transaction(function () use ($order, $cart) { $cart->cartItems->each(function (CartItem $cartItem) use ($order) { $cartItem->product->decrement('stock', $cartItem->quantity); $order->addOrderLine($cartItem->product, $cartItem->quantity); }); $order->checkout(); $stripe = new StripePayment(config(‘services.stripe.key')); $stripe->charge($order->id, $order->total_amount); }); } catch (\Exception $e) { abort(Response::HTTP_BAD_REQUEST, trans('order::errors.failed')); } return new OrderResource($order); } }

Slide 69

Slide 69 text

class OrderController extends Controller { public function store(StoreOrderRequest $request) { $cart = Cart::with('cartItems')->findOrFail($request->cart_id); $order = new Order(['user_id' => $request->user()->id]); try { DB::transaction(function () use ($order, $cart) { $cart->cartItems->each(function (CartItem $cartItem) use ($order) { $cartItem->product->decrement('stock', $cartItem->quantity); $order->addOrderLine($cartItem->product, $cartItem->quantity); }); $order->checkout(); $stripe = new StripePayment(config(‘services.stripe.key')); $stripe->charge($order->id, $order->total_amount); }); } catch (\Exception $e) { abort(Response::HTTP_BAD_REQUEST, trans('order::errors.failed')); } return new OrderResource($order); } }

Slide 70

Slide 70 text

class OrderController extends Controller { public function store(StoreOrderRequest $request) { $cart = Cart::with('cartItems')->findOrFail($request->cart_id); $order = new Order(['user_id' => $request->user()->id]); try { DB::transaction(function () use ($order, $cart) { $cart->cartItems->each(function (CartItem $cartItem) use ($order) { $cartItem->product->decrement('stock', $cartItem->quantity); $order->addOrderLine($cartItem->product, $cartItem->quantity); }); $order->checkout(); $stripe = new StripePayment(config(‘services.stripe.key')); $stripe->charge($order->id, $order->total_amount); }); } catch (\Exception $e) { abort(Response::HTTP_BAD_REQUEST, trans('order::errors.failed')); } return new OrderResource($order); } }

Slide 71

Slide 71 text

namespace Laracon\Order\Domain\Models; class CartItem extends Model { // ... public function cart(): BelongsTo { return $this->belongsTo(Cart::class); } public function product(): BelongsTo { return $this->belongsTo(Product::class); } }

Slide 72

Slide 72 text

namespace Laracon\Order\Domain\Models; class CartItem extends Model { // ... public function cart(): BelongsTo { return $this->belongsTo(Cart::class); } public function product(): BelongsTo { return $this->belongsTo(Product::class); } }

Slide 73

Slide 73 text

// src/Inventory/Contracts/ProductService.php namespace Laracon\Inventory\Contracts; interface ProductService { /** * Decrement product stock. */ public function decrementStock(int $productId, int $quantity): void; }

Slide 74

Slide 74 text

namespace Laracon\Inventory\Infrastructure\Services; use Laracon\Inventory\Contracts\ProductService as ProductServiceContract; class ProductService implements ProductServiceContract { public function decrementStock(int $productId, int $quantity): void { $product = Product::find($productId); if (!$product) { throw new ProductNotFoundException($productId); } if ($product->stock < $quantity) { throw new OutOfStockException($productId); } if (!$product->is_active) { throw new InactiveProductException($productId); } $product->decrement('stock', $quantity); } }

Slide 75

Slide 75 text

namespace Laracon\Inventory\Infrastructure\Services; use Laracon\Inventory\Contracts\ProductService as ProductServiceContract; class ProductService implements ProductServiceContract { public function decrementStock(int $productId, int $quantity): void { $product = Product::find($productId); if (!$product) { throw new ProductNotFoundException($productId); } if ($product->stock < $quantity) { throw new OutOfStockException($productId); } if (!$product->is_active) { throw new InactiveProductException($productId); } $product->decrement('stock', $quantity); } }

Slide 76

Slide 76 text

namespace Laracon\Inventory\Providers; use Laracon\Inventory\Contracts\ProductService as ProductServiceContract; use Laracon\Inventory\Infrastructure\Services\ProductService; class InventoryServiceProvider extends ServiceProvider { public $bindings = [ ProductServiceContract::class => ProductService::class, ]; // ... }

Slide 77

Slide 77 text

use Laracon\Inventory\Contracts\ProductService; class OrderController extends Controller { public function __construct(private ProductService $productService) {} public function store(StoreOrderRequest $request) { $cart = Cart::with('cartItems')->findOrFail($request->cart_id); $order = new Order(['user_id' => $request->user()->id]); try { DB::transaction(function () use ($order, $cart) { $cart->cartItems->each(function (CartItem $cartItem) use ($order) { $cartItem->product->decrement('stock', $cartItem->quantity); $order->addOrderLine($cartItem->product, $cartItem->quantity); }); // ... }); } catch (\Exception $e) { abort(Response::HTTP_BAD_REQUEST, trans('order::errors.failed')); } return new OrderResource($order); } }

Slide 78

Slide 78 text

use Laracon\Inventory\Contracts\ProductService; class OrderController extends Controller { public function __construct(private ProductService $productService) {} public function store(StoreOrderRequest $request) { $cart = Cart::with('cartItems')->findOrFail($request->cart_id); $order = new Order(['user_id' => $request->user()->id]); try { DB::transaction(function () use ($order, $cart) { $cart->cartItems->each(function (CartItem $cartItem) use ($order) { $cartItem->product->decrement('stock', $cartItem->quantity); $order->addOrderLine($cartItem->product, $cartItem->quantity); }); // ... }); } catch (\Exception $e) { abort(Response::HTTP_BAD_REQUEST, trans('order::errors.failed')); } return new OrderResource($order); } }

Slide 79

Slide 79 text

use Laracon\Inventory\Contracts\ProductService; class OrderController extends Controller { public function __construct(private ProductService $productService) {} public function store(StoreOrderRequest $request) { $cart = Cart::with('cartItems')->findOrFail($request->cart_id); $order = new Order(['user_id' => $request->user()->id]); try { DB::transaction(function () use ($order, $cart) { $cart->cartItems->each(function (CartItem $cartItem) use ($order) { $this->productService->decrementStock($cartItem->product_id, $cartItem->quantity); $order->addOrderLine($cartItem->product, $cartItem->quantity); }); // ... }); } catch (\Exception $e) { abort(Response::HTTP_BAD_REQUEST, trans('order::errors.failed')); } return new OrderResource($order); } }

Slide 80

Slide 80 text

use Laracon\Inventory\Contracts\ProductService; class OrderController extends Controller { public function __construct(private ProductService $productService) {} public function store(StoreOrderRequest $request) { $cart = Cart::with('cartItems')->findOrFail($request->cart_id); $order = new Order(['user_id' => $request->user()->id]); try { DB::transaction(function () use ($order, $cart) { $cart->cartItems->each(function (CartItem $cartItem) use ($order) { $this->productService->decrementStock($cartItem->product_id, $cartItem->quantity); $order->addOrderLine($cartItem->product, $cartItem->quantity); }); // ... }); } catch (\Exception $e) { abort(Response::HTTP_BAD_REQUEST, trans('order::errors.failed')); } return new OrderResource($order); } }

Slide 81

Slide 81 text

namespace Laracon\Order\Domain\Models; use Laracon\Inventory\Domain\Models\Product; class Order extends Model { // ... protected $orderLines = []; public function orderLines(): HasMany { return $this->hasMany(OrderLine::class); } public function addOrderLine(Product $product, int $quantity): void { $orderLine = new OrderLine([ 'product_id' => $product->id, 'product_name' => $product->name, 'price' => $product->price, 'quantity' => $quantity, ]); $this->orderLines[] = $orderLine; } }

Slide 82

Slide 82 text

namespace Laracon\Order\Domain\Models; use Laracon\Inventory\Domain\Models\Product; class Order extends Model { // ... protected $orderLines = []; public function orderLines(): HasMany { return $this->hasMany(OrderLine::class); } public function addOrderLine(Product $product, int $quantity): void { $orderLine = new OrderLine([ 'product_id' => $product->id, 'product_name' => $product->name, 'price' => $product->price, 'quantity' => $quantity, ]); $this->orderLines[] = $orderLine; } }

Slide 83

Slide 83 text

%50%BUB5SBOTGFS0CKFDU w γϯϓϧͳσʔλίϯςφΫϥε w ଞͷϞδϡʔϧʹσʔλΛఏڙ w Կͷڍಈ΋࣋ͨͳ͍ w 1010 1MBJO0ME1)10CKFDU

Slide 84

Slide 84 text

namespace Laracon\Inventory\Contracts\DataTransferObjects; class ProductDto { public function __construct( public readonly int $id, public readonly string $name, public readonly int $price, ) {} }

Slide 85

Slide 85 text

namespace Laracon\Inventory\Contracts; use Laracon\Inventory\Contracts\DataTransferObjects\ProductDto; interface ProductService { /** * Get product by product id. */ public function getProductById(int $productId): ProductDto; }

Slide 86

Slide 86 text

namespace Laracon\Inventory\Infrastructure\Services; use Laracon\Inventory\Contracts\DataTransferObjects\ProductDto; use Laracon\Inventory\Contracts\ProductService as ProductServiceContract; use Laracon\Inventory\Domain\Models\Product; class ProductService implements ProductServiceContract { public function getProductById(int $productId): ProductDto { $product = Product::find($productId); if (!$product) { throw new ProductNotFoundException($productId); } return new ProductDto( $product->id, $product->name, $product->price, ); } }

Slide 87

Slide 87 text

use Laracon\Inventory\Contracts\ProductService; class OrderController extends Controller { public function __construct(private ProductService $productService) {} public function store(StoreOrderRequest $request) { $cart = Cart::with('cartItems')->findOrFail($request->cart_id); $order = new Order(['user_id' => $request->user()->id]); try { DB::transaction(function () use ($order, $cart) { $cart->cartItems->each(function (CartItem $cartItem) use ($order) { $this->productService->decrementStock($cartItem->product_id, $cartItem->quantity); $order->addOrderLine($cartItem->product, $cartItem->quantity); }); // ... }); } catch (\Exception $e) { abort(Response::HTTP_BAD_REQUEST, trans('order::errors.failed')); } return new OrderResource($order); } }

Slide 88

Slide 88 text

use Laracon\Inventory\Contracts\ProductService; class OrderController extends Controller { public function __construct(private ProductService $productService) {} public function store(StoreOrderRequest $request) { $cart = Cart::with('cartItems')->findOrFail($request->cart_id); $order = new Order(['user_id' => $request->user()->id]); try { DB::transaction(function () use ($order, $cart) { $cart->cartItems->each(function (CartItem $cartItem) use ($order) { $this->productService->decrementStock($cartItem->product_id, $cartItem->quantity); $product = $this->productService->getProductById($cartItem->product_id); $order->addOrderLine($cartItem->product, $cartItem->quantity); }); // ... }); } catch (\Exception $e) { abort(Response::HTTP_BAD_REQUEST, trans('order::errors.failed')); } return new OrderResource($order); } }

Slide 89

Slide 89 text

use Laracon\Inventory\Contracts\ProductService; class OrderController extends Controller { public function __construct(private ProductService $productService) {} public function store(StoreOrderRequest $request) { $cart = Cart::with('cartItems')->findOrFail($request->cart_id); $order = new Order(['user_id' => $request->user()->id]); try { DB::transaction(function () use ($order, $cart) { $cart->cartItems->each(function (CartItem $cartItem) use ($order) { $this->productService->decrementStock($cartItem->product_id, $cartItem->quantity); $product = $this->productService->getProductById($cartItem->product_id); $order->addOrderLine($product, $cartItem->quantity); }); // ... }); } catch (\Exception $e) { abort(Response::HTTP_BAD_REQUEST, trans('order::errors.failed')); } return new OrderResource($order); } }

Slide 90

Slide 90 text

namespace Laracon\Order\Domain\Models; use Laracon\Inventory\Domain\Models\Product; class Order extends Model { // ... protected $orderLines = []; public function addOrderLine(Product $product, int $quantity): void { $orderLine = new OrderLine([ 'product_id' => $product->id, 'product_name' => $product->name, 'price' => $product->price, 'quantity' => $quantity, ]); $this->orderLines[] = $orderLine; } }

Slide 91

Slide 91 text

namespace Laracon\Order\Domain\Models; use Laracon\Inventory\Domain\Models\Product; class Order extends Model { // ... protected $orderLines = []; public function addOrderLine(Product $product, int $quantity): void { $orderLine = new OrderLine([ 'product_id' => $product->id, 'product_name' => $product->name, 'price' => $product->price, 'quantity' => $quantity, ]); $this->orderLines[] = $orderLine; } }

Slide 92

Slide 92 text

namespace Laracon\Order\Domain\Models; use Laracon\Inventory\Contracts\DataTransferObjects\ProductDto; class Order extends Model { // ... protected $orderLines = []; public function addOrderLine(ProductDto $product, int $quantity): void { $orderLine = new OrderLine([ 'product_id' => $product->id, 'product_name' => $product->name, 'price' => $product->price, 'quantity' => $quantity, ]); $this->orderLines[] = $orderLine; } }

Slide 93

Slide 93 text

namespace Laracon\Order\Domain\Models; use Laracon\Inventory\Contracts\DataTransferObjects\ProductDto; class Order extends Model { // ... protected $orderLines = []; public function addOrderLine(ProductDto $product, int $quantity): void { $orderLine = new OrderLine([ 'product_id' => $product->id, 'product_name' => $product->name, 'price' => $product->price, 'quantity' => $quantity, ]); $this->orderLines[] = $orderLine; } }

Slide 94

Slide 94 text

use Laracon\Inventory\Contracts\ProductService; class OrderController extends Controller { public function __construct(private ProductService $productService) {} public function store(StoreOrderRequest $request) { $cart = Cart::with('cartItems')->findOrFail($request->cart_id); $order = new Order(['user_id' => $request->user()->id]); try { DB::transaction(function () use ($order, $cart) { // ... $order->checkout(); $stripe = new StripePayment(config(‘services.stripe.key')); $stripe->charge($order->id, $order->total_amount); }); } catch (\Exception $e) { abort(Response::HTTP_BAD_REQUEST, trans('order::errors.failed')); } return new OrderResource($order); } }

Slide 95

Slide 95 text

use Laracon\Inventory\Contracts\ProductService; class OrderController extends Controller { public function __construct(private ProductService $productService) {} public function store(StoreOrderRequest $request) { $cart = Cart::with('cartItems')->findOrFail($request->cart_id); $order = new Order(['user_id' => $request->user()->id]); try { DB::transaction(function () use ($order, $cart) { // ... $order->checkout(); $stripe = new StripePayment(config(‘services.stripe.key')); $stripe->charge($order->id, $order->total_amount); }); } catch (\Exception $e) { abort(Response::HTTP_BAD_REQUEST, trans('order::errors.failed')); } return new OrderResource($order); } }

Slide 96

Slide 96 text

namespace Laracon\Payment\Contracts; interface PaymentService { /** * Process payment for the given amount. */ public function charge(int $orderId, int $amount): void; }

Slide 97

Slide 97 text

namespace Laracon\Payment\Infrastructure\Services; use Stripe\StripeClient; use Laracon\Payment\Contracts\PaymentService as PaymentServiceContract; class StripePayment implements PaymentServiceContract { private StripeClient $stripe; public function __construct(string $stripeKey) { $this->stripe = new StripeClient($stripeKey); } public function charge(int $orderId, int $amount): void { $this->stripe->charges->create([ 'amount' => $amount, 'currency' => 'usd', ]); // ... } }

Slide 98

Slide 98 text

namespace Laracon\Payment\Providers; use Illuminate\Support\ServiceProvider; use Laracon\Payment\Contracts\PaymentService as PaymentServiceContract; use Laracon\Payment\Infrastructure\Services\StripePayment; class PaymentServiceProvider extends ServiceProvider { public function register() { $this->app->bind(PaymentServiceContract::class, function ($app) { return new StripePayment(config(‘services.stripe.key')); }); } }

Slide 99

Slide 99 text

use Laracon\Inventory\Contracts\ProductService; class OrderController extends Controller { public function __construct( private ProductService $productService, ) {} public function store(StoreOrderRequest $request) { // ... try { DB::transaction(function () use ($order, $cart) { // ... $stripe = new StripePayment(config('service.stripe.key')); $stripe->charge($order->id, $order->total_amount); }); } catch (\Exception $e) { abort(Response::HTTP_BAD_REQUEST, trans('order::errors.failed')); } return new OrderResource($order); } }

Slide 100

Slide 100 text

use Laracon\Inventory\Contracts\ProductService; use Laracon\Payment\Contracts\PaymentService; class OrderController extends Controller { public function __construct( private ProductService $productService, private PaymentService $paymentService, ) {} public function store(StoreOrderRequest $request) { // ... try { DB::transaction(function () use ($order, $cart) { // ... $stripe = new StripePayment(config('service.stripe.key')); $stripe->charge($order->id, $order->total_amount); }); } catch (\Exception $e) { abort(Response::HTTP_BAD_REQUEST, trans('order::errors.failed')); } return new OrderResource($order); } }

Slide 101

Slide 101 text

use Laracon\Inventory\Contracts\ProductService; use Laracon\Payment\Contracts\PaymentService; class OrderController extends Controller { public function __construct( private ProductService $productService, private PaymentService $paymentService, ) {} public function store(StoreOrderRequest $request) { // ... try { DB::transaction(function () use ($order, $cart) { // ... $stripe = new StripePayment(config('service.stripe.key')); $stripe->charge($order->id, $order->total_amount); }); } catch (\Exception $e) { abort(Response::HTTP_BAD_REQUEST, trans('order::errors.failed')); } return new OrderResource($order); } }

Slide 102

Slide 102 text

use Laracon\Inventory\Contracts\ProductService; use Laracon\Payment\Contracts\PaymentService; class OrderController extends Controller { public function __construct( private ProductService $productService, private PaymentService $paymentService, ) {} public function store(StoreOrderRequest $request) { // ... try { DB::transaction(function () use ($order, $cart) { // ... $stripe->charge($order->id, $order->total_amount); }); } catch (\Exception $e) { abort(Response::HTTP_BAD_REQUEST, trans('order::errors.failed')); } return new OrderResource($order); } }

Slide 103

Slide 103 text

use Laracon\Inventory\Contracts\ProductService; use Laracon\Payment\Contracts\PaymentService; class OrderController extends Controller { public function __construct( private ProductService $productService, private PaymentService $paymentService, ) {} public function store(StoreOrderRequest $request) { // ... try { DB::transaction(function () use ($order, $cart) { // ... $this->paymentService->charge($order->id, $order->total_amount); }); } catch (\Exception $e) { abort(Response::HTTP_BAD_REQUEST, trans('order::errors.failed')); } return new OrderResource($order); } }

Slide 104

Slide 104 text

namespace Laracon\Payment\Providers; use Illuminate\Support\ServiceProvider; use Laracon\Payment\Contracts\PaymentService as PaymentServiceContract; use Laracon\Payment\Infrastructure\Services\StripePayment; class PaymentServiceProvider extends ServiceProvider { public function register() { $this->app->bind(PaymentServiceContract::class, function ($app) { return new StripePayment(config(‘services.stripe.key')); }); } }

Slide 105

Slide 105 text

namespace Laracon\Payment\Providers; use Illuminate\Support\ServiceProvider; use Laracon\Payment\Contracts\PaymentService as PaymentServiceContract; use Laracon\Payment\Infrastructure\Services\PaddlePayment; class PaymentServiceProvider extends ServiceProvider { public function register() { $this->app->bind(PaymentServiceContract::class, function ($app) { return new PaddlePayment(config(‘services.paddle.key’)); }); } }

Slide 106

Slide 106 text

Order Inventory Payment Shipping Payment Provider Warehouse System Cart Cart Item Order Order Line 2. Update inventory 3. Process payment 5. Notify warehouse 4. Save order 1. Checkout

Slide 107

Slide 107 text

Order Inventory Payment Shipping Payment Provider Warehouse System Cart Cart Item Order Order Line OrderFul fi lled Publish Subscribe 2. Update inventory 3. Process payment 4. Save order 1. Checkout

Slide 108

Slide 108 text

class OrderController extends Controller { // ... public function store(StoreOrderRequest $request) { // ... try { DB::transaction(function () use ($order, $cart) { // ... $this->paymentService->charge($order->id, $order->total_amount); }); } catch (\Exception $e) { abort(Response::HTTP_BAD_REQUEST, trans('order::errors.failed')); } return new OrderResource($order); } }

Slide 109

Slide 109 text

class OrderController extends Controller { // ... public function store(StoreOrderRequest $request) { // ... try { DB::transaction(function () use ($order, $cart) { // ... $this->paymentService->charge($order->id, $order->total_amount); }); } catch (\Exception $e) { abort(Response::HTTP_BAD_REQUEST, trans('order::errors.failed')); } OrderFulfilled::dispatch($order->id); return new OrderResource($order); } }

Slide 110

Slide 110 text

class OrderController extends Controller { // ... public function store(StoreOrderRequest $request) { // ... try { DB::transaction(function () use ($order, $cart) { // ... $this->paymentService->charge($order->id, $order->total_amount); }); } catch (\Exception $e) { abort(Response::HTTP_BAD_REQUEST, trans('order::errors.failed')); } OrderFulfilled::dispatch($order->id); return new OrderResource($order); } }

Slide 111

Slide 111 text

namespace Laracon\Shipping\Providers; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; use Laracon\Order\Contracts\Events\OrderFulfilled; use Laracon\Shipping\Domain\Listeners\NotifyWarehouse; class EventServiceProvider extends ServiceProvider { protected $listen = [ OrderFulfilled::class => [ NotifyWarehouse::class, ] ]; }

Slide 112

Slide 112 text

namespace Laracon\Shipping\Domain\Listeners; use Laracon\Order\Contracts\Events\OrderFulfilled; class NotifyWarehouse { public function handle(OrderFulfilled $event) { // Notify warehouse system } }

Slide 113

Slide 113 text

5FTUJOH.PEVMFT

Slide 114

Slide 114 text

./src/**/Tests/Unit ./src/**/Tests/Feature ./src ./src/**/Tests/*

Slide 115

Slide 115 text

./src/**/Tests/Unit ./src/**/Tests/Feature ./src ./src/**/Tests/*

Slide 116

Slide 116 text

./src/**/Tests/Unit ./src/**/Tests/Feature ./src ./src/**/Tests/*

Slide 117

Slide 117 text

it('creates a new order', function () { Event::fake(); $user = User::factory()->create(); $cart = Cart::factory()->create(['user_id' => $user->id]); // create some other test data... mock(ProductService::class, function ($mock) use ($product) { $mock->shouldReceive('decrementStock') ->with($product->id, 1) ->once(); $mock->shouldReceive('getProductById') ->with($product->id) ->once() ->andReturn($product); }); mock(PaymentService::class) ->shouldReceive('charge') ->once(); // ...

Slide 118

Slide 118 text

it('creates a new order', function () { Event::fake(); $user = User::factory()->create(); $cart = Cart::factory()->create(['user_id' => $user->id]); // create some other test data... mock(ProductService::class, function ($mock) use ($product) { $mock->shouldReceive('decrementStock') ->with($product->id, 1) ->once(); $mock->shouldReceive('getProductById') ->with($product->id) ->once() ->andReturn($product); }); mock(PaymentService::class) ->shouldReceive('charge') ->once(); // ...

Slide 119

Slide 119 text

it('creates a new order', function () { Event::fake(); $user = User::factory()->create(); $cart = Cart::factory()->create(['user_id' => $user->id]); // create some other test data... mock(ProductService::class, function ($mock) use ($product) { $mock->shouldReceive('decrementStock') ->with($product->id, 1) ->once(); $mock->shouldReceive('getProductById') ->with($product->id) ->once() ->andReturn($product); }); mock(PaymentService::class) ->shouldReceive('charge') ->once(); // ...

Slide 120

Slide 120 text

Ϟδϡʔϧؒͷ֎෦Ωʔ੍໿͸ີ݁߹ʹͭͳ͕Δ Order module Inventory module Cart Items Products product_id id

Slide 121

Slide 121 text

it('creates a new order', function () { Event::fake(); $user = User::factory()->create(); $cart = Cart::factory()->create(['user_id' => $user->id]); // create some other test data... mock(ProductService::class, function ($mock) use ($product) { $mock->shouldReceive('decrementStock') ->with($product->id, 1) ->once(); $mock->shouldReceive('getProductById') ->with($product->id) ->once() ->andReturn($product); }); mock(PaymentService::class) ->shouldReceive('charge') ->once(); // ...

Slide 122

Slide 122 text

// ... $order = postJson('/order-module/orders', ['cart_id' => $cart->id]) ->assertCreated() ->json('data'); assertDatabaseHas('orders', [ 'id' => $order['id'], ]); Event::assertDispatched(OrderFulfilled::class, $order['id']);

Slide 123

Slide 123 text

ϞδϡʔϧͷυϝΠϯڥքΛڧ੍

Slide 124

Slide 124 text

%FQUSBDΛ࢖ͬͨ੩తίʔυղੳ w ϨΠϠʔΛఆٛ w ϨΠϠʔؒΞΫηεͷϧʔϧΛఆٛ w ϧʔϧʹҧ൓͍ͯ͠ΔίʔυΛݕ஌ w ґଘؔ܎ΛՄࢹԽ

Slide 125

Slide 125 text

composer require qossmic/deptrac-shim --dev sudo apt-get install graphviz // for visualising dependency graph

Slide 126

Slide 126 text

parameters: paths: - ./src - ./app layers: - name: Common collectors: - type: directory regex: ./app/.* - name: Contracts collectors: - type: directory regex: .*/Contracts/.* - name: Vendor collectors: - type: bool must_not: - type: directory regex: ./(src|app)/.* - name: Inventory collectors: - type: bool must: - type: directory regex: ./src/Inventory/.* must_not: - type: directory regex: ./src/Inventory/Contracts/.* // deptrac.yaml

Slide 127

Slide 127 text

parameters: paths: - ./src - ./app layers: - name: Common collectors: - type: directory regex: ./app/.* - name: Contracts collectors: - type: directory regex: .*/Contracts/.* - name: Vendor collectors: - type: bool must_not: - type: directory regex: ./(src|app)/.* - name: Inventory collectors: - type: bool must: - type: directory regex: ./src/Inventory/.* must_not: - type: directory regex: ./src/Inventory/Contracts/.* // deptrac.yaml

Slide 128

Slide 128 text

parameters: paths: - ./src - ./app layers: - name: Common collectors: - type: directory regex: ./app/.* - name: Contracts collectors: - type: directory regex: .*/Contracts/.* - name: Vendor collectors: - type: bool must_not: - type: directory regex: ./(src|app)/.* - name: Inventory collectors: - type: bool must: - type: directory regex: ./src/Inventory/.* must_not: - type: directory regex: ./src/Inventory/Contracts/.* // deptrac.yaml

Slide 129

Slide 129 text

parameters: paths: - ./src - ./app layers: - name: Common collectors: - type: directory regex: ./app/.* - name: Contracts collectors: - type: directory regex: .*/Contracts/.* - name: Vendor collectors: - type: bool must_not: - type: directory regex: ./(src|app)/.* - name: Inventory collectors: - type: bool must: - type: directory regex: ./src/Inventory/.* must_not: - type: directory regex: ./src/Inventory/Contracts/.* // deptrac.yaml

Slide 130

Slide 130 text

parameters: paths: - ./src - ./app layers: - name: Common collectors: - type: directory regex: ./app/.* - name: Contracts collectors: - type: directory regex: .*/Contracts/.* - name: Vendor collectors: - type: bool must_not: - type: directory regex: ./(src|app)/.* - name: Inventory collectors: - type: bool must: - type: directory regex: ./src/Inventory/.* must_not: - type: directory regex: ./src/Inventory/Contracts/.* // deptrac.yaml

Slide 131

Slide 131 text

parameters: paths: - ./src - ./app layers: - name: Common collectors: - type: directory regex: ./app/.* - name: Contracts collectors: - type: directory regex: .*/Contracts/.* - name: Vendor collectors: - type: bool must_not: - type: directory regex: ./(src|app)/.* - name: Inventory collectors: - type: bool must: - type: directory regex: ./src/Inventory/.* must_not: - type: directory regex: ./src/Inventory/Contracts/.* // deptrac.yaml

Slide 132

Slide 132 text

parameters: paths: - ./src - ./app layers: - name: Common collectors: - type: directory regex: ./app/.* - name: Contracts collectors: - type: directory regex: .*/Contracts/.* - name: Vendor collectors: - type: bool must_not: - type: directory regex: ./(src|app)/.* - name: Inventory collectors: - type: bool must: - type: directory regex: ./src/Inventory/.* must_not: - type: directory regex: ./src/Inventory/Contracts/.* // deptrac.yaml

Slide 133

Slide 133 text

parameters: paths: - ./src - ./app layers: - name: Common collectors: - type: directory regex: ./app/.* - name: Contracts collectors: - type: directory regex: .*/Contracts/.* - name: Vendor collectors: - type: bool must_not: - type: directory regex: ./(src|app)/.* - name: Inventory collectors: - type: bool must: - type: directory regex: ./src/Inventory/.* must_not: - type: directory regex: ./src/Inventory/Contracts/.* // deptrac.yaml

Slide 134

Slide 134 text

parameters: paths: - ./src - ./app layers: # ... ruleset: Common: - Vendor Contracts: - Vendor - Common Inventory: - Contracts - Vendor - Common Order: - Contracts - Vendor - Common Payment: - Contracts - Vendor - Common Shipping: - Contracts - Vendor - Common // deptrac.yaml

Slide 135

Slide 135 text

parameters: paths: - ./src - ./app layers: # ... ruleset: Common: - Vendor Contracts: - Vendor - Common Inventory: - Contracts - Vendor - Common Order: - Contracts - Vendor - Common Payment: - Contracts - Vendor - Common Shipping: - Contracts - Vendor - Common // deptrac.yaml

Slide 136

Slide 136 text

parameters: paths: - ./src - ./app layers: # ... ruleset: Common: - Vendor Contracts: - Vendor - Common Inventory: - Contracts - Vendor - Common Order: - Contracts - Vendor - Common Payment: - Contracts - Vendor - Common Shipping: - Contracts - Vendor - Common // deptrac.yaml

Slide 137

Slide 137 text

parameters: paths: - ./src - ./app layers: # ... ruleset: Common: - Vendor Contracts: - Vendor - Common Inventory: - Contracts - Vendor - Common Order: - Contracts - Vendor - Common Payment: - Contracts - Vendor - Common Shipping: - Contracts - Vendor - Common // deptrac.yaml

Slide 138

Slide 138 text

parameters: paths: - ./src - ./app layers: # ... ruleset: Common: - Vendor Contracts: - Vendor - Common Inventory: - Contracts - Vendor - Common Order: - Contracts - Vendor - Common Payment: - Contracts - Vendor - Common Shipping: - Contracts - Vendor - Common // deptrac.yaml

Slide 139

Slide 139 text

%FQUSBDJO$*

Slide 140

Slide 140 text

No content

Slide 141

Slide 141 text

No content

Slide 142

Slide 142 text

੩తίʔυղੳ͸׬ᘳͰ͸ͳ͍

Slide 143

Slide 143 text

DB::table('products') ->where('id', $orderLine->product_id) ->decrement(‘stock', $orderLine->quantity); // src/Order/Domain/Models/Order.php

Slide 144

Slide 144 text

·ͱΊ w ϚΠΫϩαʔϏεͷલஈ֊ͱͯ͠ͷϞδϡϥʔϞϊϦε w ϞδϡʔϧԽ͢ΔͨΊͷσΟϨΫτϦߏ଄ w ίϯτϥΫτΛ௨ͨ͠Ϟδϡʔϧؒ௨৴ w ϞδϡʔϧͷςετͰ͸ɺίϯτϥΫτΛϞοΫ w %FQUSBDΛ࢖ͬͯυϝΠϯͷڥքΛڧ੍

Slide 145

Slide 145 text

ϦϯΫू •https://github.com/avosalmon/modular-monolith-laravel •Modularising the Monolith - Laracon Online 2022 •Deconstructing the Monolith: Designing Software that Maximizes Developer Productivity •Under Deconstruction: The State of Shopify’s Monolith •Long live the Monolith! Monolithic Architecture != Big Ball of Mud •Strategic Monoliths and Microservices •Domain-Driven Laravel

Slide 146

Slide 146 text

THANK YOU @avosalmon