Slide 1

Slide 1 text

ActiveRecordパターンの呪縛を 学びほぐして挑むクリーンアー キテクチャへの入り口 Press Space for next page PHPカンファレンス沖縄2023 Sep 16th, 2023. v0.0.2

Slide 2

Slide 2 text

自己紹介 「障害のない社会をつくる」をビジョンに掲げている「りたりこ」という会社に所属しています 以下のアカウントで活動しています 2 / 95 katzumi(かつみ)と申します katzchum k2tzumi katzumi

Slide 3

Slide 3 text

3 / 95 日々ちょうぜつ分厚い本と向き合っています 圧巻の1.5K頁オーバー。3年に一回、大改訂(大改定)があります。レセプト業務の基盤システムを開発して います!先月ツナギメエフエムにゲスト出演してお話をさせて頂きました!

Slide 4

Slide 4 text

お願い 登壇者の励みになるので是非ともご意見やご感想など、フィードバック頂けると助かります mm あとでスライドを公開します 4 / 95 写真撮影、SNSでの実況について    🙆‍♀📷    🙅‍♂📹💸    🙅📸👨‍👦‍👦 #trackb-1540-activerecordパターンの呪縛を学びほぐ して挑むクリーンアーキテクチャへの入り口 #phpcon_okinawa #track_b

Slide 5

Slide 5 text

今日お話すること・話さないこと 5 / 95 スコープ的なお話 🤫話さないこと 具体的な実装例 このセッションでは設計パターンの原理・原則に焦 点を当てます クリーンアーキテクチャへの移行手法 オブジェクト指向の基本的な概念や用語 📣話すこと 設計の原理・原則及びパターンの適用条件 クリーンアーキテクチャの目指すゴール ActiveRecordパターン経験者が陥りがちなアンチパ ターン 今後のActiveRecordとの付き合い方 クリーンアーキテクチャとの相性の良い設計パター ン

Slide 6

Slide 6 text

本セッションのゴール🏁 6 / 95 🤓ActiveRecordパターンの 経験者向け アンチパターンに陥らない為の前提知識を得ること 🧐クリーンアーキテクチャに 興味がある人向け 採用のメリットや概念が理解できること

Slide 7

Slide 7 text

7 / 95 学びほぐす「アンラーニング」とは? コトバンク(デジタル大辞泉)より What’s unlearning? 既得の知識・習慣を捨てること。 環境変化の激しい現代社会を生き抜くために、過去の経験にとらわれないよう、意識的に学習知識を捨て去ること。 “

Slide 8

Slide 8 text

8 / 95 ActiveRecordパターンとは? データベースの操作をオブジェクト指向で行うデザインパターン ActiveRecordパターンでは、テーブルと1対1で対応するモデル(クラス)を作ります。 モデルはテーブルの行と1対1で対応し、各行の属性(カラム)はモデルのプロパティにします。 これにより、オブジェクト指向の方法でデータベースを操作できます。 1. ChatGPTさん曰く ↩︎ 皆んな大好きActiveRecordパターン [1]

Slide 9

Slide 9 text

9 / 95 各種フレームワーク ライブラリで採用

Slide 10

Slide 10 text

10 / 95 フルスタックフレームワーク Laravel Eloquent ORM Symfony Doctrine ORM Yii framework ActiveRecord Ruby on Rails ActiveRecord Spring Boot Spring Data JPA ライブラリ Java Hibernate Golang GORM, XORM

Slide 11

Slide 11 text

11 / 95 Eloquent ORMでのモデル作成 scaffoldして $ php artisan make:model User

Slide 12

Slide 12 text

11 / 95 Eloquent ORMでのモデル作成 scaffoldして こうなる(する) $ php artisan make:model User namespace App\Models; use Illuminate\Database\Eloquent\Model; class User extends Model { protected $fillable = ['username', 'email']; protected $table = 'users'; }

Slide 13

Slide 13 text

12 / 95 Eloquentモデルの使い勝手 use App\Models\User; // ユーザーを作成 $user = new User(); $user->username = 'john_doe'; $user->email = 'john@example.com'; $user->save(); // ユーザーを更新 $user = User::find(1); $user->email = 'updated_email@example.com'; $user->save(); // ユーザーを削除 $user = User::find(1); $user->delete(); // ユーザーを検索 $users = User::all(); foreach ($users as $user) { echo $user->username . ': ' . $user->email . PHP_EOL; }

Slide 14

Slide 14 text

12 / 95 Eloquentモデルの使い勝手 // ユーザーを更新 $user = User::find(1); $user->email = 'updated_email@example.com'; $user->save(); use App\Models\User; // ユーザーを作成 $user = new User(); $user->username = 'john_doe'; $user->email = 'john@example.com'; $user->save(); // ユーザーを削除 $user = User::find(1); $user->delete(); // ユーザーを検索 $users = User::all(); foreach ($users as $user) { echo $user->username . ': ' . $user->email . PHP_EOL; }

Slide 15

Slide 15 text

12 / 95 Eloquentモデルの使い勝手 // ユーザーを削除 $user = User::find(1); $user->delete(); use App\Models\User; // ユーザーを作成 $user = new User(); $user->username = 'john_doe'; $user->email = 'john@example.com'; $user->save(); // ユーザーを更新 $user = User::find(1); $user->email = 'updated_email@example.com'; $user->save(); // ユーザーを検索 $users = User::all(); foreach ($users as $user) { echo $user->username . ': ' . $user->email . PHP_EOL; }

Slide 16

Slide 16 text

12 / 95 Eloquentモデルの使い勝手 // ユーザーを検索 $users = User::all(); foreach ($users as $user) { echo $user->username . ': ' . $user->email . PHP_EOL; } use App\Models\User; // ユーザーを作成 $user = new User(); $user->username = 'john_doe'; $user->email = 'john@example.com'; $user->save(); // ユーザーを更新 $user = User::find(1); $user->email = 'updated_email@example.com'; $user->save(); // ユーザーを削除 $user = User::find(1); $user->delete();

Slide 17

Slide 17 text

13 / 95 👍 超絶便利!

Slide 18

Slide 18 text

14 / 95 SQL書かなくていい! テーブルとオブジェクトが自動的にマッピングされ る!

Slide 19

Slide 19 text

15 / 95 ⛏深掘りしてみる

Slide 20

Slide 20 text

16 / 95 由来 Ruby on RailsのActiveRecordという名称は、マ ーチンファウラー氏の書籍「Patterns of Enterprise Application Architecture (PoEAA)」(2002年出版)で紹介された ActiveRecordパターンに由来しています。 1. パターンをまとめた設計カタログで、用語自 体はもっと先からあった模様 ↩︎ RailsのActiveRecordが初出ではない [1]

Slide 21

Slide 21 text

17 / 95 PoEAAで紹介されている設計カタログ 1. Domain Logic Patterns 2. Data Source Architectural Patterns 3. Object-Relational Behavioral Patterns 4. Object-Relational Structural Patterns 5. Object-Relational Metadata Mapping Patterns 6. Web Presentation Patterns 7. Distribution Patterns 8. Session State Patterns 9. Base Patterns 1. 各パターンの邦訳名は以下URLを参照 https://bliki-ja.github.io/pofeaa/CatalogOfPofEAA_Ja↩︎ 以下の9つのカテゴリに40種類のパターンをまとめられている [1]

Slide 22

Slide 22 text

18 / 95 PoEAAで紹介されている設計カタログ 1. Domain Logic Patterns 2. Data Source Architectural Patterns ← ココ! 3. Object-Relational Behavioral Patterns 4. Object-Relational Structural Patterns 5. Object-Relational Metadata Mapping Patterns 6. Web Presentation Patterns 7. Distribution Patterns 8. Session State Patterns 9. Base Patterns ActiveRecordパターンに言及されている領域

Slide 23

Slide 23 text

19 / 95 Data Source Architectural Patterns Table Data Gateway データベースのテーブルに対する操作を提供するオブジェクトのパターン Row Data Gateway データベースの行に対応するオブジェクトのパターン Active Record ← コレ! データベースの行に対応するオブジェクトで、自分自身を保存や削除できるパターン Data Mapper データベースの行とドメインオブジェクトの間のマッピングを担当するオブジェクトのパターン Data Source Architectural Patternsは、データベースとのやり取りをするためのパターンをまとめたもの

Slide 24

Slide 24 text

20 / 95 ActiveRecordとは? "An object that wraps a row in a database table or view, encapsulates the database access, and adds domain logic on that data. An object carries both data and behavior. " https://bliki-ja.github.io/pofeaa/ActiveRecord より データベースのテーブルやビューの列をラップし、データベースアクセスをカプセル化し、 ドメインロジックを追加するオブジェクト データと振る舞いの両方を持つオブジェクト “

Slide 25

Slide 25 text

21 / 95 Data Source? 🤔

Slide 26

Slide 26 text

22 / 95 データベースとのやり取りをするだけではない! Domain Logic Patterns ← 特にココ! ビジネスロジックの一部であるドメインロジックを実装するためのパターンをまとめたもの Object-Relational Behavioral Patterns オブジェクトとリレーショナルデータベースの間の振る舞いの問題を解決するためのパターンをまとめたも の Object-Relational Structural Patterns オブジェクトとリレーショナルデータベースの間の構造の問題を解決するためのパターンをまとめたもの Object-Relational Metadata Mapping Patterns オブジェクトとリレーショナルデータベースの間のマッピングをメタデータで管理するためのパターンをま とめたもの Data Source以外のパターンも混ざっている

Slide 27

Slide 27 text

23 / 95 💎ActiveRecordは凄くリッチなパターン

Slide 28

Slide 28 text

24 / 95 ActiveRecordパターンが向いているケース ドメインロジックがシンプル ドメインモデルとテーブルの構造が非常に近い 以下「全てを1対1で関連付けを行う」が前提 テーブルとクラス 行とインスタンス カラムとプロパティ

Slide 29

Slide 29 text

24 / 95 ActiveRecordパターンが向いているケース ドメインロジックがシンプル ドメインモデルとテーブルの構造が非常に近い 以下「全てを1対1で関連付けを行う」が前提 テーブルとクラス 行とインスタンス カラムとプロパティ 前提(適用条件)を満たしていれば非常に強力! 強力すぎるが故に前提を崩さないように設計時に注力するようになる

Slide 30

Slide 30 text

25 / 95 悪い兆候 🚩Red flags Fat Model Fat Controller MVAC Service Object 前提が崩れ始めてきた際に出現するワード

Slide 31

Slide 31 text

26 / 95 🤮責務が溢れてきてしまっている

Slide 32

Slide 32 text

27 / 95 関心事(=責務)を分けて考えると ActiveRecordパターンは Domain Model(Domain Logic Patterns) + Row Data Gateway(Data Source Architectural Patterns) 2つのパターンに分解できる。 業務ロジックを持ったRow Data Gateway。 ドメインの振る舞いとデータの永続化は別レイヤーの関心事 ActiveRecordパターンは敢えてレイヤーを分けずに密結合にしてDRY(Don't Repeat Your Self:繰り返し を避けること)に書けるように注力しています。 MEMO

Slide 33

Slide 33 text

28 / 95 レイヤードにしていく理由 責務を明確にし、複雑性の排除を行う テストしやすくする 変更の影響を局所化させる アプリケーションの責務を分割する ざっくり要約すると。。 各層の責務と依存関係を明確にすることで、本質的な問題と向き合うため MEMO

Slide 34

Slide 34 text

29 / 95 レイヤー増えるだけなの? それってServiceLayer足しただけでは?🤔

Slide 35

Slide 35 text

30 / 95 マインドセット が 違う 何を主として考えるか?

Slide 36

Slide 36 text

31 / 95 データソースを中心に した設計 データソースを変更した場合にドメインモデル が影響を受ける DomainがData Sourceに依存している

Slide 37

Slide 37 text

32 / 95 ドメインモデルを中心にした設計 データソースレイヤーは外に配置されている The Clean Architecture

Slide 38

Slide 38 text

33 / 95 設計の原則 DRY vs SOLID アプローチと目指しているものが違う

Slide 39

Slide 39 text

34 / 95 Clean Architecture 達 人に学ぶソフトウェア の構造と設計 SOLIDの原則 設計の原則がコンポーネントレベルまで適用 されている 第Ⅲ部 設計の原則

Slide 40

Slide 40 text

35 / 95 SOLIDの原則 設計の原則の頭文字 S: 単一責任の原則(SRP: Single Responsibility Principle) O: 開放閉鎖の原則(OCP: Open-Closed Principle) L: リスコフの置換原則(LSP: Liskov Substitution Principle) I: インターフェース分離の原則(ISP: Interface Segregation Principle) D: 依存性逆転の原則(DIP: Dependency Inversion Principle)

Slide 41

Slide 41 text

36 / 95 SRP: 単一責任の原則 一つのモジュールやクラスは、一つの機能だけを持つべきであるという原則 これにより、コードの変更やテストがし易く再利用性が向上する 🚩 悪い兆候 多目的クラス, 神クラス, 責務の不明確さ Single Responsibility Principle

Slide 42

Slide 42 text

36 / 95 SRP: 単一責任の原則 一つのモジュールやクラスは、一つの機能だけを持つべきであるという原則 これにより、コードの変更やテストがし易く再利用性が向上する 🚩 悪い兆候 多目的クラス, 神クラス, 責務の不明確さ Single Responsibility Principle クリーンアーキテクチャでは、各層やコンポーネントが単一の責務を持つように設計されます レイヤーごとに責務を分けることで、変更が他のレイヤーに影響しないようにしています

Slide 43

Slide 43 text

37 / 95 OCP: 開放閉鎖の原則 ソフトウェア要素は、拡張には開いており、修正には閉じているべきであるという原則。 これにより、ソースコードを変えずに機能を拡張でき、保守性や再利用性が向上する 🚩 悪い兆候 条件分岐の乱用, 責務過多 Open-Closed Principle

Slide 44

Slide 44 text

37 / 95 OCP: 開放閉鎖の原則 ソフトウェア要素は、拡張には開いており、修正には閉じているべきであるという原則。 これにより、ソースコードを変えずに機能を拡張でき、保守性や再利用性が向上する 🚩 悪い兆候 条件分岐の乱用, 責務過多 Open-Closed Principle クリーンアーキテクチャでは、内側の層が外側の層に対して開放されており、外側の層が内側の 層に対して閉鎖されます 内側のレイヤーは外側のレイヤーに影響されないようにし、外側のレイヤーは内側のレイヤーを 拡張することができます

Slide 45

Slide 45 text

38 / 95 LSP: リスコフの置換原則 派生クラスは、その基底クラスと置換可能であるべきであるという原則。 これにより、型の互換性や拡張性が保たれ、コードの再利用性や可読性が向上する 🚩 悪い兆候 事前条件の強化, 事後条件の弱化 Liskov Substitution Principle

Slide 46

Slide 46 text

38 / 95 LSP: リスコフの置換原則 派生クラスは、その基底クラスと置換可能であるべきであるという原則。 これにより、型の互換性や拡張性が保たれ、コードの再利用性や可読性が向上する 🚩 悪い兆候 事前条件の強化, 事後条件の弱化 Liskov Substitution Principle クリーンアーキテクチャでは、インターフェースを用いて抽象化された内側の層が、具体的な実 装である外側の層に置き換えられるように設計されます 内側のレイヤーは外側のレイヤーの具体的な実装を知る必要がなくなります

Slide 47

Slide 47 text

39 / 95 ISP: インターフェース分離の原則 インターフェースは、分離できるものは分離するべきであるという原則 これにより、クライアントは不要なメソッドに依存しなくて済み、コードの可読性や保守性が向上する 🚩 悪い兆候 肥大化したインターフェース, 不完全なインターフェース Interface Segregation Principle

Slide 48

Slide 48 text

39 / 95 ISP: インターフェース分離の原則 インターフェースは、分離できるものは分離するべきであるという原則 これにより、クライアントは不要なメソッドに依存しなくて済み、コードの可読性や保守性が向上する 🚩 悪い兆候 肥大化したインターフェース, 不完全なインターフェース Interface Segregation Principle クリーンアーキテクチャでは、各層やコンポーネントが必要最低限のインターフェースを持つよ うに設計されます 内側のレイヤーが外側のレイヤーの具体的な実装を知る必要がなくなります

Slide 49

Slide 49 text

40 / 95 DIP: 依存性逆転の原則 プログラムの重要な部分は、逆に重要でない部分にが依存しないようにするべきである。 そのためにモジュールは、抽象に依存するようにするべきであるという原則 これによりモジュールは、実装の詳細に左右されなくなり、コードの柔軟性や再利用性が向上する 🚩 悪い兆候 抽象が具象に依存する Dependency Inversion Principle

Slide 50

Slide 50 text

40 / 95 DIP: 依存性逆転の原則 プログラムの重要な部分は、逆に重要でない部分にが依存しないようにするべきである。 そのためにモジュールは、抽象に依存するようにするべきであるという原則 これによりモジュールは、実装の詳細に左右されなくなり、コードの柔軟性や再利用性が向上する 🚩 悪い兆候 抽象が具象に依存する Dependency Inversion Principle クリーンアーキテクチャでは、依存性逆転の原則を適用しています 内側の層が外側の層に依存しないように設計されています

Slide 51

Slide 51 text

41 / 95 The Clean Architecture(≠Clean Architecture)の誤 解😕

Slide 52

Slide 52 text

42 / 95 親の顔より見た図 「Clean Architecture」の22章の中で例示されているLayered Architecture 本日10ページぶり2回目の登場

Slide 53

Slide 53 text

43 / 95 誤解されている内容 この通りに実装するのが正解 Clean Architectureの具体例として挙げられているだけ レイヤーは4層 レイヤーは4つ以外は認めないというルールはないと明記されている MVC1に近い構成にしないといけない そもそもWebアプリケーション以外 も想定している図なので盲信すると危険! 最近のWebアプリケーションの構成と乖離が出てくる 他にも色々あるけれど。。 1. 同心円の外側のDevices, UI, External Interfacesがソレ それぞれ、キーボードやNativeアプリ(GUI)、CLI、メールやイベント・キューとかもありそう ↩︎ やたら独り歩きしている感 [1]

Slide 54

Slide 54 text

44 / 95 本質的な2つルール 依存の方向は外側から内側のレイヤーにのみ向ける 制御の流れと依存の向きは依存性逆転の原則で分離してコントロールする [1]

Slide 55

Slide 55 text

45 / 95 各層(レイヤー間)の関係性 レイヤー 位置 抽象度 重要度 安定度 具体的な コンポーネント 役割 内側 高い (抽象) 高い 高い ビジネスルール ドメインモデル ソフトウェアの本質的部分や目的を表すもの 外側 低い (具象) 低い 低い データソース (データベース,Web API) ソフトウェアの技術的詳細や手段を表すもの 1. 抽象度とは、コンポーネントが具体的な実装や詳細から独立している程度を表す指標 ↩︎ 2. 重要度とは、システムが解決しようとしている課題や問題領域の関心事にどれだけ適合しているかを表す指標。 高い場合は修正の影響が大きい(テスト含めて) ↩︎ 3. 安定度とは、修正されにくい度合い又は、依存性関係が少なく他のモジュールの修正の影響を受けづらい度合い ↩︎ 円の内側は高レイヤーで外側は低レイヤーとする [1] [2] [3]

Slide 56

Slide 56 text

46 / 95 抽象度と重要度と安定度は連動する

Slide 57

Slide 57 text

46 / 95 抽象度と重要度と安定度は連動する 。。のか?

Slide 58

Slide 58 text

47 / 95 🙅‍♀️

Slide 59

Slide 59 text

47 / 95 🙅‍♀️ 違う、そうじゃない

Slide 60

Slide 60 text

48 / 95 🙆‍♀ 重要度に対して抽象度を調整し安定度をコントロ ールする

Slide 61

Slide 61 text

49 / 95 各層(レイヤー間)の 目指す関係性 レイヤー 位置 抽象度 重要度 安定度 具体的な コンポーネント 役割 内側 高い (抽象) 高い 高い ビジネスルール ドメインモデル ソフトウェアの本質的部分や目的を表すもの 外側 低い (具象) 低い 低い データソース (データベース,Web API) ソフトウェアの技術的詳細や手段を表すもの 1. 抽象度とは、コンポーネントが具体的な実装や詳細から独立している程度を表す指標 ↩︎ 2. 重要度とは、システムが解決しようとしている課題や問題領域の関心事にどれだけ適合しているかを表す指標。 高い場合は修正の影響が大きい(テスト含めて) ↩︎ 3. 安定度とは、修正されにくい度合い又は、依存性関係が少なく他のモジュールの修正の影響を受けづらい度合い ↩︎ 円の内側は高レイヤーで外側は低レイヤーとする [1] [2] [3]

Slide 62

Slide 62 text

50 / 95 各層(レイヤー間)の 目指す関係性 重要度の高いものコアとし、中心に据える レイヤー 位置 重要度 抽象度 安定度 具体的な コンポーネント 役割 内側 高い 高い (抽象) 高い ビジネスルール ドメインモデル ソフトウェアの本質的部分や目的を表すもの 外側 低い 低い (具象) 低い データソース (データベース,Web API) ソフトウェアの技術的詳細や手段を表すもの 1. 重要度とは、システムが解決しようとしている課題や問題領域の関心事にどれだけ適合しているかを表す指標。 高い = 内部品質の高さが求められるコアとなるもの↩︎ 2. 抽象度とは、コンポーネントが他の具体的な実装や詳細から独立させた度合いの指標 ↩︎ 3. 安定度とは、修正されにくい度合い又は、依存性関係を少なくし、他のモジュールの修正に閉じている状態の度合い ↩︎ [1] [2] [3]

Slide 63

Slide 63 text

51 / 95 SDP: 安定依存の原則 パッケージ設計の原則の一つで、パッケージの依存は常により安定したパッケージに向くべきであるという原 則 安定したレイヤーに依存しないといけない 不安定なレイヤーに依存すると、その依存しているレイヤーが不安定になる Stable Dependencies Principle

Slide 64

Slide 64 text

51 / 95 SDP: 安定依存の原則 パッケージ設計の原則の一つで、パッケージの依存は常により安定したパッケージに向くべきであるという原 則 安定したレイヤーに依存しないといけない 不安定なレイヤーに依存すると、その依存しているレイヤーが不安定になる Stable Dependencies Principle クリーンアーキテクチャでは、重要度が一番高いもの=ドメインモデルとしている ドメインモデルの設計を長期的に安定させるという考え方

Slide 65

Slide 65 text

52 / 95 🙃技術的な詳細で不安定なデー タソースにドメインが依存する のはおかしい! 抽象が具象に依存する

Slide 66

Slide 66 text

53 / 95 複雑で不安定なものから切り離す手法 制御のフロー Application Domain Infrastructure 依存の向き x 抽象が具象に依存している Application Domain Infrastructure 制御の流れと依存の向きは連動してしまう ※説明を単純にする為にレイヤーを3層にした例 PoEAA的に以下の関心事に対応 Application: Web Presentation Domain: Domain Logic Infrastructure: Data Source MEMO

Slide 67

Slide 67 text

54 / 95 複雑で不安定なものから切り離す手法 依存性逆転の原則 ドメイン層はインターフェースに依存させることで永続化技術の詳細から隔離させる Application Domain «interface» Repository Infrastructure RDBRepository RestRepository Repository パターン ドメインモデルの永続化するのに具象に依存させない

Slide 68

Slide 68 text

55 / 95 ✨これで本当にCleanといえるのか?

Slide 69

Slide 69 text

56 / 95 ドメインが持つ依存を排除し抽象度を上げる アプリケーションサービス アプリケーションサービスは、ドメイン層とプレゼンテーション層の間に位置し、ドメインオブジェクトや レポジトリなどを利用して、アプリケーションの要求を満たす。 Controller UseCase Domain «interface» Repository Infrastructure RDBRepository RestRepository Application Layer を見直し ドメインモデルをもっと安定させる

Slide 70

Slide 70 text

56 / 95 ドメインが持つ依存を排除し抽象度を上げる アプリケーションサービス アプリケーションサービスは、ドメイン層とプレゼンテーション層の間に位置し、ドメインオブジェクトや レポジトリなどを利用して、アプリケーションの要求を満たす。 Controller UseCase Domain «interface» Repository Infrastructure RDBRepository RestRepository Application Layer を見直し ドメインモデルをもっと安定させる 完全に 独立

Slide 71

Slide 71 text

57 / 95 レイヤー間だけでなく クラス(コンポーネント)間にも SOLID・パッケージ設計の原則は適用可能 Clean Codeという考え方

Slide 72

Slide 72 text

58 / 95 ActiveRecordパターン の呪縛のまとめ

Slide 73

Slide 73 text

59 / 95 ActiveRecordパターンの呪縛 ドメインモデルとテーブルを1:1にしないといけない データ永続化とドメイン振る舞いを一体化させる思い込み テーブル設計を先にし、モデルに制約を後付けする それが一番早くて黄金パターンだという成功体験(シンプルなドメインのみという前提無視) レコード=ドメインオブジェクトでいつでも更新・削除は簡単 オブジェクトの属性を更新したり、saveやdeleteがいつでもカジュアルにできる(表面的な操作としては) 認知バイアス多め

Slide 74

Slide 74 text

60 / 95 ああ・・・ クセになってんだ

Slide 75

Slide 75 text

61 / 95 学びほぐし事例集

Slide 76

Slide 76 text

62 / 95 アンラーニング、その前に… 新しい用語が複数出てきます 詳細な説明はしません クリーンアーキテクチャでの設計に興味を持ってもらうことが目標です 気になった用語はワードだけ覚えて、後で自分で調べるスタイルでお願いします 🙇‍♀ アンチパターンとその対処法になります 以前の考え方や行動パターンと対比して新しいものに触れます 全てやらないとクリーンアーキテクチャにならないわけではありません 設計パターンなので、思考の流れを理解するのが大事です クリーンアーキテクチャの経験者は一緒に内省しましょう😅 1. The Clean Architectureの誤解で触れた通り ↩︎ 2. 登壇者は紹介するアンチパターンの全てに心当たりがあります ↩︎ ここからの注意点 [1] [2]

Slide 77

Slide 77 text

63 / 95 テーブル定義 = Domain modelの プロパティ 表面的なデータ構造だけで設計してませんか?

Slide 78

Slide 78 text

64 / 95 学びほぐし事例 の変革の必要性 テーブル設計 != モデル設計 論理設計と物理設計を同じと思っている テーブル設計の変更に影響を受ける Valueオブジェクト や集約 が表現されていない 振る舞いや制約がないか、分散してしまっている primitiveな型Onlyは要注意 テーブルの正規化対応できない 単純に関連モデルとして実装すると、データ構造が露出する 1. 日本語論理名を英語に翻訳するだけとか。。 ↩︎ 2. 同一性を持たず、属性が同じなら同じと扱うオブジェクト。例えば、日付や金額など ↩︎ 3. 一貫性を保つ必要があるドメインオブジェクトのまとまり。トランザクション境界にもなる ↩︎ ActiveRecordは必ず1対1にしていたけれど [1] [2] [3]

Slide 79

Slide 79 text

65 / 95 学びほぐし事例 のブレークスルー まずはドメインに向き合いましょう 1. ドメインの問題領域を理解する 2. ユビキタス言語を定義する 3. ドメインオブジェクトを識別する オブジェクトの種類を決めValueオブジェクトや集約を識別する。 振る舞いや関係(協調・制約)も洗い出す ドメインファーストな設計をしましょう。永続化はレイヤーを分けて次に考えましょう

Slide 80

Slide 80 text

66 / 95 モデルにsetterが 存在 プロパティが書き換え可能

Slide 81

Slide 81 text

67 / 95 学びほぐし事例 の変革の必要性 状態を変更した際に副作用が発生する可能性がある 制約をすり抜けてしまう コードの可読性が悪い ドメインイベントを見落とす 手続き的にsetterを呼び出しされても意図が伝わらない ビジネスルールがあっても認識されない 1. 後からビジネスルールの追加があった場合などに対応漏れが発生する ↩︎ ActiveRecordはミュータブルなモデル [1]

Slide 82

Slide 82 text

68 / 95 学びほぐし事例 のブレークスルー イミュータブルなモデルにしましょう ドメインイベント毎に適切なメソッドを定義しましょう コンストラクタで制約を定義しましょう イミュータブルなモデルでコンストラクタに制約が定義されていれば、各種イベントで状態が変化しても制 約が必ず有効になります オブジェクト作成後にその状態を変えることをできなくする(不変オブジェクトにする)

Slide 83

Slide 83 text

69 / 95 コンストラクタ に制約がない 引数の型指定も制約になります

Slide 84

Slide 84 text

70 / 95 学びほぐし事例 の変革の必要性 制約違反したオブジェクトが存在できてしまう 制約をすり抜けて整合性や一貫性が失われる validメソッドが実装されていても制約違反を発見するタイミングが遅い validメソッドの呼び忘れも起こる ビジネスルールがわからなくなる 認知負荷が高くなる ドメインの制約を守る実装がそのドメインモデルを使う人任せになってしまう ビジネスルールが散らばってしまう ActiveRecordは制約違反があってもオブジェクト自体は存在できてしまう ドメインモデルがgetter・setterしかない場合にドメイン貧血症と言われます 個人的にはコンストラクタの制約がないことが問題だと感じています MEMO

Slide 85

Slide 85 text

71 / 95 学びほぐし事例 のブレークスルー 適切にコンストラクタに制約を定義しましょう 制約違反したオブジェクトが存在しないことが保証される また、制約違反を発見するタイミングが早くなる イミュータブルなモデルにしましょう イミュータブルな設計とセットでコンストラクタに制約を実装する

Slide 86

Slide 86 text

72 / 95 読み込みと書き 込みのモデルが同じ データの永続化と読み込みの両方に同じモデルを使用する

Slide 87

Slide 87 text

73 / 95 学びほぐし事例 の変革の必要性 正規化したデータをうまく扱えない 書き込み時はコードのみ、名称は不要 逆に読み込み時には名称も一緒にロードして表示したい データ更新時に不要なデータまで必要になる モデルが複雑で関連するデータが多い場合に準備が大変 一括更新になりがちでデータの不整合が発生する 不要なカラムまで更新して、同時更新されると不整合となる ActiveRecordではテーブルとの関連は原則1:1。分けれなくもないけれど普通はやらない

Slide 88

Slide 88 text

74 / 95 学びほぐし事例 のブレークスルー データの更新(コマンド)と参照(クエリ)を別のモデルに分ける コマンド・クエリ責務分離(Command-Query Responsibility Segregation, CQRS)の原則に従う。 コマンドは正規化してデータの一貫性と整合性を保つ クエリは非正規化してデータ参照時のパフォーマンスを上げる 1. モデルだけでなくデータソースも別にし、スケーラビリティやパフォーマンスを向上させる場合もありま す。 ↩︎ ReadオブジェクトとWriteオブジェクトを分ける [1]

Slide 89

Slide 89 text

75 / 95 ユースケース毎 に適切なコマンドが ない データの新規登録と更新の両対応のメソッドが定義されている

Slide 90

Slide 90 text

76 / 95 学びほぐし事例 の変革の必要性 ドメインのイベントを見落としている 新規登録時と更新時の制約が違うケースがある 1. モデルにnull許可のプロパティが多い場合は情報が混ざっている可能性がある ↩︎ ActiveRecordでCRUDが簡単にでき、最新状態を常に上書きしていたけれど [1]

Slide 91

Slide 91 text

77 / 95 学びほぐし事例 のブレークスルー イミュータブルデータモデルで設計してみる 更新と削除を行わないテーブル設計 エンティティをイベントとリソースに分けてモデリングする コマンドのオブジェクトを分けるようにする 新規登録用のオブジェクトと更新用のオブジェクト(イベント対象と内容のみ)で分ける 1. T字形ER データベース設計技法と相性が良いです ↩︎ 事実を失う操作(更新・削除)を行わせない、データの構造や関係とする [1] イミュータブルデータモデルは、イベントソーシングパターンとも相性が良いです。 イベントの量が多くなった際に分散処理しやすくなります。 MEMO

Slide 92

Slide 92 text

78 / 95 ユースケース毎 に適切な粒度のクエ リがない 集約がうまく扱えていない

Slide 93

Slide 93 text

79 / 95 学びほぐし事例 の変革の必要性 データの関連を適切にクエリとして表現する必要がある 集約をクエリとして表現しないと、クエリ間 でもN+1が起こる 集約データをRepositoryから取得する際の戻り値がEager Loadになる Lazy Loadでエンティティを返すにはテクニックが必要 1. 個々のクエリを再利用してしまい、APIでもN+1で呼び出される危険性がある ↩︎ ActiveRecordはデフォルトでLazy Loadが前提になっているけれど [1]

Slide 94

Slide 94 text

80 / 95 学びほぐし事例 のブレークスルー 集約を扱うクエリを適切に定義する 集約ルートに対して一覧表示や詳細表示などのシーンに応じてクエリを最適化する 集計機能実装時にデータベースの力を借りる 大量データをメモリロードせずに、集計関数を利用する パフォーマンスに考慮したクエリを定義する

Slide 95

Slide 95 text

81 / 95 Hello world!! Clean Architecture🎉 学習おつかれさまでした

Slide 96

Slide 96 text

82 / 95 Good by ActiveRecord?🤔 クリーンアーキテクチャとActiveRecordって相性悪くない?

Slide 97

Slide 97 text

83 / 95 ソフトウェア設計における戦術と戦略の違い 戦術(Tactics) 個々のモジュールやクラスなどの小さなスコープで適用できる設計テクニック(Design Pattern) インターフェースと実装の分離や情報隠蔽など → SOLID原則も含まれます 戦略(Strategy) システム全体や大きなスコープで適用できる設計テクニック(Architectural Pattern) モジュール間の依存関係や抽象化のレベルなど → クリーンアーキテクチャ 戦略には中長期的な視点が必要

Slide 98

Slide 98 text

84 / 95 ActiveRecordは戦術?それとも戦略? PoEAA的にはData Source Architectural Patterns データソースを中心にした設計で戦略的 ActiveRecordパターンは以下の設計思想・戦術を組み合わせるとより戦略的になる 1. CoC (設定より規約) 2. DRY (繰り返しを避ける) 3. REST (RDBMSのCRUDとHTTPのリクエストメソッドに関連させる) 1. ActiveRecordのモデル名とエンドポイントのURIも関連させる ↩︎ 戦術を組み合わせることで戦略に昇華することができる [1]

Slide 99

Slide 99 text

戦略の相違 85 / 95 🏃‍♂️ActiveRecord レイヤーを分けずにDRYに書く戦略 データソースやビジネスロジックの分離は考えない 🌱クリーンアーキテクチャ レイヤーに分けて依存関係を分離し、品質を高める 戦略 ドメインからデータソースを意識させなくする

Slide 100

Slide 100 text

86 / 95 【結論】 戦略として相性悪い 目指している方向性が違い、戦術的にもバッティングしてしまっている 戦略なので設計時の思考にも反映されがちで矛盾が生じる (アンチパターンの要因)

Slide 101

Slide 101 text

87 / 95 なので。。

Slide 102

Slide 102 text

88 / 95 今後のActiveRecord系ライブラリとの向き合い方 Infrastructure層のRepositoryでActiveRecordを使う ORマッパー・クエリービルダとして使う ただしドメインオブジェクトとActiveRecordオブジェクトへのマッピングが必要 ドメインオブエジェクトではなくDTO(Data Transfer Object), DPO(Data Persistence Object)の場合も あり 1. Data Mapperでマッピングするほうが良いが、関心事が混ざる問題がある ↩︎ 2. DPOはRepositoryのインターフェース側に定義させる ↩︎ ActiveRecordは戦術的に使うだけに留める [1] [2]

Slide 103

Slide 103 text

89 / 95 フレームワークとの関わり方にも通ずる 戦術なら変更は可能。戦略変更は計画的に 一番外側に配置して、ユースケース・ドメインからは依存させないようにする 戦略的ではなく、戦術として使う(薄く利用する) 戦術は局所的な影響に留めることができ、比較的容易に変更できることが多い

Slide 104

Slide 104 text

90 / 95 まとめ

Slide 105

Slide 105 text

91 / 95 設計パターンの適用条件 ActiveRecordパターンの場合 (DRY / 開発効率スピード重視 / 作るものがシンプルで明確) データベースのテーブルとオブジェクトの構造がほぼ同じ データベースの操作が単純 ビジネスロジックが複雑でない クリーンアーキテクチャの場合 (SOLID / 変更容易性・拡張性重視 / 作るものが複雑で不明確、又は変更多い) ビジネスロジックやドメイン知識が重要 外部リソースやフレームワークに依存しないようにしたい 変更や追加に柔軟に対応したい 再利用可能なケース

Slide 106

Slide 106 text

92 / 95 ActiveRecordの呪縛から逃れるには? ActiveReocrdは責務が多いので分解する SOLIDでコアなドメインになるようにモデリングする ドメインファーストな設計をする

Slide 107

Slide 107 text

93 / 95 よりクリーンなコードにしていく 複雑なものは下位層に隠す データソースだけでなく他のものも モデルの表現を豊かにする Valueオブジェクト・集約・ドメインイベントを識別して、振る舞いや関係を定義する 構造や振る舞いがわかりやすくする イミュータブルなモデル・データ構造にして一貫性を保つ CQRSの原則に従いスケーラビリティやパフォーマンスを高める 将来的な分散システム(マイクロサービス等)への移行も視野に入れておく 切り出しやすくして変化に強くする → 複雑さが下位層に隠れている状態

Slide 108

Slide 108 text

94 / 95 Note ツナギメエフエム Ep.52 ちょうぜつソフトウェア設計入門――PHPで理解するオブジェクト指向の活用 Patterns of Enterprise Application Architecture / Martin Fowler’s Bliki (ja) texta.fm 3.Low-Code Development texta.fm 4.Not Just ORM A Philosophy of Software Design を30分でざっと理解する TM(T字形ER)によるモデリング PHPではじめるCQRSっぽいやつ 現場で役立つシステム設計の原則 〜変更を楽で安全にするオブジェクト指向の実践技法 参考資料&紹介した記事など

Slide 109

Slide 109 text

95 / 95 ご清聴ありがとうございました