Pro Yearly is on sale from $80 to $50! »

思想と理想の果てに / At the end of thought and ideal

E37b4344ef4bfd0fc4826c04971e54fb?s=47 nrs
December 01, 2019

思想と理想の果てに / At the end of thought and ideal

at PHP Conference Japan 2019
各種 URL: https://nrslib.com/phpcon-2019-proposal/

# URL
HomePage: https://nrslib.com
Twitter: https://twitter.com/nrslib

E37b4344ef4bfd0fc4826c04971e54fb?s=128

nrs

December 01, 2019
Tweet

Transcript

  1. 思想と理想の果てに Masanobu Naruse 1

  2. 2

  3. 3

  4. 4

  5. 5

  6. 6

  7. 7

  8. 8 1. フレームワーク非依存 2. テスタビリティの確保 3. UI非依存 4. データベース非依存 5.

    外部機能非依存
  9. 9 1. フレームワーク非依存 2. テスタビリティの確保 3. UI非依存 4. データベース非依存 5.

    外部機能非依存
  10. 10

  11. なぜ推すか

  12. 12

  13. 13 これは どこに書こう

  14. 14 質問された仕様の 記述されてる場所が 分からない これは どこに書こう

  15. 15 質問された仕様の 記述されてる場所が 分からない これは どこに書こう 本質的でないことから あなたを解放する

  16. 16

  17. 17 今日はスーパーの セール日だ 今週はどこに 出かけよう かな

  18. 18 クリーンアーキテクチャを知る フレームワークを作る エピローグ

  19. 19 クリーンアーキテクチャを知る フレームワークを作る エピローグ

  20. 20

  21. 21 この図の前に

  22. 22 まずはこの図 22

  23. 23 まずはこの図 ヘキサゴナルアーキテクチャ 23

  24. 24 24

  25. 25 ビジネスを中心に見立て それ以外を交換可能なものとする 25

  26. 26 ビジネスを中心に見立て それ以外を交換可能なものとする プラガプル 26

  27. 27 別名 ポートアンドアダプター 27

  28. 28 別名 ポートアンドアダプター ポート アダプター 28

  29. 29 たとえるなら 29

  30. 30 30

  31. 31 31

  32. 32 32

  33. 33 33

  34. 34 34

  35. 35 35

  36. 36 36

  37. 37 37

  38. 38 38

  39. 39 39

  40. 40 40

  41. 41 In Software 41

  42. 42 42

  43. 43 43

  44. 44 44

  45. 45 45

  46. 46 46

  47. 47 47

  48. 48 48

  49. 49 アプリケーションを中心に見据え プラガプルを実現 49

  50. 50 アプリケーションを中心に見据え プラガプルを実現 50 ビジネスロジックを UIやデータストアの処理に点在させない

  51. 51 51

  52. 52 52

  53. 53 53

  54. 54 根底にあるもの 54

  55. 55 SOLID 原則 55

  56. 56 SOLID 原則 56

  57. 57 Dependency Inversion Principle 57

  58. 58 58 Business Logic Database Access Module

  59. 59 59 Business Logic Database Access Module Change

  60. 60 60 Business Logic Database Access Module Change Involve

  61. 61 61 Business Logic Database Access Module Data Access Interface

    <I>
  62. 62 62 Business Logic Database Access Module Data Access Interface

    <I>
  63. 63 63 Business Logic Database Access Module Data Access Interface

    <I>
  64. 64 64 Business Logic Database Access Module Data Access Interface

    <I>
  65. 65 65 Business Logic Database Access Module Data Access Interface

    <I>
  66. 66 66 Business Logic Database Access Module Data Access Interface

    Change <I>
  67. 67 67 Business Logic Database Access Module Data Access Interface

    Change Involve <I>
  68. 68 68 Business Logic Database Access Module Data Access Interface

    Change Involve Involve <I>
  69. 69 69 Business Logic Database Access Module Data Access Interface

    Change Involve Involve leading <I>
  70. 70 70

  71. 71 71

  72. 72 具体的な実現方法を 言及している 違いは?

  73. 73 具体的な実現方法を 言及している 具体的な実現方法を 言及している

  74. 74 74

  75. 75 75

  76. 76 76

  77. 図を詳しく見る 77

  78. 78

  79. 79

  80. 80 Enterprise Business Rules Entities

  81. 81 Enterprise Business Rules Entities ビジネスルールをカプセル化したオブジェクト ≒ドメインオブジェクト

  82. 82

  83. 83 Application Business Rules Use Cases

  84. 84 Application Business Rules Use Cases アプリケーションレイヤー ドメインオブジェクトを束ねあげ ユースケースを実現する

  85. 85

  86. 86 Interface Adapters

  87. 87 Controllers Presenters Gateways Interface Adapters

  88. 88 Controllers Presenters Gateways Interface Adapters

  89. 89 Controllers Presenters Gateways Interface Adapters

  90. 90 Controllers Presenters Gateways Interface Adapters Mock

  91. 91

  92. 92 Controller Presenter

  93. 93 Controller Presenter

  94. 94 Controller Presenter

  95. 95 Controller Presenter

  96. 96 Controller Presenter Use Case Interactor

  97. 97 Controller Presenter Use Case Interactor Use Case Input Port

    Use Case Output Port
  98. 98 Controller Presenter Use Case Interactor Use Case Input Port

    Use Case Output Port < I > < I >
  99. 99 Controller Presenter Use Case Interactor Use Case Input Port

    Use Case Output Port < I > < I >
  100. 100 Controller Presenter Use Case Interactor Use Case Input Port

    Use Case Output Port < I > < I >
  101. 101 Controller Presenter Use Case Interactor Use Case Input Port

    Use Case Output Port < I > < I >
  102. 102 Controller Presenter Use Case Interactor Use Case Input Port

    Use Case Output Port < I > < I >
  103. 103 Frameworks & Drivers

  104. 104 Frameworks & Drivers 詳細なコード ギークなコード ビジネスロジックが これに依存しないようにする

  105. 105 Frameworks & Drivers 詳細なコード ギークなコード ビジネスロジックが これに依存しないようにする DIP

  106. 106 依存の方向

  107. 107 依存の方向は内向き 内側の変更は外に影響する 内側は外側を知らない 依存の方向

  108. 108 依存の方向は内向き 内側の変更は外に影響する 内側は外側を知らない ドメインロジックで Web とか DB とか UI

    とかを 扱わない 依存の方向
  109. 実装例 109

  110. 110 110 Controller Presenter Use Case Interactor Use Case Input

    Port Use Case Output Port < I > < I >
  111. 111 Controller Presenter Use Case Interactor Use Case Input Port

    Use Case Output Port < I > < I > もっと細かく
  112. 112 図 22-2 「Clean Architecture 達人に学ぶソフトウェアの構造と設計」(Robert C. Martin)より

  113. 113 図 22-2 「Clean Architecture 達人に学ぶソフトウェアの構造と設計」(Robert C. Martin)より

  114. 114 図 22-2 「Clean Architecture 達人に学ぶソフトウェアの構造と設計」(Robert C. Martin)より

  115. 115 図 22-2 「Clean Architecture 達人に学ぶソフトウェアの構造と設計」(Robert C. Martin)より 上下は逆で 用語も少し違うけど

    同じ
  116. 116 図 22-2 「Clean Architecture 達人に学ぶソフトウェアの構造と設計」(Robert C. Martin)より 上下は逆で 用語も少し違うけど

    同じ これに従って実装
  117. 117 1.コードを確認 2.処理の流れを確認

  118. 118 1.コードを確認 2.処理の流れを確認

  119. 119

  120. 120

  121. 121

  122. 122 class UserController { private $inputPort; public function __construct(UserCreateInputPortInterface $inputPort)

    { $this->inputPort = $inputPort; } public function create(string $name, string $roleId) { $role = $this->convertRole($roleId); $inputData = new UserCreateInputData($name, $role); $this->inputPort->handle($inputData); } private function convertRole(string $roleId): string { switch ($roleId) { case 'admin': return UserRole::ADMIN; case 'member': return UserRole::MEMBER; default: throw new ¥Exception(); } } }
  123. 123 class UserController { private $inputPort; public function __construct(UserCreateInputPortInterface $inputPort)

    { $this->inputPort = $inputPort; } public function create(string $name, string $roleId) { $role = $this->convertRole($roleId); $inputData = new UserCreateInputData($name, $role); $this->inputPort->handle($inputData); } private function convertRole(string $roleId): string { switch ($roleId) { case 'admin': return UserRole::ADMIN; case 'member': return UserRole::MEMBER; default: throw new ¥Exception(); } } } アプリケーションが 要求するデータに 入力を変換
  124. 124 class UserController { private $inputPort; public function __construct(UserCreateInputPortInterface $inputPort)

    { $this->inputPort = $inputPort; } public function create(string $name, string $roleId) { $role = $this->convertRole($roleId); $inputData = new UserCreateInputData($name, $role); $this->inputPort->handle($inputData); } private function convertRole(string $roleId): string { switch ($roleId) { case 'admin': return UserRole::ADMIN; case 'member': return UserRole::MEMBER; default: throw new ¥Exception(); } } } アプリケーションが 要求するデータに 入力を変換
  125. 125

  126. 126

  127. 127 DS : Data Structure

  128. 128 DS : Data Structure class UserCreateInputData { private $name;

    private $roleId; public function __construct(string $name, string $roleId) { $this->name = $name; $this->roleId = $roleId; } public function getName(): string { return $this->name; } public function getRoleId(): string { return $this->roleId; } }
  129. 129

  130. 130

  131. 131 interface UserCreateInputPortInterface { function handle(UserCreateInputData $inputData); }

  132. 132

  133. 133

  134. 134 class UserCreateInteractor implements UserCreateInputPortInterface { private $repository; private $outputPort;

    public function __construct( UserRepositoryInterface $repository, UserCreateOutputPortInterface $outputPort) { $this->repository = $repository; $this->outputPort = $outputPort; } public function handle(UserCreateInputData $inputData) { $id = uniqid(); $user = new User ( new UserId($id), new UserName($inputData->getName()), $this->getRole($inputData->getRole()) ); $this->repository->save($user); $outputdata = new UserCreateOutputData($id); $this->outputPort->output($outputdata); } ... }
  135. 135 class UserCreateInteractor implements UserCreateInputPortInterface { private $repository; private $outputPort;

    public function __construct( UserRepositoryInterface $repository, UserCreateOutputPortInterface $outputPort) { $this->repository = $repository; $this->outputPort = $outputPort; } public function handle(UserCreateInputData $inputData) { $id = uniqid(); $user = new User ( new UserId($id), new UserName($inputData->getName()), $this->getRole($inputData->getRole()) ); $this->repository->save($user); $outputdata = new UserCreateOutputData($id); $this->outputPort->output($outputdata); } ... }
  136. 136 class UserCreateInteractor implements UserCreateInputPortInterface { private $repository; private $outputPort;

    public function __construct( UserRepositoryInterface $repository, UserCreateOutputPortInterface $outputPort) { $this->repository = $repository; $this->outputPort = $outputPort; } public function handle(UserCreateInputData $inputData) { $id = uniqid(); $user = new User ( new UserId($id), new UserName($inputData->getName()), $this->getRole($inputData->getRole()) ); $this->repository->save($user); $outputdata = new UserCreateOutputData($id); $this->outputPort->output($outputdata); } ... }
  137. 137 class UserCreateInteractor implements UserCreateInputPortInterface { private $repository; private $outputPort;

    public function __construct( UserRepositoryInterface $repository, UserCreateOutputPortInterface $outputPort) { $this->repository = $repository; $this->outputPort = $outputPort; } public function handle(UserCreateInputData $inputData) { $id = uniqid(); $user = new User ( new UserId($id), new UserName($inputData->getName()), $this->getRole($inputData->getRole()) ); $this->repository->save($user); $outputdata = new UserCreateOutputData($id); $this->outputPort->output($outputdata); } ... }
  138. 138

  139. 139

  140. 140 interface UserRepositoryInterface { function find(UserId $id): User; function save(User

    $user); function delete(User $user); }
  141. 141 interface UserRepositoryInterface { function find(UserId $id): User; function save(User

    $user); function delete(User $user); }
  142. 142 interface UserRepositoryInterface { function find(UserId $id): User; function save(User

    $user); function delete(User $user); } Gateways
  143. 143

  144. 144

  145. 145 class UserRepository implements UserRepositoryInterface { public function save(User $user)

    { DB::table('users') ->updateOrInsert( ['id' => $user->getId()], ['name' => $user->getName()] ); } public function find(UserId $id) { $user = DB::table('users')->where('id', $id->getValue())->first(); return new User($id, $user->name); } ... } オブジェクトの 永続化・再構築が できればなんでもいい
  146. 146

  147. 147

  148. 148 class User { private $id; private $name private $role;

    public function __construct( UserId $id, UserName $name, UserRole $role ) { if (is_null($id)) throw new ¥Exception(); if (is_null($name)) throw new ¥Exception(); if (is_null($role)) throw new ¥Exception(); $this->id = $id; $this->name = $name; $this->role = $role; } ... public function changeName(UserName $name) { if (is_null($name)) throw new ¥Exception(); $this->name = $name; } }
  149. 149 class User { private $id; private $name private $role;

    public function __construct( UserId $id, UserName $name, UserRole $role ) { if (is_null($id)) throw new ¥Exception(); if (is_null($name)) throw new ¥Exception(); if (is_null($role)) throw new ¥Exception(); $this->id = $id; $this->name = $name; $this->role = $role; } ... public function changeName(UserName $name) { if (is_null($name)) throw new ¥Exception(); $this->name = $name; } } class UserName { private $value; public function __construct(string $value) { if (is_null($value)) throw new ¥Exception(); if (strlen($value) < 3) throw new ¥Exception(); if (strlen($value) > 10) throw new ¥Exception(); $this->value = $value; } public function getValue(): string { return $this->value; } }
  150. 150 class User { private $id; private $name private $role;

    public function __construct( UserId $id, UserName $name, UserRole $role ) { if (is_null($id)) throw new ¥Exception(); if (is_null($name)) throw new ¥Exception(); if (is_null($role)) throw new ¥Exception(); $this->id = $id; $this->name = $name; $this->role = $role; } ... public function changeName(UserName $name) { if (is_null($name)) throw new ¥Exception(); $this->name = $name; } } class UserName { private $value; public function __construct(string $value) { if (is_null($value)) throw new ¥Exception(); if (strlen($value) < 3) throw new ¥Exception(); if (strlen($value) > 10) throw new ¥Exception(); $this->value = $value; } public function getValue(): string { return $this->value; } } データモデルとは異なる
  151. 151

  152. 152

  153. 153 class UserCreateOutputData { private $createdId; public function __construct(string $createdId)

    { $this->createdId = $createdId; } public function getCreatedId(): string { return $this->createdId; } }
  154. 154

  155. 155

  156. 156 interface UserCreateOutputPortInterface { function output(UserCreateOutputData $outputData); }

  157. 157

  158. 158

  159. 159 class UserCreatePresenter implements UserCreateOutputPortInterface { private $middleware; public function

    __construct(ClarcMiddleware $middleware) { $this->middleware = $middleware; } public function output(UserCreateOutputData $outputData) { $viewModel = new UserCreateViewModel($outputData); $this->middleware->setData(view('user.create', compact('viewModel'))); } }
  160. 160

  161. 161

  162. 162 class UserCreateViewModel { private $createdId; public function __construct(UserCreateOutputData $source)

    { $this->createdId = $source->getCreatedId(); } public function getCreatedId(): string { return $this->createdId; } }
  163. 163 class UserCreateViewModel { private $createdId; public function __construct(UserCreateOutputData $source)

    { $this->createdId = $source->getCreatedId(); } public function getCreatedId(): string { return $this->createdId; } } <h1>Test</h1> <p> {{$viewModel->getCreatedId()}} </p>
  164. 164 1.コードを確認 2.処理の流れを確認

  165. 165

  166. 166 Flow of Control class UserController { private $inputPort; public

    function __construct(UserCreateInputPortInterface $inputPort) { $this->inputPort = $inputPort; } public function create(string $name, string $roleId) { $role = $this->convertRole($roleId); $inputData = new UserCreateInputData($name, $role); $this->inputPort->handle($inputData); } private function convertRole(string $roleId): string {
  167. 167 Flow of Control Create class UserController { private $inputPort;

    public function __construct(UserCreateInputPortInterface $inputPort) { $this->inputPort = $inputPort; } public function create(string $name, string $roleId) { $role = $this->convertRole($roleId); $inputData = new UserCreateInputData($name, $role); $this->inputPort->handle($inputData); } private function convertRole(string $roleId): string {
  168. 168 Flow of Control Call class UserController { private $inputPort;

    public function __construct(UserCreateInputPortInterface $inputPort) { $this->inputPort = $inputPort; } public function create(string $name, string $roleId) { $role = $this->convertRole($roleId); $inputData = new UserCreateInputData($name, $role); $this->inputPort->handle($inputData); } private function convertRole(string $roleId): string {
  169. 169 Flow of Control

  170. 170 Flow of Control interface UserCreateInputPortInterface { function handle(UserCreateInputData $inputData);

    }
  171. 171 Flow of Control Delegate interface UserCreateInputPortInterface { function handle(UserCreateInputData

    $inputData); }
  172. 172 Flow of Control

  173. 173 Flow of Control class UserCreateInteractor implements UserCreateInputPortInterface { private

    $repository; private $outputPort; public function __construct( UserRepositoryInterface $repository, UserCreateOutputPortInterface $outputPort) { $this->repository = $repository; $this->outputPort = $outputPort; } public function handle(UserCreateInputData $inputData) { $id = uniqid(); $user = new User ( new UserId($id), new UserName($inputData->getName()), $this->getRole($inputData->getRole()) ); $this->repository->save($user); $outputdata = new UserCreateOutputData($id); $this->outputPort->output($outputdata); }
  174. 174 Flow of Control Create class UserCreateInteractor implements UserCreateInputPortInterface {

    private $repository; private $outputPort; public function __construct( UserRepositoryInterface $repository, UserCreateOutputPortInterface $outputPort) { $this->repository = $repository; $this->outputPort = $outputPort; } public function handle(UserCreateInputData $inputData) { $id = uniqid(); $user = new User ( new UserId($id), new UserName($inputData->getName()), $this->getRole($inputData->getRole()) ); $this->repository->save($user); $outputdata = new UserCreateOutputData($id); $this->outputPort->output($outputdata); }
  175. 175 Flow of Control Call class UserCreateInteractor implements UserCreateInputPortInterface {

    private $repository; private $outputPort; public function __construct( UserRepositoryInterface $repository, UserCreateOutputPortInterface $outputPort) { $this->repository = $repository; $this->outputPort = $outputPort; } public function handle(UserCreateInputData $inputData) { $id = uniqid(); $user = new User ( new UserId($id), new UserName($inputData->getName()), $this->getRole($inputData->getRole()) ); $this->repository->save($user); $outputdata = new UserCreateOutputData($id); $this->outputPort->output($outputdata); }
  176. 176 Flow of Control interface UserRepositoryInterface { function find(UserId $id):

    User; function save(User $user); function delete(User $user); }
  177. 177 Flow of Control Delegate interface UserRepositoryInterface { function find(UserId

    $id): User; function save(User $user); function delete(User $user); }
  178. 178 Flow of Control class UserRepository implements UserRepositoryInterface { ...

    public function save(User $user) { DB::table('users') ->updateOrInsert( ['id' => $user->getId()], ['name' => $user->getName()] ); } public function find(UserId $id) { $user = DB::table('users')->where('id', $id->getValue())->first(); return new User($id, $user->name); } }
  179. 179 Flow of Control Access class UserRepository implements UserRepositoryInterface {

    ... public function save(User $user) { DB::table('users') ->updateOrInsert( ['id' => $user->getId()], ['name' => $user->getName()] ); } public function find(UserId $id) { $user = DB::table('users')->where('id', $id->getValue())->first(); return new User($id, $user->name); } }
  180. 180 Flow of Control class UserCreateInteractor implements UserCreateInputPortInterface { ...

    public function handle(UserCreateInputData $input { $id = uniqid(); $user = new User ( new UserId($id), new UserName($inputData->getName()), $this->getRole($inputData->getRole()) ); $this->repository->save($user); $outputdata = new UserCreateOutputData($id); $this->outputPort->output($outputdata); }
  181. 181 Flow of Control Create class UserCreateInteractor implements UserCreateInputPortInterface {

    ... public function handle(UserCreateInputData $input { $id = uniqid(); $user = new User ( new UserId($id), new UserName($inputData->getName()), $this->getRole($inputData->getRole()) ); $this->repository->save($user); $outputdata = new UserCreateOutputData($id); $this->outputPort->output($outputdata); }
  182. 182 Flow of Control Call class UserCreateInteractor implements UserCreateInputPortInterface {

    ... public function handle(UserCreateInputData $input { $id = uniqid(); $user = new User ( new UserId($id), new UserName($inputData->getName()), $this->getRole($inputData->getRole()) ); $this->repository->save($user); $outputdata = new UserCreateOutputData($id); $this->outputPort->output($outputdata); }
  183. 183 Flow of Control

  184. 184 Flow of Control interface UserCreateOutputPortInterface { function output(UserCreateOutputData $outputData);

    }
  185. 185 Flow of Control Delegate interface UserCreateOutputPortInterface { function output(UserCreateOutputData

    $outputData); }
  186. メリット

  187. 187 処理(InputPort)や出力(OutputPort)を 差し替えられる 187 interface UserCreateOutputPortInterface { function output(UserCreateOutputData $outputData);

    } interface UserCreateInputPortInterface { function handle(UserCreateInputData $inputData); }
  188. 188 188 Business Logic Database Access Module Data Access Interface

    Change Involve Involve leading 主導権をビジネスロジックに
  189. 代償

  190. 190 Browser Server Request Response

  191. 191 Browser Server interface UserCreateOutputPortInterface { function output(UserCreateOutputData $outputData); }

    Request Response
  192. 192 Browser Server interface UserCreateOutputPortInterface { function output(UserCreateOutputData $outputData); }

    Request Response
  193. 193 193 LaraClean OutputPort を使わない (実践= Web に適応)

  194. 194 194 LaraClean OutputPort を使わない (実践= Web に適応) 理想が現実に負けた気分

  195. 195 In programming

  196. 196 class UserController { private $inputPort; public function __construct(UserCreateInputPortInterface $inputPort)

    { $this->inputPort = $inputPort; } public function create(string $name, string $roleId) { $role = $this->convertRole($roleId); $inputData = new UserCreateInputData($name, $role); $this->inputPort->handle($inputData); } private function convertRole(string $roleId): string { switch ($roleId) { case 'admin': return UserRole::ADMIN; case 'member': return UserRole::MEMBER; default: throw new ¥Exception(); } } }
  197. 197 class UserController { private $inputPort; public function __construct(UserCreateInputPortInterface $inputPort)

    { $this->inputPort = $inputPort; } public function create(string $name, string $roleId) { $role = $this->convertRole($roleId); $inputData = new UserCreateInputData($name, $role); $this->inputPort->handle($inputData); } private function convertRole(string $roleId): string { switch ($roleId) { case 'admin': return UserRole::ADMIN; case 'member': return UserRole::MEMBER; default: throw new ¥Exception(); } } } class UserCreateInputData { private $name; private $roleId; public function __construct(string $name, string $roleId) { $this->name = $name; $this->roleId = $roleId; } public function getName(): string { return $this->name; } public function getRoleId(): string { return $this->roleId; } }
  198. 198 class UserController { private $inputPort; public function __construct(UserCreateInputPortInterface $inputPort)

    { $this->inputPort = $inputPort; } public function create(string $name, string $roleId) { $role = $this->convertRole($roleId); $inputData = new UserCreateInputData($name, $role); $this->inputPort->handle($inputData); } private function convertRole(string $roleId): string { switch ($roleId) { case 'admin': return UserRole::ADMIN; case 'member': return UserRole::MEMBER; default: throw new ¥Exception(); } } } class UserCreateInputData { private $name; private $roleId; public function __construct(string $name, string $roleId) { $this->name = $name; $this->roleId = $roleId; } public function getName(): string { return $this->name; } public function getRoleId(): string { return $this->roleId; } } interface UserCreateInputPortInterface { function handle(UserCreateInputData $inputData); }
  199. 199 class UserController { private $inputPort; public function __construct(UserCreateInputPortInterface $inputPort)

    { $this->inputPort = $inputPort; } public function create(string $name, string $roleId) { $role = $this->convertRole($roleId); $inputData = new UserCreateInputData($name, $role); $this->inputPort->handle($inputData); } private function convertRole(string $roleId): string { switch ($roleId) { case 'admin': return UserRole::ADMIN; case 'member': return UserRole::MEMBER; default: throw new ¥Exception(); } } } class UserCreateInputData { private $name; private $roleId; public function __construct(string $name, string $roleId) { $this->name = $name; $this->roleId = $roleId; } public function getName(): string { return $this->name; } public function getRoleId(): string { return $this->roleId; } } interface UserCreateInputPortInterface { function handle(UserCreateInputData $inputData); } class UserCreateInteractor implements UserCreateInputPortInterfac { private $repository; private $outputPort; public function __construct( UserRepositoryInterface $repository, UserCreateOutputPortInterface $outputPort) { $this->repository = $repository; $this->outputPort = $outputPort; } public function handle(UserCreateInputData $inputData) { $id = uniqid(); $user = new User ( new UserId($id), new UserName($inputData->getName()), $this->getRole($inputData->getRole()) );
  200. 200 class UserController { private $inputPort; public function __construct(UserCreateInputPortInterface $inputPort)

    { $this->inputPort = $inputPort; } public function create(string $name, string $roleId) { $role = $this->convertRole($roleId); $inputData = new UserCreateInputData($name, $role); $this->inputPort->handle($inputData); } private function convertRole(string $roleId): string { switch ($roleId) { case 'admin': return UserRole::ADMIN; case 'member': return UserRole::MEMBER; default: throw new ¥Exception(); } } } class UserCreateInputData { private $name; private $roleId; public function __construct(string $name, string $roleId) { $this->name = $name; $this->roleId = $roleId; } public function getName(): string { return $this->name; } public function getRoleId(): string { return $this->roleId; } } interface UserCreateInputPortInterface { function handle(UserCreateInputData $inputData); } class UserCreateInteractor implements UserCreateInputPortInterfac { private $repository; private $outputPort; public function __construct( UserRepositoryInterface $repository, UserCreateOutputPortInterface $outputPort) { $this->repository = $repository; $this->outputPort = $outputPort; } public function handle(UserCreateInputData $inputData) { $id = uniqid(); $user = new User ( new UserId($id), new UserName($inputData->getName()), $this->getRole($inputData->getRole()) ); interface UserCreateOutputPortInterface { function output(UserCreateOutputData $outputData); }
  201. 201 class UserController { private $inputPort; public function __construct(UserCreateInputPortInterface $inputPort)

    { $this->inputPort = $inputPort; } public function create(string $name, string $roleId) { $role = $this->convertRole($roleId); $inputData = new UserCreateInputData($name, $role); $this->inputPort->handle($inputData); } private function convertRole(string $roleId): string { switch ($roleId) { case 'admin': return UserRole::ADMIN; case 'member': return UserRole::MEMBER; default: throw new ¥Exception(); } } } class UserCreateInputData { private $name; private $roleId; public function __construct(string $name, string $roleId) { $this->name = $name; $this->roleId = $roleId; } public function getName(): string { return $this->name; } public function getRoleId(): string { return $this->roleId; } } interface UserCreateInputPortInterface { function handle(UserCreateInputData $inputData); } class UserCreateInteractor implements UserCreateInputPortInterfac { private $repository; private $outputPort; public function __construct( UserRepositoryInterface $repository, UserCreateOutputPortInterface $outputPort) { $this->repository = $repository; $this->outputPort = $outputPort; } public function handle(UserCreateInputData $inputData) { $id = uniqid(); $user = new User ( new UserId($id), new UserName($inputData->getName()), $this->getRole($inputData->getRole()) ); interface UserCreateOutputPortInterface { function output(UserCreateOutputData $outputData); } class UserCreateOutputData { private $createdId; public function __construct(string $createdId) { $this->createdId = $createdId; } public function getCreatedId(): string { return $this->createdId; } }
  202. 202 class UserController { private $inputPort; public function __construct(UserCreateInputPortInterface $inputPort)

    { $this->inputPort = $inputPort; } public function create(string $name, string $roleId) { $role = $this->convertRole($roleId); $inputData = new UserCreateInputData($name, $role); $this->inputPort->handle($inputData); } private function convertRole(string $roleId): string { switch ($roleId) { case 'admin': return UserRole::ADMIN; case 'member': return UserRole::MEMBER; default: throw new ¥Exception(); } } } class UserCreateInputData { private $name; private $roleId; public function __construct(string $name, string $roleId) { $this->name = $name; $this->roleId = $roleId; } public function getName(): string { return $this->name; } public function getRoleId(): string { return $this->roleId; } } interface UserCreateInputPortInterface { function handle(UserCreateInputData $inputData); } class UserCreateInteractor implements UserCreateInputPortInterfac { private $repository; private $outputPort; public function __construct( UserRepositoryInterface $repository, UserCreateOutputPortInterface $outputPort) { $this->repository = $repository; $this->outputPort = $outputPort; } public function handle(UserCreateInputData $inputData) { $id = uniqid(); $user = new User ( new UserId($id), new UserName($inputData->getName()), $this->getRole($inputData->getRole()) ); interface UserCreateOutputPortInterface { function output(UserCreateOutputData $outputData); } class UserCreateOutputData { private $createdId; public function __construct(string $createdId) { $this->createdId = $createdId; } public function getCreatedId(): string { return $this->createdId; } } class UserCreatePresenter implements UserCreateOutputPor { private $middleware; public function __construct(ClarcMiddleware $middlew { $this->middleware = $middleware; } public function output(UserCreateOutputData $outputD { $viewModel = new UserCreateViewModel($outputData $this->middleware->setData(view('view_resource', } }
  203. 203 class UserController { private $inputPort; public function __construct(UserCreateInputPortInterface $inputPort)

    { $this->inputPort = $inputPort; } public function create(string $name, string $roleId) { $role = $this->convertRole($roleId); $inputData = new UserCreateInputData($name, $role); $this->inputPort->handle($inputData); } private function convertRole(string $roleId): string { switch ($roleId) { case 'admin': return UserRole::ADMIN; case 'member': return UserRole::MEMBER; default: throw new ¥Exception(); } } } class UserCreateInputData { private $name; private $roleId; public function __construct(string $name, string $roleId) { $this->name = $name; $this->roleId = $roleId; } public function getName(): string { return $this->name; } public function getRoleId(): string { return $this->roleId; } } interface UserCreateInputPortInterface { function handle(UserCreateInputData $inputData); } class UserCreateInteractor implements UserCreateInputPortInterfac { private $repository; private $outputPort; public function __construct( UserRepositoryInterface $repository, UserCreateOutputPortInterface $outputPort) { $this->repository = $repository; $this->outputPort = $outputPort; } public function handle(UserCreateInputData $inputData) { $id = uniqid(); $user = new User ( new UserId($id), new UserName($inputData->getName()), $this->getRole($inputData->getRole()) ); interface UserCreateOutputPortInterface { function output(UserCreateOutputData $outputData); } class UserCreateOutputData { private $createdId; public function __construct(string $createdId) { $this->createdId = $createdId; } public function getCreatedId(): string { return $this->createdId; } } class UserCreatePresenter implements UserCreateOutputPor { private $middleware; public function __construct(ClarcMiddleware $middlew { $this->middleware = $middleware; } public function output(UserCreateOutputData $outputD { $viewModel = new UserCreateViewModel($outputData $this->middleware->setData(view('view_resource', } } class UserCreateViewModel { private $createdId; public function __construct(UserCreateOutputData { $this->createdId = $source->getCreatedId(); } public function getCreatedId(): string { return $this->createdId; } }
  204. 204 204

  205. 205 205

  206. 206 206 クリーンアーキテクチャを知る フレームワークを作る エピローグ

  207. 前提の確認 207

  208. 208 フレームワークとは 208

  209. 209 ソフトウェアフレームワーク (英: software framework)とは、 プログラミングにおいて、 アプリケーションプログラム等に 必要な一般的な機能が、 あらかじめ別に実装されたものである。 209

  210. 210 ソフトウェアフレームワーク (英: software framework)とは、 プログラミングにおいて、 アプリケーションプログラム等に 必要な一般的な機能が、 あらかじめ別に実装されたものである。 210

  211. 211 必要な物…… 211

  212. 212 212

  213. 213 213 easy

  214. 214 214 スキャフォールディング機能 easy

  215. 君のフレームワークは どこから?

  216. 216

  217. 217 217 スキャフォールディング機能を 作るためのライブラリ easy

  218. ログラムを作るための プ プ プ ログラムを作るための ログラム

  219. ログラムを作るための プ プ プ ログラムを作るための ログラム

  220. 220 220

  221. ログラムを作るための プ プ プ ログラムを作るための ログラム

  222. 222 222

  223. 223 223 スキャフォールディング機能 easy

  224. ログラムを作るための プ プ プ ログラムを作るための ログラム

  225. 225 225

  226. 226 226 プログラムソースを 生成するプログラム

  227. 227 227 /** * Class MyTestClass * @package nrslib */

    class MyTestClass { /** @var string */ private $testField; /** @var string */ public $testField2; /** * MyTestClass constructor. * @param string $testField */ public function __construct(string $testField) { $this->testField = $testField; } /** * @param string $test * @param string $test2 */ public function test(string $test, string $test { } /** * */ private function test2() { $testField = 1; } } Programming
  228. 228 228

  229. 229 229

  230. 230 230 クラスを定義するには class と書きます。

  231. 231 231

  232. 232 232

  233. 233 233 https://github.com/nrslib/cfg

  234. 234 234 $meta = new ClassMeta("MyTestClass", "nrslib"); $meta->setupClass() ->setConstructor(function ($define)

    { $define->addArgument('testField', 'string') ->addBody('$this->testField = $testField;'); }); $meta->setupFields() ->addField('testField', 'string') ->addField('testField2', 'string', AccessLevel::public()); $meta->setupMethods() ->addMethod('test', function($define) { $define->setAccessLevel(AccessLevel::public()) ->addArgument('test', 'string') ->addArgument('test2', 'string'); }) ->addMethod('test2', function($define) { $define->addBody('$testField = 1;'); }); $compiler = new ClassCompiler(); $source = $compiler->compile($meta);
  235. 235 235 /** * Class MyTestClass * @package nrslib */

    class MyTestClass { /** @var string */ private $testField; /** @var string */ public $testField2; /** * MyTestClass constructor. * @param string $testField */ public function __construct(string $testField) { $this->testField = $testField; } /** * @param string $test * @param string $test2 */ public function test(string $test, string $test2) { } /** * */ private function test2() { $testField = 1; } }
  236. 236 236 $meta = new ClassMeta("MyTestClass", "nrslib"); $meta->setupClass() ->setConstructor(function ($define)

    { $define->addArgument('testField', 'string') ->addBody('$this->testField = $testField;'); }); $meta->setupFields() ->addField('testField', 'string') ->addField('testField2', 'string', AccessLevel::public()); $meta->setupMethods() ->addMethod('test', function($define) { $define->setAccessLevel(AccessLevel::public()) ->addArgument('test', 'string') ->addArgument('test2', 'string'); }) ->addMethod('test2', function($define) { $define->addBody('$testField = 1;'); }); $compiler = new ClassCompiler(); $source = $compiler->compile($meta);
  237. 237 237 $meta = new ClassMeta("MyTestClass", "nrslib"); $meta->setupClass() ->setConstructor(function ($define)

    { $define->addArgument('testField', 'string') ->addBody('$this->testField = $testField;'); }); $meta->setupFields() ->addField('testField', 'string') ->addField('testField2', 'string', AccessLevel::public()); $meta->setupMethods() ->addMethod('test', function($define) { $define->setAccessLevel(AccessLevel::public()) ->addArgument('test', 'string') ->addArgument('test2', 'string'); }) ->addMethod('test2', function($define) { $define->addBody('$testField = 1;'); }); $compiler = new ClassCompiler(); $source = $compiler->compile($meta);
  238. 238 238 $meta = new ClassMeta("MyTestClass", "nrslib"); $meta->setupClass() ->setConstructor(function ($define)

    { $define->addArgument('testField', 'string') ->addBody('$this->testField = $testField;'); }); $meta->setupFields() ->addField('testField', 'string') ->addField('testField2', 'string', AccessLevel::public()); $meta->setupMethods() ->addMethod('test', function($define) { $define->setAccessLevel(AccessLevel::public()) ->addArgument('test', 'string') ->addArgument('test2', 'string'); }) ->addMethod('test2', function($define) { $define->addBody('$testField = 1;'); }); $compiler = new ClassCompiler(); $source = $compiler->compile($meta);
  239. 239 239 $meta = new ClassMeta("MyTestClass", "nrslib"); $meta->setupClass() ->setConstructor(function ($define)

    { $define->addArgument('testField', 'string') ->addBody('$this->testField = $testField;'); }); $meta->setupFields() ->addField('testField', 'string') ->addField('testField2', 'string', AccessLevel::public()); $meta->setupMethods() ->addMethod('test', function($define) { $define->setAccessLevel(AccessLevel::public()) ->addArgument('test', 'string') ->addArgument('test2', 'string'); }) ->addMethod('test2', function($define) { $define->addBody('$testField = 1;'); }); $compiler = new ClassCompiler(); $source = $compiler->compile($meta);
  240. 240 240 <?php namespace nrslib¥Cfg¥Templates¥Classes; ... require_once "Helper.php"; ?> <?=

    "<?php" ?> namespace <?= $class->getNamespace(); ?>; <?php Helper::usingBlock($class); ?> /** * Class <?= $class->getName(); ?> * @package <?= $class->getNamespace(); ?> */ class <?= $class->getName(); ?><?php extendsBlock($class); ?><?php implem { <?php fieldBlock($fieldsSetting); ?> <?php constructorBlock($class, $class->getConstructor()); ?> <?php methodBlock($methodsSetting); ?> }
  241. 241 241 <?php namespace nrslib¥Cfg¥Templates¥Classes; ... require_once "Helper.php"; ?> <?=

    "<?php" ?> namespace <?= $class->getNamespace(); ?>; <?php Helper::usingBlock($class); ?> /** * Class <?= $class->getName(); ?> * @package <?= $class->getNamespace(); ?> */ class <?= $class->getName(); ?><?php extendsBlock($class); ?><?php implem { <?php fieldBlock($fieldsSetting); ?> <?php constructorBlock($class, $class->getConstructor()); ?> <?php methodBlock($methodsSetting); ?> } twig / smarty / blade 使えよ
  242. 242 242 <?php namespace nrslib¥Cfg¥Templates¥Classes; ... require_once "Helper.php"; ?> <?=

    "<?php" ?> namespace <?= $class->getNamespace(); ?>; <?php Helper::usingBlock($class); ?> /** * Class <?= $class->getName(); ?> * @package <?= $class->getNamespace(); ?> */ class <?= $class->getName(); ?><?php extendsBlock($class); ?><?php implem { <?php fieldBlock($fieldsSetting); ?> <?php constructorBlock($class, $class->getConstructor()); ?> <?php methodBlock($methodsSetting); ?> } twig / smarty / blade 使えよ それな
  243. 243 243 <?php namespace nrslib¥Cfg¥Templates¥Classes; ... require_once "Helper.php"; ?> <?=

    "<?php" ?> namespace <?= $class->getNamespace(); ?>; <?php Helper::usingBlock($class); ?> /** * Class <?= $class->getName(); ?> * @package <?= $class->getNamespace(); ?> */ class <?= $class->getName(); ?><?php extendsBlock($class); ?><?php implem { <?php fieldBlock($fieldsSetting); ?> <?php constructorBlock($class, $class->getConstructor()); ?> <?php methodBlock($methodsSetting); ?> }
  244. 244 244 <?php namespace nrslib¥Cfg¥Templates¥Classes; ... require_once "Helper.php"; ?> <?=

    "<?php" ?> namespace <?= $class->getNamespace(); ?>; <?php Helper::usingBlock($class); ?> /** * Class <?= $class->getName(); ?> * @package <?= $class->getNamespace(); ?> */ class <?= $class->getName(); ?><?php extendsBlock($class); ?><?php implem { <?php fieldBlock($fieldsSetting); ?> <?php constructorBlock($class, $class->getConstructor()); ?> <?php methodBlock($methodsSetting); ?> }
  245. 245 245 <?php namespace nrslib¥Cfg¥Templates¥Classes; ... require_once "Helper.php"; ?> <?=

    "<?php" ?> namespace <?= $class->getNamespace(); ?>; <?php Helper::usingBlock($class); ?> /** * Class <?= $class->getName(); ?> * @package <?= $class->getNamespace(); ?> */ class <?= $class->getName(); ?><?php extendsBlock($class); ?><?php implem { <?php fieldBlock($fieldsSetting); ?> <?php constructorBlock($class, $class->getConstructor()); ?> <?php methodBlock($methodsSetting); ?> }
  246. 246 246 <?php namespace nrslib¥Cfg¥Templates¥Classes; ... require_once "Helper.php"; ?> <?=

    "<?php" ?> namespace <?= $class->getNamespace(); ?>; <?php Helper::usingBlock($class); ?> /** * Class <?= $class->getName(); ?> * @package <?= $class->getNamespace(); ?> */ class <?= $class->getName(); ?><?php extendsBlock($class); ?><?php implem { <?php fieldBlock($fieldsSetting); ?> <?php constructorBlock($class, $class->getConstructor()); ?> <?php methodBlock($methodsSetting); ?> }
  247. 247 247

  248. 248 248 PHP って テンプレートエンジン だったんだね

  249. 249 249 っていうの 作ったんです

  250. 250 250 っていうの 作ったんです そういうの あるで

  251. 251 251 https://github.com/nikic/PHP-Parser

  252. None
  253. スキャフォールディング 機能を作る

  254. 254 254 セルフホスティング

  255. 255 フレームワーク非依存

  256. 256 256

  257. 257 257 スキャフォールディングも クリーンアーキテクチャの 構成で作っちゃえよ

  258. 258 258 スキャフォールディングも クリーンアーキテクチャの 構成で作っちゃえよ

  259. 259 259 スキャフォールディングも クリーンアーキテクチャの 構成で作っちゃえよ 常に理想を追うのです クリーンアーキテクチャの 理想を追いなさい

  260. 260 260 https://github.com/nrslib/clarc-php-core

  261. 261 261

  262. 262 262 $classRenderer = new ClassRenderer(); $interfaceRenderer = new InterfaceRenderer();

    $presenter = new UseCaseCreateTestPresenter(); $controllerBuilder = new DefaultControllerSourceFileBuilder($classRenderer); $presenterBuilder = new DefaultPresenterSourceFileBuilder($classRenderer); $interactor = new UseCaseCreateInteractor( $presenter, $classRenderer, $interfaceRenderer, $controllerBuilder, $presenterBuilder); $namespace = 'nrslib¥¥Test'; $inputData = new UseCaseCreateInputData( new UseCaseCreateNamespaceData( $namespace . '¥¥A', $namespace . '¥¥B', $namespace . '¥¥C', $namespace . '¥¥D', $namespace . '¥¥E', $namespace . '¥¥F'), new UseCaseSchema('Test', 'MyAction'), [ new TypeAndName('string', 'inputStringField') ], [ new TypeAndName('string', 'outputStringField') ] ); $interactor->handle($inputData); $outputData = $presenter->outputData;
  263. 263 263 $classRenderer = new ClassRenderer(); $interfaceRenderer = new InterfaceRenderer();

    $presenter = new UseCaseCreateTestPresenter(); $controllerBuilder = new DefaultControllerSourceFileBuilder($classRenderer); $presenterBuilder = new DefaultPresenterSourceFileBuilder($classRenderer); $interactor = new UseCaseCreateInteractor( $presenter, $classRenderer, $interfaceRenderer, $controllerBuilder, $presenterBuilder); $namespace = 'nrslib¥¥Test'; $inputData = new UseCaseCreateInputData( new UseCaseCreateNamespaceData( $namespace . '¥¥A', $namespace . '¥¥B', $namespace . '¥¥C', $namespace . '¥¥D', $namespace . '¥¥E', $namespace . '¥¥F'), new UseCaseSchema('Test', 'MyAction'), [ new TypeAndName('string', 'inputStringField') ], [ new TypeAndName('string', 'outputStringField') ] ); $interactor->handle($inputData); $outputData = $presenter->outputData;
  264. 264 264 $classRenderer = new ClassRenderer(); $interfaceRenderer = new InterfaceRenderer();

    $presenter = new UseCaseCreateTestPresenter(); $controllerBuilder = new DefaultControllerSourceFileBuilder($classRenderer); $presenterBuilder = new DefaultPresenterSourceFileBuilder($classRenderer); $interactor = new UseCaseCreateInteractor( $presenter, $classRenderer, $interfaceRenderer, $controllerBuilder, $presenterBuilder); $namespace = 'nrslib¥¥Test'; $inputData = new UseCaseCreateInputData( new UseCaseCreateNamespaceData( $namespace . '¥¥A', $namespace . '¥¥B', $namespace . '¥¥C', $namespace . '¥¥D', $namespace . '¥¥E', $namespace . '¥¥F'), new UseCaseSchema('Test', 'MyAction'), [ new TypeAndName('string', 'inputStringField') ], [ new TypeAndName('string', 'outputStringField') ] ); $interactor->handle($inputData); $outputData = $presenter->outputData;
  265. 265 class UserController { private $inputPort; public function __construct(UserCreateInputPortInterface $inputPort)

    { $this->inputPort = $inputPort; } public function create(string $name, string $roleId) { $role = $this->convertRole($roleId); $inputData = new UserCreateInputData($name, $role); $this->inputPort->handle($inputData); } private function convertRole(string $roleId): string { switch ($roleId) { case 'admin': return UserRole::ADMIN; case 'member': return UserRole::MEMBER; default: throw new ¥Exception(); } } } class UserCreateInputData { private $name; private $roleId; public function __construct(string $name, string $roleId) { $this->name = $name; $this->roleId = $roleId; } public function getName(): string { return $this->name; } public function getRoleId(): string { return $this->roleId; } } interface UserCreateInputPortInterface { function handle(UserCreateInputData $inputData); } class UserCreateInteractor implements UserCreateInputPortInterfac { private $repository; private $outputPort; public function __construct( UserRepositoryInterface $repository, UserCreateOutputPortInterface $outputPort) { $this->repository = $repository; $this->outputPort = $outputPort; } public function handle(UserCreateInputData $inputData) { $id = uniqid(); $user = new User ( new UserId($id), new UserName($inputData->getName()), $this->getRole($inputData->getRole()) ); interface UserCreateOutputPortInterface { function output(UserCreateOutputData $outputData); } class UserCreateOutputData { private $createdId; public function __construct(string $createdId) { $this->createdId = $createdId; } public function getCreatedId(): string { return $this->createdId; } } class UserCreatePresenter implements UserCreateOutputPor { private $middleware; public function __construct(ClarcMiddleware $middlew { $this->middleware = $middleware; } public function output(UserCreateOutputData $outputD { $viewModel = new UserCreateViewModel($outputData $this->middleware->setData(view('view_resource', } } class UserCreateViewModel { private $createdId; public function __construct(UserCreateOutputData { $this->createdId = $source->getCreatedId(); } public function getCreatedId(): string { return $this->createdId; } }
  266. WEB で動かそう

  267. 267 Browser Server interface UserCreateOutputPortInterface { function output(UserCreateOutputData $outputData); }

    Request Response
  268. 268 268 思想が先! http は後!

  269. 269 269

  270. 270 270 まず最初にすることは MVC を捨てること

  271. 271 class UserController extends BaseController { public function create( UserCreateInputPortInterface

    $inputPort, Request $request) { $name = $request->input('name'); $roleId = $request->input('roleId'); $inputData = new UserCreateInputData($name, $roleId); $inputPort->handle($inputData); ... 戻り値を無くす } }
  272. 272 class UserController extends BaseController { public function create( UserCreateInputPortInterface

    $inputPort, Request $request) { $name = $request->input('name'); $roleId = $request->input('roleId'); $inputData = new UserCreateInputData($name, $roleId); $inputPort->handle($inputData); ... 戻り値を無くす } }
  273. 273 class UserController extends BaseController { public function create( UserCreateInputPortInterface

    $inputPort, Request $request) { $name = $request->input('name'); $roleId = $request->input('roleId'); $inputData = new UserCreateInputData($name, $roleId); $inputPort->handle($inputData); ... 戻り値を無くす } }
  274. 274 class UserController extends BaseController { public function create( UserCreateInputPortInterface

    $inputPort, Request $request) { $name = $request->input('name'); $roleId = $request->input('roleId'); $inputData = new UserCreateInputData($name, $roleId); $inputPort->handle($inputData); ... 戻り値を無くす } }
  275. 275 class UserCreatePresenter implements UserCreateOutputPortInterface { public function output(UserCreateOutputData $outputData)

    { ... どうにかしてビューを描画する } }
  276. 276 class UserCreatePresenter implements UserCreateOutputPortInterface { public function output(UserCreateOutputData $outputData)

    { ... どうにかしてビューを描画する } } こっちの方がむしろ Classic MVC に近い
  277. 277 戻り値がなくなる

  278. 278 278 いい機能ありますよ!

  279. 279 Browser Server Request Response

  280. 280 Browser Server Request Response Middle ware

  281. 281 class ClarcMiddleWare { /** @var mixed */ private $data;

    /** @var Router */ private $router; public function __construct(Router $router) { $this->router = $router; } public function setData($data): void { $this->data = $data; } public function handle($request, Closure $next) { $response = $next($request); if ($this->data === null) { return $response; } return $this->router->prepareResponse( $this->router->getCurrentRequest(), $this->data); } }
  282. 282 class ClarcMiddleWare { /** @var mixed */ private $data;

    /** @var Router */ private $router; public function __construct(Router $router) { $this->router = $router; } public function setData($data): void {$this->data = $data; } public function handle($request, Closure $next) { $response = $next($request); if ($this->data === null) { return $response; } return $this->router->prepareResponse( $this->router->getCurrentRequest(), $this->data); } } class UserCreatePresenter implements UserCreateOutputPortInterface { private $middleware; public function __construct(ClarcMiddleware $middleware) { $this->middleware = $middleware; } public function output(UserCreateOutputData $outputData) { $viewModel = new UserCreateViewModel($outputData); $this->middleware->setData( view('user.create', compact('viewModel'))); } }
  283. 283 class ClarcMiddleWare { /** @var mixed */ private $data;

    /** @var Router */ private $router; public function __construct(Router $router) { $this->router = $router; } public function setData($data): void {$this->data = $data; } public function handle($request, Closure $next) { $response = $next($request); if ($this->data === null) { return $response; } return $this->router->prepareResponse( $this->router->getCurrentRequest(), $this->data); } } class UserCreatePresenter implements UserCreateOutputPortInterface { private $middleware; public function __construct(ClarcMiddleware $middleware) { $this->middleware = $middleware; } public function output(UserCreateOutputData $outputData) { $viewModel = new UserCreateViewModel($outputData); $this->middleware->setData( view('user.create', compact('viewModel'))); } }
  284. 284 class ClarcMiddleWare { /** @var mixed */ private $data;

    /** @var Router */ private $router; public function __construct(Router $router) { $this->router = $router; } public function setData($data): void {$this->data = $data; } public function handle($request, Closure $next) { $response = $next($request); if ($this->data === null) { return $response; } return $this->router->prepareResponse( $this->router->getCurrentRequest(), $this->data); } } class UserCreatePresenter implements UserCreateOutputPortInterface { private $middleware; public function __construct(ClarcMiddleware $middleware) { $this->middleware = $middleware; } public function output(UserCreateOutputData $outputData) { $viewModel = new UserCreateViewModel($outputData); $this->middleware->setData( view('user.create', compact('viewModel'))); } }
  285. 285 class ClarcMiddleWare { /** @var mixed */ private $data;

    /** @var Router */ private $router; public function __construct(Router $router) { $this->router = $router; } public function setData($data): void {$this->data = $data; } public function handle($request, Closure $next) { $response = $next($request); if ($this->data === null) { return $response; } return $this->router->prepareResponse( $this->router->getCurrentRequest(), $this->data); } } class UserCreatePresenter implements UserCreateOutputPortInterface { private $middleware; public function __construct(ClarcMiddleware $middleware) { $this->middleware = $middleware; } public function output(UserCreateOutputData $outputData) { $viewModel = new UserCreateViewModel($outputData); $this->middleware->setData( view('user.create', compact('viewModel'))); } }
  286. 286 class ClarcMiddleWare { /** @var mixed */ private $data;

    /** @var Router */ private $router; public function __construct(Router $router) { $this->router = $router; } public function setData($data): void { $this->data = $data; } public function handle($request, Closure $next) { $response = $next($request); if ($this->data === null) { return $response; } return $this->router->prepareResponse( $this->router->getCurrentRequest(), $this->data); } }
  287. 287 class ClarcMiddleWare { /** @var mixed */ private $data;

    /** @var Router */ private $router; public function __construct(Router $router) { $this->router = $router; } public function setData($data): void { $this->data = $data; } public function handle($request, Closure $next) { $response = $next($request); if ($this->data === null) { return $response; } return $this->router->prepareResponse( $this->router->getCurrentRequest(), $this->data); } }
  288. 288 class ClarcMiddleWare { /** @var mixed */ private $data;

    /** @var Router */ private $router; public function __construct(Router $router) { $this->router = $router; } public function setData($data): void { $this->data = $data; } public function handle($request, Closure $next) { $response = $next($request); if ($this->data === null) { return $response; } return $this->router->prepareResponse( $this->router->getCurrentRequest(), $this->data); } }
  289. 289 class ClarcMiddleWare { /** @var mixed */ private $data;

    /** @var Router */ private $router; public function __construct(Router $router) { $this->router = $router; } public function setData($data): void { $this->data = $data; } public function handle($request, Closure $next) { $response = $next($request); if ($this->data === null) { return $response; } return $this->router->prepareResponse( $this->router->getCurrentRequest(), $this->data); } }
  290. 290 290 やればできるやないか

  291. 291 291 よっしゃ、君に乗せたるわ

  292. 292 https://github.com/nrslib/clarc-laravel-plugin

  293. 293 clarc:make clarc:init Clarc Commands

  294. 294 clarc:make clarc:init Clarc Commands

  295. 295 Browser Server Request Response Middle ware

  296. 296 Browser Server Request Response Middle ware

  297. 297 class ClarcInitializeCommand extends ClarcCommand { protected $signature = 'clarc:init';

    public function handle() { $this->title('Initialize clarc'); $middlewareFilePath = LaravelConfig::DIR_MIDDLEWARE . 'ClarcMiddl $this->info('Creating file ' . $middlewareFilePath); file_put_contents($middlewareFilePath, ClarcModuleCodes::$middleW $this->info('Wrote ' . realpath($middlewareFilePath)); $this->newline(); $clarcProviderFilePath = LaravelConfig::DIR_PROVIDER . 'ClarcProv $this->info('Creating file' . $clarcProviderFilePath); file_put_contents($clarcProviderFilePath, ClarcModuleCodes::$clar $this->info('Wrote ' . realpath($clarcProviderFilePath)); $this->newline(); $this->info('Please append ClarcProvider to app.php'); } }
  298. 298 class ClarcInitializeCommand extends ClarcCommand { protected $signature = 'clarc:init';

    public function handle() { $this->title('Initialize clarc'); $middlewareFilePath = LaravelConfig::DIR_MIDDLEWARE . 'ClarcMiddl $this->info('Creating file ' . $middlewareFilePath); file_put_contents($middlewareFilePath, ClarcModuleCodes::$middleW $this->info('Wrote ' . realpath($middlewareFilePath)); $this->newline(); $clarcProviderFilePath = LaravelConfig::DIR_PROVIDER . 'ClarcProv $this->info('Creating file' . $clarcProviderFilePath); file_put_contents($clarcProviderFilePath, ClarcModuleCodes::$clar $this->info('Wrote ' . realpath($clarcProviderFilePath)); $this->newline(); $this->info('Please append ClarcProvider to app.php'); } }
  299. 299 clarc:make clarc:init Clarc Commands

  300. 300 300 easy

  301. 301

  302. 302

  303. 303

  304. 304 class ClarcMakeCommand extends ClarcCommand { public function handle() {

    $this->title('Make usecase'); $this->info('Please input your controller name.'); $controllerName = $this->need('controller name'); $controllerName = ucfirst($controllerName); $this->info('Please input your action name.'); $actionName = $this->need('action name.'); $actionName = ucfirst($actionName); $schema = new UseCaseSchema($controllerName, $actionName); $objectCreatePresenter = new ClarcMakeCommandObjectCreatePresenter($this, $schema); $alreadyRegistered = !$this->checkFiles($controllerName, $actionName, $objectCreateP if ($alreadyRegistered) { $this->info('process ended.'); return; } $this->info('Please select input data fields'); $inputDataFields = $this->askFields(); $this->info('Please select output data fields'); $outputDataFields = $this->askFields(); $this->createClarcObjects($objectCreatePresenter, $controllerName, $actionName, $inp $this->registerDependency($controllerName, $actionName); $this->info('process ended.');
  305. 305 private function createClarcObjects( ClarcMakeCommandObjectCreatePresenter $objectCreatePresenter, string $controllerName, string $actionName,

    array $inputDataFields, array $outputDataFields) { $objectCreateInteractor = new ClarcObjectCreateInteractor( $objectCreatePresenter, new ClassRenderer(), new InterfaceRenderer() ); $currentControllerContent = $this->getCurrentControllerContent( $controllerName); $objectCreateInputData = new ClarcObjectCreateInputData( $controllerName, $currentControllerContent, $actionName, $inputDataFields, $outputDataFields ); $objectCreateInteractor->handle($objectCreateInputData); }
  306. 306 private function createClarcObjects( ClarcMakeCommandObjectCreatePresenter $objectCreatePresenter, string $controllerName, string $actionName,

    array $inputDataFields, array $outputDataFields) { $objectCreateInteractor = new ClarcObjectCreateInteractor( $objectCreatePresenter, new ClassRenderer(), new InterfaceRenderer() ); $currentControllerContent = $this->getCurrentControllerContent( $controllerName); $objectCreateInputData = new ClarcObjectCreateInputData( $controllerName, $currentControllerContent, $actionName, $inputDataFields, $outputDataFields ); $objectCreateInteractor->handle($objectCreateInputData); }
  307. 307 private function createClarcObjects( ClarcMakeCommandObjectCreatePresenter $objectCreatePresenter, string $controllerName, string $actionName,

    array $inputDataFields, array $outputDataFields) { $objectCreateInteractor = new ClarcObjectCreateInteractor( $objectCreatePresenter, new ClassRenderer(), new InterfaceRenderer() ); $currentControllerContent = $this->getCurrentControllerContent( $controllerName); $objectCreateInputData = new ClarcObjectCreateInputData( $controllerName, $currentControllerContent, $actionName, $inputDataFields, $outputDataFields ); $objectCreateInteractor->handle($objectCreateInputData); }
  308. 308 private function createClarcObjects( ClarcMakeCommandObjectCreatePresenter $objectCreatePresenter, string $controllerName, string $actionName,

    array $inputDataFields, array $outputDataFields) { $objectCreateInteractor = new ClarcObjectCreateInteractor( $objectCreatePresenter, new ClassRenderer(), new InterfaceRenderer() ); $currentControllerContent = $this->getCurrentControllerContent( $controllerName); $objectCreateInputData = new ClarcObjectCreateInputData( $controllerName, $currentControllerContent, $actionName, $inputDataFields, $outputDataFields ); $objectCreateInteractor->handle($objectCreateInputData); }
  309. 309 309 easy

  310. 310 class ClarcProvider extends ServiceProvider { public function register() {

    $this->app->singleton(ClarcMiddleWare::class); $this->registerUseCases(); } private function registerUseCases() { // TestMyAction $this->app->bind( ¥packages¥InputPorts¥Test¥MyAction¥TestMyActionInputPortInterface::class, ¥packages¥Interactors¥Test¥TestMyActionInteractor::class); $this->app->bind( ¥packages¥OutputPorts¥Test¥MyAction¥TestMyActionOutputPortInterface::class, ¥App¥Http¥Presenters¥Test¥TestMyActionPresenter::class); // UserCreate $this->app->bind( ¥packages¥InputPorts¥User¥Create¥UserCreateInputPortInterface::class, ¥packages¥Interactors¥User¥UserCreateInteractor::class); $this->app->bind( ¥packages¥OutputPorts¥User¥Create¥UserCreateOutputPortInterface::class, ¥App¥Http¥Presenters¥User¥UserCreatePresenter::class); } }
  311. Demo

  312. 312 クリーンアーキテクチャを知る フレームワークを作る エピローグ

  313. 313 クリーンアーキテクチャを知る フレームワークを作る エピローグ

  314. 314 クリーンアーキテクチャを知る フレームワークを作る エピローグ

  315. 315 クリーンアーキテクチャを知る フレームワークを作る エピローグ

  316. クリーンアーキテクチャを知る フレームワークを作る エピローグ ??? 316

  317. 317 はたして

  318. 318 これはフレームワークと 言えるのだろうか

  319. 319

  320. 320

  321. 321

  322. 322 https://github.com/nrslib/clarc-php-concept

  323. 機能

  324. 324 Scaffolding IoC Container Routing

  325. 325 Scaffolding IoC Container Routing

  326. 326 326 easy

  327. 327 Scaffolding IoC Container Routing

  328. 328 class Startup { public function configureService( ServiceCollection $serviceCollection) {

    $this->registerUsecases($serviceCollection); } private function registerUseCases( ServiceCollection $serviceCollection) { // UserCreate $serviceCollection->addTransient(UserCreateController::class); $serviceCollection->addTransient( UserCreateInputPortInterface::class, UserCreateInteractor::class); $serviceCollection->addTransient( UserCreateOutputPortInterface::class, UserCreatePresenter::class); } }
  329. 329 Scaffolding IoC Container Routing

  330. 330 private function getControllerName($server) { $root = $server['DOCUMENT_ROOT']; $url =

    parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); $tokens = explode('/', $url); $count = count($tokens); if (!isset($tokens[1])) { throw new ¥Exception($tokens); } $top = ucfirst($tokens[1]); if (!isset($tokens[2])) { throw new ¥Exception($tokens[1]); } $second = ucfirst($tokens[2]); $path = $root . '/app/InterfaceAdapters/Controllers/' . $top . '/' . if ($count === 3 && file_exists($path)) { return 'App¥¥InterfaceAdapters¥¥Controllers¥¥' . $top . '¥¥' . $s } return null; }
  331. 331 private function getControllerName($server) { $root = $server['DOCUMENT_ROOT']; $url =

    parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); $tokens = explode('/', $url); $count = count($tokens); if (!isset($tokens[1])) { throw new ¥Exception($tokens); } $top = ucfirst($tokens[1]); if (!isset($tokens[2])) { throw new ¥Exception($tokens[1]); } $second = ucfirst($tokens[2]); $path = $root . '/app/InterfaceAdapters/Controllers/' . $top . '/' . if ($count === 3 && file_exists($path)) { return 'App¥¥InterfaceAdapters¥¥Controllers¥¥' . $top . '¥¥' . $s } return null; }
  332. 332 private function getControllerName($server) { $root = $server['DOCUMENT_ROOT']; $url =

    parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); $tokens = explode('/', $url); $count = count($tokens); if (!isset($tokens[1])) { throw new ¥Exception($tokens); } $top = ucfirst($tokens[1]); if (!isset($tokens[2])) { throw new ¥Exception($tokens[1]); } $second = ucfirst($tokens[2]); $path = $root . '/app/InterfaceAdapters/Controllers/' . $top . '/' . if ($count === 3 && file_exists($path)) { return 'App¥¥InterfaceAdapters¥¥Controllers¥¥' . $top . '¥¥' . $s } return null; }
  333. 333 private function getControllerName($server) { $root = $server['DOCUMENT_ROOT']; $url =

    parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); $tokens = explode('/', $url); $count = count($tokens); if (!isset($tokens[1])) { throw new ¥Exception($tokens); } $top = ucfirst($tokens[1]); if (!isset($tokens[2])) { throw new ¥Exception($tokens[1]); } $second = ucfirst($tokens[2]); $path = $root . '/app/InterfaceAdapters/Controllers/' . $top . '/' . if ($count === 3 && file_exists($path)) { return 'App¥¥InterfaceAdapters¥¥Controllers¥¥' . $top . '¥¥' . $s } S return null; } http://sample.com/{controller}/{action} 以外認めない
  334. Demo

  335. クリーンアーキテクチャを知る フレームワークを作る エピローグ おまけ 335

  336. 336 336

  337. 337 337

  338. 338 Auther Masanobu Naruse HomePage https://nrslib.com Twitter @nrslib 338