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

依存関係のコントロール / Dependency Control

nrs
September 24, 2022

依存関係のコントロール / Dependency Control

PHP Conference 2022 でソフトウェア開発における依存をどのように制御するかについてお話ししました。

この資料を使ったトークが YouTube にアップロードされています。
トークURL: https://youtu.be/0Y7ew4FHdO4

セッションの概要など詳しくはコチラ→https://fortee.jp/phpcon-2022/proposal/2a07ae7f-0b02-42d5-a5f3-53d397a7dcfc

◆ URL
トークURL: https://youtu.be/0Y7ew4FHdO4
チャンネル登録: https://www.youtube.com/c/narusemi?sub_confirmation=1
Twitter: https://twitter.com/nrslib

nrs

September 24, 2022
Tweet

More Decks by nrs

Other Decks in Programming

Transcript

  1. class Foo { public function greet() { echo "hello"; }

    } $foo = new Foo(); $foo->greet();
  2. class Foo { private readonly Bar $bar; public function __construct(Bar

    $bar) { $this->bar = $bar; } public function greet() { echo "hello " . $this->bar->getName(); } } class Bar { private readonly string $name; public function __construct(string $name) { $this->name = $name; } public function getName(): string { return $this->name; } } $bar = new Bar("nrs"); $foo = new Foo($bar); $foo->greet();
  3. class Foo { private readonly Bar $bar; public function __construct(Bar

    $bar) { $this->bar = $bar; } public function greet() { echo "hello " . $this->bar->getName(); } } class Bar { private readonly string $name; public function __construct(string $name) { $this->name = $name; } public function getName(): string { return $this->name; } } $bar = new Bar("nrs"); $foo = new Foo($bar); $foo->greet();
  4. class Foo { private readonly Bar $bar; public function __construct(Bar

    $bar) { $this->bar = $bar; } public function greet() { echo "hello " . $this->bar->getName(); } } class Bar { private readonly string $name; public function __construct(string $name) { $this->name = $name; } public function getName(): string { return $this->name; } } $bar = new Bar("nrs"); $foo = new Foo($bar); $foo->greet();
  5. class Foo { private readonly Bar $bar; public function __construct(Bar

    $bar) { $this->bar = $bar; } public function greet() { echo "hello " . $this->bar->getName(); } } class Bar { private readonly string $name; public function __construct(string $name) { $this->name = $name; } public function getName(): string { return $this->name; } } $bar = new Bar("nrs"); $foo = new Foo($bar); $foo->greet();
  6. class Foo { private readonly Bar $bar; public function __construct(Bar

    $bar) { $this->bar = $bar; } public function greet() { echo "hello " . $this->bar->getName(); } } class Bar { private readonly string $name; public function __construct(string $name) { $this->name = $name; } public function getName(): string { return $this->name; } } $bar = new Bar("nrs"); $foo = new Foo($bar); $foo->greet();
  7. class Foo { private readonly Bar $bar; public function __construct(Bar

    $bar) { $this->bar = $bar; } public function greet() { echo "hello " . $this->bar->getName(); } } class Bar { private readonly string $name; public function __construct(string $name) { $this->name = $name; } public function getName(): string { return $this->name; } } $bar = new Bar("nrs"); $foo = new Foo($bar); $foo->greet();
  8. class Foo { private readonly Bar $bar; public function __construct(Bar

    $bar) { $this->bar = $bar; } public function greet() { echo "hello " . $this->bar->getName(); } } class Bar { private readonly string $name; public function __construct(string $name) { $this->name = $name; } public function getName(): string { return $this->name; } }
  9. class Foo { private readonly Bar $bar; public function __construct(Bar

    $bar) { $this->bar = $bar; } public function greet() { echo "hello " . $this->bar->getName(); } } class Bar { private readonly string $name; public function __construct(string $name) { $this->name = $name; } public function getName(): string { return $this->name; } }
  10. class Foo { private readonly Bar $bar; public function __construct(Bar

    $bar) { $this->bar = $bar; } public function greet() { echo "hello " . $this->bar->getName(); } } class Bar { private readonly string $name; public function __construct(string $name) { $this->name = $name; } public function getName(): string { return $this->name; } }
  11. class Foo { private readonly Bar $bar; public function __construct(Bar

    $bar) { $this->bar = $bar; } public function greet() { echo "hello " . $this->bar->getName(); } } class Bar { private readonly string $name; public function __construct(string $name) { $this->name = $name; } public function getName(): string { return $this->name; } }
  12. class UserCreateInteractor implements UserCreateUseCaseInterface { /** * @param UserCreateRequest $request

    * @return UserCreateResponse */ public function handle(UserCreateRequest $request): UserCreateResponse { $userId = new UserId(uniqid()); $user = new User($userId, $request->getName()); DB::table('users') ->updateOrInsert( ['id' => $user->getId()], ['name' => $user->getName()] ); return new UserCreateResponse($userId->getValue()); } } use Illuminate¥Support¥Facades¥DB;
  13. class UserCreateInteractor implements UserCreateUseCaseInterface { /** * @param UserCreateRequest $request

    * @return UserCreateResponse */ public function handle(UserCreateRequest $request): UserCreateResponse { $userId = new UserId(uniqid()); $user = new User($userId, $request->getName()); DB::table('users') ->updateOrInsert( ['id' => $user->getId()], ['name' => $user->getName()] ); return new UserCreateResponse($userId->getValue()); } } use Illuminate¥Support¥Facades¥DB;
  14. class UserCreateInteractor implements UserCreateUseCaseInterface { /** * @param UserCreateRequest $request

    * @return UserCreateResponse */ public function handle(UserCreateRequest $request): UserCreateResponse { $userId = new UserId(uniqid()); $user = new User($userId, $request->getName()); DB::table('users') ->updateOrInsert( ['id' => $user->getId()], ['name' => $user->getName()] ); return new UserCreateResponse($userId->getValue()); } } use Illuminate¥Support¥Facades¥DB;
  15. class UserCreateInteractor implements UserCreateUseCaseInterface { /** * @param UserCreateRequest $request

    * @return UserCreateResponse */ public function handle(UserCreateRequest $request): UserCreateResponse { $userId = new UserId(uniqid()); $user = new User($userId, $request->getName()); DB::table('users') ->updateOrInsert( ['id' => $user->getId()], ['name' => $user->getName()] ); return new UserCreateResponse($userId->getValue()); } } use Illuminate¥Support¥Facades¥DB;
  16. class UserCreateInteractor implements UserCreateUseCaseInterface { /** * @param UserCreateRequest $request

    * @return UserCreateResponse */ public function handle(UserCreateRequest $request): UserCreateResponse { $userId = new UserId(uniqid()); $user = new User($userId, $request->getName()); DB::table('users') ->updateOrInsert( ['id' => $user->getId()], ['name' => $user->getName()] ); return new UserCreateResponse($userId->getValue()); } } use Illuminate¥Support¥Facades¥DB;
  17. class UserCreateInteractor implements UserCreateUseCaseInterface { /** * @param UserCreateRequest $request

    * @return UserCreateResponse */ public function handle(UserCreateRequest $request): UserCreateResponse { $userId = new UserId(uniqid()); $user = new User($userId, $request->getName()); DB::table('users') ->updateOrInsert( ['id' => $user->getId()], ['name' => $user->getName()] ); return new UserCreateResponse($userId->getValue()); } } use Illuminate¥Support¥Facades¥DB;
  18. class UserCreateInteractor implements UserCreateUseCaseInterface { /** * @param UserCreateRequest $request

    * @return UserCreateResponse */ public function handle(UserCreateRequest $request): UserCreateResponse { $userId = new UserId(uniqid()); $user = new User($userId, $request->getName()); DB::table('users') ->updateOrInsert( ['id' => $user->getId()], ['name' => $user->getName()] ); return new UserCreateResponse($userId->getValue()); } } use Illuminate¥Support¥Facades¥DB;
  19. class UserCreateInteractor implements UserCreateUseCaseInterface { /** * @param UserCreateRequest $request

    * @return UserCreateResponse */ public function handle(UserCreateRequest $request): UserCreateResponse { $userId = new UserId(uniqid()); $user = new User($userId, $request->getName()); DB::table('users') ->updateOrInsert( ['id' => $user->getId()], ['name' => $user->getName()] ); return new UserCreateResponse($userId->getValue()); } } use Illuminate¥Support¥Facades¥DB;
  20. class UserCreateInteractor implements UserCreateUseCaseInterface { /** * @param UserCreateRequest $request

    * @return UserCreateResponse */ public function handle(UserCreateRequest $request): UserCreateResponse { $userId = new UserId(uniqid()); $user = new User($userId, $request->getName()); DB::table('users') ->updateOrInsert( ['id' => $user->getId()], ['name' => $user->getName()] ); return new UserCreateResponse($userId->getValue()); } } use Illuminate¥Support¥Facades¥DB;
  21. class UserCreateInteractor implements UserCreateUseCaseInterface { /** * @param UserCreateRequest $request

    * @return UserCreateResponse */ public function handle(UserCreateRequest $request): UserCreateResponse { $userId = new UserId(uniqid()); $user = new User($userId, $request->getName()); DB::table('users') ->updateOrInsert( ['id' => $user->getId()], ['name' => $user->getName()] ); return new UserCreateResponse($userId->getValue()); } } use Illuminate¥Support¥Facades¥DB;
  22. class UserCreateInteractor implements UserCreateUseCaseInterface { /** * @param UserCreateRequest $request

    * @return UserCreateResponse */ public function handle(UserCreateRequest $request): UserCreateResponse { $userId = new UserId(uniqid()); $user = new User($userId, $request->getName()); DB::table('users') ->updateOrInsert( ['id' => $user->getId()], ['name' => $user->getName()] ); return new UserCreateResponse($userId->getValue()); } } use Illuminate¥Support¥Facades¥DB;
  23. class UserCreateInteractor implements UserCreateUseCaseInterface { /** * @param UserCreateRequest $request

    * @return UserCreateResponse */ public function handle(UserCreateRequest $request): UserCreateResponse { $userId = new UserId(uniqid()); $user = new User($userId, $request->getName()); DB::table('users') ->updateOrInsert( ['id' => $user->getId()], ['name' => $user->getName()] ); return new UserCreateResponse($userId->getValue()); } } use Illuminate¥Support¥Facades¥DB;
  24. class UserCreateInteractor implements UserCreateUseCaseInterface { /** * @param UserCreateRequest $request

    * @return UserCreateResponse */ public function handle(UserCreateRequest $request): UserCreateResponse { $userId = new UserId(uniqid()); $user = new User($userId, $request->getName()); DB::table('users') ->updateOrInsert( ['id' => $user->getId()], ['name' => $user->getName()] ); return new UserCreateResponse($userId->getValue()); } } use Illuminate¥Support¥Facades¥DB;
  25. class UserCreateInteractor implements UserCreateUseCaseInterface { /** * @param UserCreateRequest $request

    * @return UserCreateResponse */ public function handle(UserCreateRequest $request): UserCreateResponse { $userId = new UserId(uniqid()); $user = new User($userId, $request->getName()); DB::table('users') ->updateOrInsert( ['id' => $user->getId()], ['name' => $user->getName()] ); return new UserCreateResponse($userId->getValue()); } } use Illuminate¥Support¥Facades¥DB;
  26. class UserCreateInteractor implements UserCreateUseCaseInterface { /** * @param UserCreateRequest $request

    * @return UserCreateResponse */ public function handle(UserCreateRequest $request): UserCreateResponse { $userId = new UserId(uniqid()); $user = new User($userId, $request->getName()); DB::table('users') ->updateOrInsert( ['id' => $user->getId()], ['name' => $user->getName()] ); return new UserCreateResponse($userId->getValue()); } } use Illuminate¥Support¥Facades¥DB;
  27. class UserCreateInteractor implements UserCreateUseCaseInterface { /** * @param UserCreateRequest $request

    * @return UserCreateResponse */ public function handle(UserCreateRequest $request): UserCreateResponse { $userId = new UserId(uniqid()); $user = new User($userId, $request->getName()); DB::table('users') ->updateOrInsert( ['id' => $user->getId()], ['name' => $user->getName()] ); return new UserCreateResponse($userId->getValue()); } } use Illuminate¥Support¥Facades¥DB;
  28. class UserCreateInteractor implements UserCreateUseCaseInterface { private $userRepository; public function __construct(QueryBuilderUserRepository

    $userRepository) { $this->userRepository = $userRepository; } public function handle(UserCreateRequest $request): UserCreateResponse { $userId = new UserId(uniqid()); $user = new User($userId, $request->getName()); $this->userRepository->save($user); return new UserCreateResponse($userId->getValue()); } }
  29. class UserCreateInteractor implements UserCreateUseCaseInterface { private $userRepository; public function __construct(QueryBuilderUserRepository

    $userRepository) { $this->userRepository = $userRepository; } public function handle(UserCreateRequest $request): UserCreateResponse { $userId = new UserId(uniqid()); $user = new User($userId, $request->getName()); $this->userRepository->save($user); return new UserCreateResponse($userId->getValue()); } }
  30. class QueryBuilderUserRepository implements UserRepositoryInterface { public function save(User $user) {

    DB::table('users') ->updateOrInsert( ['id' => $user->getId()], ['name' => $user->getName()] ); } }
  31. class UserCreateInteractor implements UserCreateUseCaseInterface { private $userRepository; public function __construct(QueryBuilderUserRepository

    $userRepository) { $this->userRepository = $userRepository; } public function handle(UserCreateRequest $request): UserCreateResponse { $userId = new UserId(uniqid()); $user = new User($userId, $request->getName()); $this->userRepository->save($user); return new UserCreateResponse($userId->getValue()); } }
  32. class UserCreateInteractor implements UserCreateUseCaseInterface { private $userRepository; public function __construct(QueryBuilderUserRepository

    $userRepository) { $this->userRepository = $userRepository; } public function handle(UserCreateRequest $request): UserCreateResponse { $userId = new UserId(uniqid()); $user = new User($userId, $request->getName()); $this->userRepository->save($user); return new UserCreateResponse($userId->getValue()); } }
  33. class UserCreateInteractor implements UserCreateUseCaseInterface { private $userRepository; public function __construct(UserRepositoryInterface

    $userRepository) { $this->userRepository = $userRepository; } public function handle(UserCreateRequest $request): UserCreateResponse { $userId = new UserId(uniqid()); $user = new User($userId, $request->getName()); $this->userRepository->save($user); return new UserCreateResponse($userId->getValue()); } }
  34. class UserCreateInteractor implements UserCreateUseCaseInterface { private $userRepository; public function __construct(UserRepositoryInterface

    $userRepository) { $this->userRepository = $userRepository; } public function handle(UserCreateRequest $request): UserCreateResponse { $userId = new UserId(uniqid()); $user = new User($userId, $request->getName()); $this->userRepository->save($user); return new UserCreateResponse($userId->getValue()); } }
  35. class UserCreateInteractor implements UserCreateUseCaseInterface { private $userRepository; public function __construct(QueryBuilderUserRepository

    $userRepository) { $this->userRepository = $userRepository; } public function handle(UserCreateRequest $request): UserCreateResponse { $userId = new UserId(uniqid()); $user = new User($userId, $request->getName()); $this->userRepository->save($user); return new UserCreateResponse($userId->getValue()); } }
  36. class UserCreateInteractor implements UserCreateUseCaseInterface { private $userRepository; public function __construct(QueryBuilderUserRepository

    $userRepository) { $this->userRepository = $userRepository; } public function handle(UserCreateRequest $request): UserCreateResponse { $userId = new UserId(uniqid()); $user = new User($userId, $request->getName()); $this->userRepository->save($user); return new UserCreateResponse($userId->getValue()); } }
  37. class UserCreateInteractor implements UserCreateUseCaseInterface { private $userRepository; public function __construct(QueryBuilderUserRepository

    $userRepository) { $this->userRepository = $userRepository; } public function handle(UserCreateRequest $request): UserCreateResponse { $userId = new UserId(uniqid()); $user = new User($userId, $request->getName()); $this->userRepository->save($user); return new UserCreateResponse($userId->getValue()); } }
  38. class QueryBuilderUserRepository implements UserRepositoryInterface { public function save(User $user) {

    DB::table('users') ->updateOrInsert( ['id' => $user->getId()], ['name' => $user->getName()] ); } }
  39. class QueryBuilderUserRepository implements UserRepositoryInterface { public function save(User $user) {

    DB::table('users') ->updateOrInsert( ['id' => $user->getId()], ['name' => $user->getName()] ); } }
  40. class UserCreateInteractor implements UserCreateUseCaseInterface { private $userRepository; public function __construct(UserRepositoryInterface

    $userRepository) { $this->userRepository = $userRepository; } public function handle(UserCreateRequest $request) { $userId = new UserId(uniqid()); $createdUser = new User($userId, $request->getName()); $this->userRepository->save($createdUser); return new UserCreateResponse($userId->getValue()); } }
  41. class UserCreateInteractor implements UserCreateUseCaseInterface { private $userRepository; public function __construct(UserRepositoryInterface

    $userRepository) { $this->userRepository = $userRepository; } public function handle(UserCreateRequest $request) { $userId = new UserId(uniqid()); $createdUser = new User($userId, $request->getName()); $this->userRepository->save($createdUser); return new UserCreateResponse($userId->getValue()); } }
  42. class UserController extends BaseController { public function create(UserCreateUseCaseInterface $interactor, Request

    $request) { $name = $request->input('name'); $request = new UserCreateRequest($name); $response = $interactor->handle($request); $viewModel = new UserCreateViewModel($response->getCreatedUserId(), $name); return view('user.create', compact('viewModel')); } }
  43. class UserController extends BaseController { public function create(UserCreateUseCaseInterface $interactor, Request

    $request) { $name = $request->input('name'); $request = new UserCreateRequest($name); $response = $interactor->handle($request); $viewModel = new UserCreateViewModel($response->getCreatedUserId(), $name); return view('user.create', compact('viewModel')); } }
  44. @Saga class SecureStockIssueSaga { @Transient @Autowired private lateinit var commandGateway:

    CommandGateway private lateinit var sagaState: SecureStockIssuerSagaState @StartSaga @SagaEventHandler(associationProperty = "secureStockId") fun on(event: SecureStockEvents.SecureStockIssued, @MessageIdentifier messageIdentifier: String) { sagaState = SecureStockIssuerSagaState(event.secureStockId, secureItemIdToNr = event.itemIdToNr) log("[START SAGA] Handling the Secure Stock Issued Event within the Saga ($sagaState).") sagaState.secureItemIdToNr.keys.forEach { SagaLifecycle.associateWith("itemId", it.asString()) } val commands = event.itemIdToNr.map { (itemId, nr) -> ItemAggregateProtocol.SecureItem(itemId, event.secureStockId, nr, messageIdentifier) } val replies = commands.map { commandGateway.sendAndWait<ItemAggregateProtocol.SecureItemReply>(it) } if (replies.any { it is ItemAggregateProtocol.SecureItemFailed }) { val secureSucceededList = replies.filterIsInstance<ItemAggregateProtocol.SecureItemSucceeded>() compensateSecureStockIssued(secureSucceededList) } } }
  45. @Saga class SecureStockIssueSaga { @Transient @Autowired private lateinit var commandGateway:

    CommandGateway private lateinit var sagaState: SecureStockIssuerSagaState @StartSaga @SagaEventHandler(associationProperty = "secureStockId") fun on(event: SecureStockEvents.SecureStockIssued, @MessageIdentifier messageIdentifier: String) { sagaState = SecureStockIssuerSagaState(event.secureStockId, secureItemIdToNr = event.itemIdToNr) log("[START SAGA] Handling the Secure Stock Issued Event within the Saga ($sagaState).") sagaState.secureItemIdToNr.keys.forEach { SagaLifecycle.associateWith("itemId", it.asString()) } val commands = event.itemIdToNr.map { (itemId, nr) -> ItemAggregateProtocol.SecureItem(itemId, event.secureStockId, nr, messageIdentifier) } val replies = commands.map { commandGateway.sendAndWait<ItemAggregateProtocol.SecureItemReply>(it) } if (replies.any { it is ItemAggregateProtocol.SecureItemFailed }) { val secureSucceededList = replies.filterIsInstance<ItemAggregateProtocol.SecureItemSucceeded>() compensateSecureStockIssued(secureSucceededList) } } }
  46. 伝統的なソフトウェア開発手法では高レベルなモジュールが低レベルな モジュールに依存する形で作成される傾向がありました。言い換えるな ら抽象が詳細に依存するような形で構築されていました。 抽象が詳細に依存するようになると、低レベルのモジュールにおける方 針の変更が高レベルのモジュールに波及します。これはおかしな話です。 重要なドメインのルールが含まれるのはいつだって高レベルなモジュー ルです。低レベルなモジュールの変更を理由にして、重要な高レベルの モジュールを変更する (たとえばデータストアの変更を理由にビジネスロジックを変更する)な どということは起きてほしくない事態です。

    主体となるべきは高レベルなモジュール、すなわち抽象です。低レベル なモジュールが主体となるべきではありません。 高レベルなモジュールは低レベルのモジュールを利用するクライアント です。クライアントがすべきはどのような処理を呼び出すかの宣言です。 先述したとおり、インターフェースはそれを利用するクライアントが宣言す るものであり、主導権はそのクライアントにあります。インターフェースを宣 言し、低レベルのモジュールはそのインターフェースに合わせて実装を行 うことで、より重要な高次元の概念に主導権を握らせることが可能にな るのです。 『ドメイン駆動設計入門 ボトムアップでわかる!ドメイン駆動設計の基本(翔泳社)』 7.3.2