$30 off During Our Annual Pro Sale. View Details »

依存関係のコントロール / 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. None
  2. None
  3. None
  4. 他に頼って存在、または生活すること。 デジタル大辞泉

  5. None
  6. None
  7. None
  8. None
  9. None
  10. None
  11. None
  12. None
  13. None
  14. None
  15. $msg = "hello"; var_dump($msg);

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

    } $foo = new Foo(); $foo->greet();
  17. 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();
  18. 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();
  19. 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();
  20. 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();
  21. 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();
  22. 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();
  23. 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; } }
  24. 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; } }
  25. 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; } }
  26. 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; } }
  27. None
  28. 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;
  29. 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;
  30. 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;
  31. 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;
  32. 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;
  33. 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;
  34. 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;
  35. 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;
  36. 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;
  37. 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;
  38. None
  39. None
  40. None
  41. 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;
  42. 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;
  43. None
  44. None
  45. None
  46. None
  47. None
  48. None
  49. 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;
  50. 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;
  51. 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;
  52. 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;
  53. None
  54. None
  55. None
  56. None
  57. None
  58. None
  59. None
  60. None
  61. None
  62. None
  63. None
  64. None
  65. None
  66. None
  67. None
  68. class QueryBuilderUserRepository { public function save(User $user) { DB::table('users') ->updateOrInsert(

    ['id' => $user->getId()], ['name' => $user->getName()] ); } }
  69. 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()); } }
  70. 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()); } }
  71. None
  72. None
  73. interface UserRepositoryInterface { public function save(User $user); }

  74. None
  75. None
  76. class QueryBuilderUserRepository { public function save(User $user) { DB::table('users') ->updateOrInsert(

    ['id' => $user->getId()], ['name' => $user->getName()] ); } }
  77. class QueryBuilderUserRepository implements UserRepositoryInterface { public function save(User $user) {

    DB::table('users') ->updateOrInsert( ['id' => $user->getId()], ['name' => $user->getName()] ); } }
  78. None
  79. None
  80. 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()); } }
  81. 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()); } }
  82. 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()); } }
  83. 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()); } }
  84. None
  85. None
  86. None
  87. None
  88. None
  89. None
  90. 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()); } }
  91. 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()); } }
  92. 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()); } }
  93. None
  94. None
  95. None
  96. None
  97. None
  98. class QueryBuilderUserRepository implements UserRepositoryInterface { public function save(User $user) {

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

    DB::table('users') ->updateOrInsert( ['id' => $user->getId()], ['name' => $user->getName()] ); } }
  100. None
  101. None
  102. None
  103. None
  104. None
  105. None
  106. $repository = new QueryBuilderUserRepository(); $interactor = new UserCreateInteractor($repository); $interactor->handle(new UserCreateRequest("nrs"));

  107. $repository = new QueryBuilderUserRepository(); $interactor = new UserCreateInteractor($repository); $interactor->handle(new UserCreateRequest("nrs"));

  108. $repository = new AwesomeUserRepository(); $interactor = new UserCreateInteractor($repository); $interactor->handle(new UserCreateRequest("nrs"));

  109. $repository = new AwesomeUserRepository(); $interactor = new UserCreateInteractor($repository); $interactor->handle(new UserCreateRequest("nrs"));

  110. $repository = new AwesomeUserRepository(); $interactor = new UserCreateInteractor($repository); $interactor->handle(new UserCreateRequest("nrs"));

  111. None
  112. $this->app->bind( UserRepositoryInterface::class, QueryBuilderUserRepository::class );

  113. None
  114. None
  115. 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()); } }
  116. 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()); } }
  117. None
  118. 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')); } }
  119. 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')); } }
  120. None
  121. @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) } } }
  122. @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) } } }
  123. None
  124. None
  125. None
  126. None
  127. None
  128. None
  129. None
  130. 伝統的なソフトウェア開発手法では高レベルなモジュールが低レベルな モジュールに依存する形で作成される傾向がありました。言い換えるな ら抽象が詳細に依存するような形で構築されていました。 抽象が詳細に依存するようになると、低レベルのモジュールにおける方 針の変更が高レベルのモジュールに波及します。これはおかしな話です。 重要なドメインのルールが含まれるのはいつだって高レベルなモジュー ルです。低レベルなモジュールの変更を理由にして、重要な高レベルの モジュールを変更する (たとえばデータストアの変更を理由にビジネスロジックを変更する)な どということは起きてほしくない事態です。

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

  133. None
  134. None
  135. None
  136. None
  137. None
  138. None
  139. None
  140. None
  141. None
  142. None
  143. None
  144. None
  145. None
  146. None
  147. None
  148. None
  149. None
  150. None
  151. None
  152. None
  153. None
  154. None
  155. None
  156. None
  157. None
  158. None
  159. None
  160. None
  161. None
  162. None
  163. OO Robert C. Martin 『CleanArchitecture ( )』

  164. None
  165. None
  166. None
  167. None
  168. None
  169. None
  170. None
  171. None
  172. None
  173. None
  174. None
  175. None
  176. None
  177. None
  178. None
  179. None
  180. None
  181. None
  182. None
  183. None
  184. None
  185. OO Robert C. Martin 『CleanArchitecture ( )』

  186. None
  187. None