Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

2

Slide 3

Slide 3 text

3

Slide 4

Slide 4 text

4

Slide 5

Slide 5 text

5

Slide 6

Slide 6 text

6

Slide 7

Slide 7 text

7

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

10

Slide 11

Slide 11 text

なぜ推すか

Slide 12

Slide 12 text

12

Slide 13

Slide 13 text

13 これは どこに書こう

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

16

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

20

Slide 21

Slide 21 text

21 この図の前に

Slide 22

Slide 22 text

22 まずはこの図 22

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

24 24

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

29 たとえるなら 29

Slide 30

Slide 30 text

30 30

Slide 31

Slide 31 text

31 31

Slide 32

Slide 32 text

32 32

Slide 33

Slide 33 text

33 33

Slide 34

Slide 34 text

34 34

Slide 35

Slide 35 text

35 35

Slide 36

Slide 36 text

36 36

Slide 37

Slide 37 text

37 37

Slide 38

Slide 38 text

38 38

Slide 39

Slide 39 text

39 39

Slide 40

Slide 40 text

40 40

Slide 41

Slide 41 text

41 In Software 41

Slide 42

Slide 42 text

42 42

Slide 43

Slide 43 text

43 43

Slide 44

Slide 44 text

44 44

Slide 45

Slide 45 text

45 45

Slide 46

Slide 46 text

46 46

Slide 47

Slide 47 text

47 47

Slide 48

Slide 48 text

48 48

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

51 51

Slide 52

Slide 52 text

52 52

Slide 53

Slide 53 text

53 53

Slide 54

Slide 54 text

54 根底にあるもの 54

Slide 55

Slide 55 text

55 SOLID 原則 55

Slide 56

Slide 56 text

56 SOLID 原則 56

Slide 57

Slide 57 text

57 Dependency Inversion Principle 57

Slide 58

Slide 58 text

58 58 Business Logic Database Access Module

Slide 59

Slide 59 text

59 59 Business Logic Database Access Module Change

Slide 60

Slide 60 text

60 60 Business Logic Database Access Module Change Involve

Slide 61

Slide 61 text

61 61 Business Logic Database Access Module Data Access Interface

Slide 62

Slide 62 text

62 62 Business Logic Database Access Module Data Access Interface

Slide 63

Slide 63 text

63 63 Business Logic Database Access Module Data Access Interface

Slide 64

Slide 64 text

64 64 Business Logic Database Access Module Data Access Interface

Slide 65

Slide 65 text

65 65 Business Logic Database Access Module Data Access Interface

Slide 66

Slide 66 text

66 66 Business Logic Database Access Module Data Access Interface Change

Slide 67

Slide 67 text

67 67 Business Logic Database Access Module Data Access Interface Change Involve

Slide 68

Slide 68 text

68 68 Business Logic Database Access Module Data Access Interface Change Involve Involve

Slide 69

Slide 69 text

69 69 Business Logic Database Access Module Data Access Interface Change Involve Involve leading

Slide 70

Slide 70 text

70 70

Slide 71

Slide 71 text

71 71

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

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

Slide 74

Slide 74 text

74 74

Slide 75

Slide 75 text

75 75

Slide 76

Slide 76 text

76 76

Slide 77

Slide 77 text

図を詳しく見る 77

Slide 78

Slide 78 text

78

Slide 79

Slide 79 text

79

Slide 80

Slide 80 text

80 Enterprise Business Rules Entities

Slide 81

Slide 81 text

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

Slide 82

Slide 82 text

82

Slide 83

Slide 83 text

83 Application Business Rules Use Cases

Slide 84

Slide 84 text

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

Slide 85

Slide 85 text

85

Slide 86

Slide 86 text

86 Interface Adapters

Slide 87

Slide 87 text

87 Controllers Presenters Gateways Interface Adapters

Slide 88

Slide 88 text

88 Controllers Presenters Gateways Interface Adapters

Slide 89

Slide 89 text

89 Controllers Presenters Gateways Interface Adapters

Slide 90

Slide 90 text

90 Controllers Presenters Gateways Interface Adapters Mock

Slide 91

Slide 91 text

91

Slide 92

Slide 92 text

92 Controller Presenter

Slide 93

Slide 93 text

93 Controller Presenter

Slide 94

Slide 94 text

94 Controller Presenter

Slide 95

Slide 95 text

95 Controller Presenter

Slide 96

Slide 96 text

96 Controller Presenter Use Case Interactor

Slide 97

Slide 97 text

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

Slide 98

Slide 98 text

98 Controller Presenter Use Case Interactor Use Case Input Port Use Case Output Port < I > < I >

Slide 99

Slide 99 text

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

Slide 100

Slide 100 text

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

Slide 101

Slide 101 text

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

Slide 102

Slide 102 text

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

Slide 103

Slide 103 text

103 Frameworks & Drivers

Slide 104

Slide 104 text

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

Slide 105

Slide 105 text

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

Slide 106

Slide 106 text

106 依存の方向

Slide 107

Slide 107 text

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

Slide 108

Slide 108 text

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

Slide 109

Slide 109 text

実装例 109

Slide 110

Slide 110 text

110 110 Controller Presenter Use Case Interactor Use Case Input Port Use Case Output Port < I > < I >

Slide 111

Slide 111 text

111 Controller Presenter Use Case Interactor Use Case Input Port Use Case Output Port < I > < I > もっと細かく

Slide 112

Slide 112 text

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

Slide 113

Slide 113 text

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

Slide 114

Slide 114 text

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

Slide 115

Slide 115 text

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

Slide 116

Slide 116 text

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

Slide 117

Slide 117 text

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

Slide 118

Slide 118 text

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

Slide 119

Slide 119 text

119

Slide 120

Slide 120 text

120

Slide 121

Slide 121 text

121

Slide 122

Slide 122 text

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(); } } }

Slide 123

Slide 123 text

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(); } } } アプリケーションが 要求するデータに 入力を変換

Slide 124

Slide 124 text

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(); } } } アプリケーションが 要求するデータに 入力を変換

Slide 125

Slide 125 text

125

Slide 126

Slide 126 text

126

Slide 127

Slide 127 text

127 DS : Data Structure

Slide 128

Slide 128 text

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; } }

Slide 129

Slide 129 text

129

Slide 130

Slide 130 text

130

Slide 131

Slide 131 text

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

Slide 132

Slide 132 text

132

Slide 133

Slide 133 text

133

Slide 134

Slide 134 text

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); } ... }

Slide 135

Slide 135 text

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); } ... }

Slide 136

Slide 136 text

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); } ... }

Slide 137

Slide 137 text

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); } ... }

Slide 138

Slide 138 text

138

Slide 139

Slide 139 text

139

Slide 140

Slide 140 text

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

Slide 141

Slide 141 text

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

Slide 142

Slide 142 text

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

Slide 143

Slide 143 text

143

Slide 144

Slide 144 text

144

Slide 145

Slide 145 text

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); } ... } オブジェクトの 永続化・再構築が できればなんでもいい

Slide 146

Slide 146 text

146

Slide 147

Slide 147 text

147

Slide 148

Slide 148 text

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; } }

Slide 149

Slide 149 text

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; } }

Slide 150

Slide 150 text

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; } } データモデルとは異なる

Slide 151

Slide 151 text

151

Slide 152

Slide 152 text

152

Slide 153

Slide 153 text

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

Slide 154

Slide 154 text

154

Slide 155

Slide 155 text

155

Slide 156

Slide 156 text

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

Slide 157

Slide 157 text

157

Slide 158

Slide 158 text

158

Slide 159

Slide 159 text

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'))); } }

Slide 160

Slide 160 text

160

Slide 161

Slide 161 text

161

Slide 162

Slide 162 text

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

Slide 163

Slide 163 text

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

Test

{{$viewModel->getCreatedId()}}

Slide 164

Slide 164 text

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

Slide 165

Slide 165 text

165

Slide 166

Slide 166 text

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 {

Slide 167

Slide 167 text

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 {

Slide 168

Slide 168 text

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 {

Slide 169

Slide 169 text

169 Flow of Control

Slide 170

Slide 170 text

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

Slide 171

Slide 171 text

171 Flow of Control Delegate interface UserCreateInputPortInterface { function handle(UserCreateInputData $inputData); }

Slide 172

Slide 172 text

172 Flow of Control

Slide 173

Slide 173 text

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); }

Slide 174

Slide 174 text

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); }

Slide 175

Slide 175 text

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); }

Slide 176

Slide 176 text

176 Flow of Control interface UserRepositoryInterface { function find(UserId $id): User; function save(User $user); function delete(User $user); }

Slide 177

Slide 177 text

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

Slide 178

Slide 178 text

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); } }

Slide 179

Slide 179 text

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); } }

Slide 180

Slide 180 text

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); }

Slide 181

Slide 181 text

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); }

Slide 182

Slide 182 text

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); }

Slide 183

Slide 183 text

183 Flow of Control

Slide 184

Slide 184 text

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

Slide 185

Slide 185 text

185 Flow of Control Delegate interface UserCreateOutputPortInterface { function output(UserCreateOutputData $outputData); }

Slide 186

Slide 186 text

メリット

Slide 187

Slide 187 text

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

Slide 188

Slide 188 text

188 188 Business Logic Database Access Module Data Access Interface Change Involve Involve leading 主導権をビジネスロジックに

Slide 189

Slide 189 text

代償

Slide 190

Slide 190 text

190 Browser Server Request Response

Slide 191

Slide 191 text

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

Slide 192

Slide 192 text

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

Slide 193

Slide 193 text

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

Slide 194

Slide 194 text

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

Slide 195

Slide 195 text

195 In programming

Slide 196

Slide 196 text

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(); } } }

Slide 197

Slide 197 text

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; } }

Slide 198

Slide 198 text

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); }

Slide 199

Slide 199 text

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()) );

Slide 200

Slide 200 text

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); }

Slide 201

Slide 201 text

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; } }

Slide 202

Slide 202 text

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', } }

Slide 203

Slide 203 text

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; } }

Slide 204

Slide 204 text

204 204

Slide 205

Slide 205 text

205 205

Slide 206

Slide 206 text

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

Slide 207

Slide 207 text

前提の確認 207

Slide 208

Slide 208 text

208 フレームワークとは 208

Slide 209

Slide 209 text

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

Slide 210

Slide 210 text

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

Slide 211

Slide 211 text

211 必要な物…… 211

Slide 212

Slide 212 text

212 212

Slide 213

Slide 213 text

213 213 easy

Slide 214

Slide 214 text

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

Slide 215

Slide 215 text

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

Slide 216

Slide 216 text

216

Slide 217

Slide 217 text

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

Slide 218

Slide 218 text

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

Slide 219

Slide 219 text

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

Slide 220

Slide 220 text

220 220

Slide 221

Slide 221 text

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

Slide 222

Slide 222 text

222 222

Slide 223

Slide 223 text

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

Slide 224

Slide 224 text

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

Slide 225

Slide 225 text

225 225

Slide 226

Slide 226 text

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

Slide 227

Slide 227 text

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

Slide 228

Slide 228 text

228 228

Slide 229

Slide 229 text

229 229

Slide 230

Slide 230 text

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

Slide 231

Slide 231 text

231 231

Slide 232

Slide 232 text

232 232

Slide 233

Slide 233 text

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

Slide 234

Slide 234 text

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);

Slide 235

Slide 235 text

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; } }

Slide 236

Slide 236 text

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);

Slide 237

Slide 237 text

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);

Slide 238

Slide 238 text

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);

Slide 239

Slide 239 text

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);

Slide 240

Slide 240 text

240 240 = " namespace = $class->getNamespace(); ?>; /** * Class = $class->getName(); ?> * @package = $class->getNamespace(); ?> */ class = $class->getName(); ?> getConstructor()); ?> }

Slide 241

Slide 241 text

241 241 = " namespace = $class->getNamespace(); ?>; /** * Class = $class->getName(); ?> * @package = $class->getNamespace(); ?> */ class = $class->getName(); ?> getConstructor()); ?> } twig / smarty / blade 使えよ

Slide 242

Slide 242 text

242 242 = " namespace = $class->getNamespace(); ?>; /** * Class = $class->getName(); ?> * @package = $class->getNamespace(); ?> */ class = $class->getName(); ?> getConstructor()); ?> } twig / smarty / blade 使えよ それな

Slide 243

Slide 243 text

243 243 = " namespace = $class->getNamespace(); ?>; /** * Class = $class->getName(); ?> * @package = $class->getNamespace(); ?> */ class = $class->getName(); ?> getConstructor()); ?> }

Slide 244

Slide 244 text

244 244 = " namespace = $class->getNamespace(); ?>; /** * Class = $class->getName(); ?> * @package = $class->getNamespace(); ?> */ class = $class->getName(); ?> getConstructor()); ?> }

Slide 245

Slide 245 text

245 245 = " namespace = $class->getNamespace(); ?>; /** * Class = $class->getName(); ?> * @package = $class->getNamespace(); ?> */ class = $class->getName(); ?> getConstructor()); ?> }

Slide 246

Slide 246 text

246 246 = " namespace = $class->getNamespace(); ?>; /** * Class = $class->getName(); ?> * @package = $class->getNamespace(); ?> */ class = $class->getName(); ?> getConstructor()); ?> }

Slide 247

Slide 247 text

247 247

Slide 248

Slide 248 text

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

Slide 249

Slide 249 text

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

Slide 250

Slide 250 text

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

Slide 251

Slide 251 text

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

Slide 252

Slide 252 text

No content

Slide 253

Slide 253 text

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

Slide 254

Slide 254 text

254 254 セルフホスティング

Slide 255

Slide 255 text

255 フレームワーク非依存

Slide 256

Slide 256 text

256 256

Slide 257

Slide 257 text

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

Slide 258

Slide 258 text

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

Slide 259

Slide 259 text

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

Slide 260

Slide 260 text

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

Slide 261

Slide 261 text

261 261

Slide 262

Slide 262 text

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;

Slide 263

Slide 263 text

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;

Slide 264

Slide 264 text

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;

Slide 265

Slide 265 text

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; } }

Slide 266

Slide 266 text

WEB で動かそう

Slide 267

Slide 267 text

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

Slide 268

Slide 268 text

268 268 思想が先! http は後!

Slide 269

Slide 269 text

269 269

Slide 270

Slide 270 text

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

Slide 271

Slide 271 text

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); ... 戻り値を無くす } }

Slide 272

Slide 272 text

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); ... 戻り値を無くす } }

Slide 273

Slide 273 text

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); ... 戻り値を無くす } }

Slide 274

Slide 274 text

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); ... 戻り値を無くす } }

Slide 275

Slide 275 text

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

Slide 276

Slide 276 text

276 class UserCreatePresenter implements UserCreateOutputPortInterface { public function output(UserCreateOutputData $outputData) { ... どうにかしてビューを描画する } } こっちの方がむしろ Classic MVC に近い

Slide 277

Slide 277 text

277 戻り値がなくなる

Slide 278

Slide 278 text

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

Slide 279

Slide 279 text

279 Browser Server Request Response

Slide 280

Slide 280 text

280 Browser Server Request Response Middle ware

Slide 281

Slide 281 text

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); } }

Slide 282

Slide 282 text

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'))); } }

Slide 283

Slide 283 text

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'))); } }

Slide 284

Slide 284 text

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'))); } }

Slide 285

Slide 285 text

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'))); } }

Slide 286

Slide 286 text

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); } }

Slide 287

Slide 287 text

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); } }

Slide 288

Slide 288 text

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); } }

Slide 289

Slide 289 text

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); } }

Slide 290

Slide 290 text

290 290 やればできるやないか

Slide 291

Slide 291 text

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

Slide 292

Slide 292 text

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

Slide 293

Slide 293 text

293 clarc:make clarc:init Clarc Commands

Slide 294

Slide 294 text

294 clarc:make clarc:init Clarc Commands

Slide 295

Slide 295 text

295 Browser Server Request Response Middle ware

Slide 296

Slide 296 text

296 Browser Server Request Response Middle ware

Slide 297

Slide 297 text

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'); } }

Slide 298

Slide 298 text

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'); } }

Slide 299

Slide 299 text

299 clarc:make clarc:init Clarc Commands

Slide 300

Slide 300 text

300 300 easy

Slide 301

Slide 301 text

301

Slide 302

Slide 302 text

302

Slide 303

Slide 303 text

303

Slide 304

Slide 304 text

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.');

Slide 305

Slide 305 text

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); }

Slide 306

Slide 306 text

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); }

Slide 307

Slide 307 text

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); }

Slide 308

Slide 308 text

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); }

Slide 309

Slide 309 text

309 309 easy

Slide 310

Slide 310 text

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); } }

Slide 311

Slide 311 text

Demo

Slide 312

Slide 312 text

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

Slide 313

Slide 313 text

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

Slide 314

Slide 314 text

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

Slide 315

Slide 315 text

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

Slide 316

Slide 316 text

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

Slide 317

Slide 317 text

317 はたして

Slide 318

Slide 318 text

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

Slide 319

Slide 319 text

319

Slide 320

Slide 320 text

320

Slide 321

Slide 321 text

321

Slide 322

Slide 322 text

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

Slide 323

Slide 323 text

機能

Slide 324

Slide 324 text

324 Scaffolding IoC Container Routing

Slide 325

Slide 325 text

325 Scaffolding IoC Container Routing

Slide 326

Slide 326 text

326 326 easy

Slide 327

Slide 327 text

327 Scaffolding IoC Container Routing

Slide 328

Slide 328 text

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); } }

Slide 329

Slide 329 text

329 Scaffolding IoC Container Routing

Slide 330

Slide 330 text

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; }

Slide 331

Slide 331 text

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; }

Slide 332

Slide 332 text

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; }

Slide 333

Slide 333 text

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} 以外認めない

Slide 334

Slide 334 text

Demo

Slide 335

Slide 335 text

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

Slide 336

Slide 336 text

336 336

Slide 337

Slide 337 text

337 337

Slide 338

Slide 338 text

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