69.3K Views
September 16, 23
スライド概要
ActiveRecordパターンに引きづられて設計が歪みがちになるのを学びほぐすことでクリーンアーキテクチャでの開発の良いスタートラインに立てるのではないでしょうか?
ActiveRecordパターンの呪縛を 学びほぐして挑むクリーンアー キテクチャへの入り口 PHPカンファレンス沖縄2023 Sep 16th, 2023. v0.0.4 Press Space for next page
自己紹介 katzumi(かつみ)と申します 「障害のない社会をつくる」をビジョンに掲げている「りたりこ」という会社に所属しています 以下のアカウントで活動しています katzchum k2tzumi katzumi 2 / 95
日々ちょうぜつ分厚い本と向き合っています 圧巻の1.5K頁オーバー。3年に一回、大改訂(大改定)があります。レセプト業務の基盤システムを開発して います!先月ツナギメエフエムにゲスト出演してお話をさせて頂きました! 3 / 95
お願い 写真撮影、SNSでの実況について 登壇者の励みになるので是非ともご意見やご感想など、フィードバック頂けると助かります mm あとでスライドを公開します 🙆♀📷 🙅♂📹💸 🙅📸👨👦👦 #trackb-1540-activerecordパターンの呪縛を学びほぐ して挑むクリーンアーキテクチャへの入り口 #phpcon_okinawa #track_b 4 / 95
今日お話すること・話さないこと スコープ的なお話 🤫話さないこと 具体的な実装例 このセッションでは設計パターンの原理・原則に焦 点を当てます クリーンアーキテクチャへの移行手法 オブジェクト指向の基本的な概念や用語 📣話すこと 設計の原理・原則及びパターンの適用条件 クリーンアーキテクチャの目指すゴール ActiveRecordパターン経験者が陥りがちなアンチパ ターン 今後のActiveRecordとの付き合い方 クリーンアーキテクチャとの相性の良い設計パター ン 5 / 95
本セッションのゴール🏁 🤓ActiveRecordパターンの 経験者向け 🧐クリーンアーキテクチャに 興味がある人向け アンチパターンに陥らない為の前提知識を得ること 採用のメリットや概念が理解できること 6 / 95
学びほぐす「アンラーニング」とは? What’s unlearning? “ 既得の知識・習慣を捨てること。 環境変化の激しい現代社会を生き抜くために、過去の経験にとらわれないよう、意識的に学習知識を捨て去ること。 コトバンク(デジタル大辞泉)より 7 / 95
ActiveRecordパターンとは? 皆んな大好きActiveRecordパターン データベースの操作をオブジェクト指向で行うデザインパターン ActiveRecordパターンでは、テーブルと1対1で対応するモデル(クラス)を作ります。 モデルはテーブルの行と1対1で対応し、各行の属性(カラム)はモデルのプロパティにします。 これにより、オブジェクト指向の方法でデータベースを操作できます。[1] 1. ChatGPTさん曰く ↩︎ 8 / 95
各種フレームワーク ライブラリで採用 9 / 95
フルスタックフレームワーク ライブラリ Laravel Java Eloquent ORM Symfony Doctrine ORM Yii framework ActiveRecord Ruby on Rails ActiveRecord Spring Boot Spring Data JPA Hibernate Golang GORM, XORM 10 / 95
Eloquent ORMでのモデル作成 $ php artisan make:model User scaffoldして 11 / 95
Eloquent ORMでのモデル作成 $ php artisan make:model User scaffoldして namespace App\Models; use Illuminate\Database\Eloquent\Model; class User extends Model { protected $fillable = ['username', 'email']; } protected $table = 'users'; こうなる(する) 11 / 95
Eloquentモデルの使い勝手
use App\Models\User;
ユーザーを作成
//
$user = new User();
$user->username = 'john_doe';
$user->email = '[email protected]';
$user->save();
ユーザーを更新
//
$user = User::find(1);
$user->email = '[email protected]';
$user->save();
ユーザーを削除
//
$user = User::find(1);
$user->delete();
ユーザーを検索
//
$users = User::all();
foreach ($users as $user) {
echo $user->username . ': ' . $user->email . PHP_EOL;
}
12 / 95
Eloquentモデルの使い勝手
use App\Models\User;
ユーザーを作成
//
$user = new User();
$user->username = 'john_doe';
$user->email = '[email protected]';
$user->save();
ユーザーを更新
//
$user = User::find(1);
$user->email = '[email protected]';
$user->save();
ユーザーを削除
//
$user = User::find(1);
$user->delete();
ユーザーを検索
//
$users = User::all();
foreach ($users as $user) {
echo $user->username . ': ' . $user->email . PHP_EOL;
}
12 / 95
Eloquentモデルの使い勝手
use App\Models\User;
ユーザーを作成
//
$user = new User();
$user->username = 'john_doe';
$user->email = '[email protected]';
$user->save();
ユーザーを更新
//
$user = User::find(1);
$user->email = '[email protected]';
$user->save();
ユーザーを削除
//
$user = User::find(1);
$user->delete();
ユーザーを検索
//
$users = User::all();
foreach ($users as $user) {
echo $user->username . ': ' . $user->email . PHP_EOL;
}
12 / 95
Eloquentモデルの使い勝手
use App\Models\User;
ユーザーを作成
//
$user = new User();
$user->username = 'john_doe';
$user->email = '[email protected]';
$user->save();
ユーザーを更新
//
$user = User::find(1);
$user->email = '[email protected]';
$user->save();
ユーザーを削除
//
$user = User::find(1);
$user->delete();
ユーザーを検索
//
$users = User::all();
foreach ($users as $user) {
echo $user->username . ': ' . $user->email . PHP_EOL;
}
12 / 95
👍 超絶便利! 13 / 95
SQL書かなくていい! テーブルとオブジェクトが自動的にマッピングされ る! 14 / 95
⛏深掘りしてみる 15 / 95
由来 RailsのActiveRecordが初出ではない Ruby on RailsのActiveRecordという名称は、マ ーチンファウラー氏の書籍「Patterns of Enterprise Application Architecture (PoEAA)」(2002年出版)で紹介された [1] ActiveRecordパターンに由来しています。 1. パターンをまとめた設計カタログで、用語自 体はもっと先からあった模様 ↩︎ 16 / 95
PoEAAで紹介されている設計カタログ 以下の9つのカテゴリに40種類のパターンをまとめられている [1] 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 ↩︎ 17 / 95
PoEAAで紹介されている設計カタログ ActiveRecordパターンに言及されている領域 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 18 / 95
Data Source Architectural Patterns Data Source Architectural Patternsは、データベースとのやり取りをするためのパターンをまとめたもの Table Data Gateway データベースのテーブルに対する操作を提供するオブジェクトのパターン Row Data Gateway データベースの行に対応するオブジェクトのパターン Active Record ← コレ! データベースの行に対応するオブジェクトで、自分自身を保存や削除できるパターン Data Mapper データベースの行とドメインオブジェクトの間のマッピングを担当するオブジェクトのパターン 19 / 95
ActiveRecordとは? https://bliki-ja.github.io/pofeaa/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. " “ データベースのテーブルやビューの列をラップし、データベースアクセスをカプセル化し、 ドメインロジックを追加するオブジェクト データと振る舞いの両方を持つオブジェクト 20 / 95
Data Source? 🤔 21 / 95
データベースとのやり取りをするだけではない! Data Source以外のパターンも混ざっている Domain Logic Patterns ← 特にココ! ビジネスロジックの一部であるドメインロジックを実装するためのパターンをまとめたもの Object-Relational Behavioral Patterns オブジェクトとリレーショナルデータベースの間の振る舞いの問題を解決するためのパターンをまとめたも の Object-Relational Structural Patterns オブジェクトとリレーショナルデータベースの間の構造の問題を解決するためのパターンをまとめたもの Object-Relational Metadata Mapping Patterns オブジェクトとリレーショナルデータベースの間のマッピングをメタデータで管理するためのパターンをま とめたもの 22 / 95
💎ActiveRecordは凄くリッチなパターン 23 / 95
ActiveRecordパターンが向いているケース ドメインロジックがシンプル ドメインモデルとテーブルの構造が非常に近い 以下「全てを1対1で関連付けを行う」が前提 テーブルとクラス 行とインスタンス カラムとプロパティ 24 / 95
ActiveRecordパターンが向いているケース ドメインロジックがシンプル ドメインモデルとテーブルの構造が非常に近い 以下「全てを1対1で関連付けを行う」が前提 テーブルとクラス 行とインスタンス カラムとプロパティ 前提(適用条件)を満たしていれば非常に強力! 強力すぎるが故に前提を崩さないように設計時に注力するようになる 24 / 95
悪い兆候 🚩 Red flags 前提が崩れ始めてきた際に出現するワード Fat Model Fat Controller MVAC Service Object 25 / 95
🤮責務が溢れてきてしまっている 26 / 95
関心事(=責務)を分けて考えると ドメインの振る舞いとデータの永続化は別レイヤーの関心事 ActiveRecordパターンは Domain Model(Domain Logic Patterns) + Row Data Gateway(Data Source Architectural Patterns) 2つのパターンに分解できる。 業務ロジックを持ったRow Data Gateway。 MEMO ActiveRecordパターンは敢えてレイヤーを分けずに密結合にしてDRY(Don't Repeat Your Self:繰り返し を避けること)に書けるように注力しています。 27 / 95
レイヤードにしていく理由 アプリケーションの責務を分割する 責務を明確にし、複雑性の排除を行う テストしやすくする 変更の影響を局所化させる MEMO ざっくり要約すると。。 各層の責務と依存関係を明確にすることで、本質的な問題と向き合うため 28 / 95
レイヤー増えるだけなの? それってServiceLayer足しただけでは?🤔 29 / 95
マインドセット が 違う 何を主として考えるか? 30 / 95
データソースを中心に した設計 DomainがData Sourceに依存している データソースを変更した場合にドメインモデル が影響を受ける 31 / 95
ドメインモデルを中心にした設計 The Clean Architecture データソースレイヤーは外に配置されている 32 / 95
設計の原則 DRY vs SOLID アプローチと目指しているものが違う 33 / 95
Clean Architecture 達 人に学ぶソフトウェア の構造と設計 第Ⅲ部 設計の原則 SOLIDの原則 設計の原則がコンポーネントレベルまで適用 されている 34 / 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) 35 / 95
SRP: 単一責任の原則 Single Responsibility Principle 一つのモジュールやクラスは、一つの機能だけを持つべきであるという原則 これにより、コードの変更やテストがし易く再利用性が向上する 🚩 悪い兆候 多目的クラス, 神クラス, 責務の不明確さ 36 / 95
SRP: 単一責任の原則 Single Responsibility Principle 一つのモジュールやクラスは、一つの機能だけを持つべきであるという原則 これにより、コードの変更やテストがし易く再利用性が向上する 🚩 悪い兆候 多目的クラス, 神クラス, 責務の不明確さ クリーンアーキテクチャでは、各層やコンポーネントが単一の責務を持つように設計されます レイヤーごとに責務を分けることで、変更が他のレイヤーに影響しないようにしています 36 / 95
OCP: 開放閉鎖の原則 Open-Closed Principle ソフトウェア要素は、拡張には開いており、修正には閉じているべきであるという原則。 これにより、ソースコードを変えずに機能を拡張でき、保守性や再利用性が向上する 🚩 悪い兆候 条件分岐の乱用, 責務過多 37 / 95
OCP: 開放閉鎖の原則 Open-Closed Principle ソフトウェア要素は、拡張には開いており、修正には閉じているべきであるという原則。 これにより、ソースコードを変えずに機能を拡張でき、保守性や再利用性が向上する 🚩 悪い兆候 条件分岐の乱用, 責務過多 クリーンアーキテクチャでは、内側の層が外側の層に対して開放されており、外側の層が内側の 層に対して閉鎖されます 内側のレイヤーは外側のレイヤーに影響されないようにし、外側のレイヤーは内側のレイヤーを 拡張することができます 37 / 95
LSP: リスコフの置換原則 Liskov Substitution Principle 派生クラスは、その基底クラスと置換可能であるべきであるという原則。 これにより、型の互換性や拡張性が保たれ、コードの再利用性や可読性が向上する 🚩 悪い兆候 事前条件の強化, 事後条件の弱化 38 / 95
LSP: リスコフの置換原則 Liskov Substitution Principle 派生クラスは、その基底クラスと置換可能であるべきであるという原則。 これにより、型の互換性や拡張性が保たれ、コードの再利用性や可読性が向上する 🚩 悪い兆候 事前条件の強化, 事後条件の弱化 クリーンアーキテクチャでは、インターフェースを用いて抽象化された内側の層が、具体的な実 装である外側の層に置き換えられるように設計されます 内側のレイヤーは外側のレイヤーの具体的な実装を知る必要がなくなります 38 / 95
ISP: インターフェース分離の原則 Interface Segregation Principle インターフェースは、分離できるものは分離するべきであるという原則 これにより、クライアントは不要なメソッドに依存しなくて済み、コードの可読性や保守性が向上する 🚩 悪い兆候 肥大化したインターフェース, 不完全なインターフェース 39 / 95
ISP: インターフェース分離の原則 Interface Segregation Principle インターフェースは、分離できるものは分離するべきであるという原則 これにより、クライアントは不要なメソッドに依存しなくて済み、コードの可読性や保守性が向上する 🚩 悪い兆候 肥大化したインターフェース, 不完全なインターフェース クリーンアーキテクチャでは、各層やコンポーネントが必要最低限のインターフェースを持つよ うに設計されます 内側のレイヤーが外側のレイヤーの具体的な実装を知る必要がなくなります 39 / 95
DIP: 依存性逆転の原則 Dependency Inversion Principle プログラムの重要な部分は、逆に重要でない部分にが依存しないようにするべきである。 そのためにモジュールは、抽象に依存するようにするべきであるという原則 これによりモジュールは、実装の詳細に左右されなくなり、コードの柔軟性や再利用性が向上する 🚩 悪い兆候 抽象が具象に依存する 40 / 95
DIP: 依存性逆転の原則 Dependency Inversion Principle プログラムの重要な部分は、逆に重要でない部分にが依存しないようにするべきである。 そのためにモジュールは、抽象に依存するようにするべきであるという原則 これによりモジュールは、実装の詳細に左右されなくなり、コードの柔軟性や再利用性が向上する 🚩 悪い兆候 抽象が具象に依存する クリーンアーキテクチャでは、依存性逆転の原則を適用しています 内側の層が外側の層に依存しないように設計されています 40 / 95
The Clean Architecture(≠Clean Architecture)の誤 解😕 41 / 95
親の顔より見た図 本日10ページぶり2回目の登場 「Clean Architecture」の22章の中で例示されているLayered Architecture 42 / 95
誤解されている内容 やたら独り歩きしている感 この通りに実装するのが正解 Clean Architectureの具体例として挙げられているだけ レイヤーは4層 レイヤーは4つ以外は認めないというルールはないと明記されている MVC1に近い構成にしないといけない そもそもWebアプリケーション以外 [1] も想定している図なので盲信すると危険! 最近のWebアプリケーションの構成と乖離が出てくる 他にも色々あるけれど。。 1. 同心円の外側のDevices, UI, External Interfacesがソレ それぞれ、キーボードやNativeアプリ(GUI)、CLI、メールやイベント・キューとかもありそう ↩︎ 43 / 95
本質的な2つルール 依存の方向は外側から内側のレイヤーにのみ向ける[1] 制御の流れと依存の向きは依存性逆転の原則で分離してコントロールする 44 / 95
各層(レイヤー間)の関係性 円の内側は高レイヤーで外側は低レイヤーとする レイヤー 抽象度 [1] 重要度[2] 安定度[3] 具体的な 位置 コンポーネント 役割 内側 高い (抽象) 高い 高い ビジネスルール ドメインモデル ソフトウェアの本質的部分や目的を表すもの 外側 低い (具象) 低い 低い データソース (データベース,Web API) ソフトウェアの技術的詳細や手段を表すもの 1. 抽象度とは、コンポーネントが具体的な実装や詳細から独立している程度を表す指標 ↩︎ 2. 重要度とは、システムが解決しようとしている課題や問題領域の関心事にどれだけ適合しているかを表す指標。 高い場合は修正の影響が大きい(テスト含めて) ↩︎ 3. 安定度とは、修正されにくい度合い又は、依存性関係が少なく他のモジュールの修正の影響を受けづらい度合い ↩︎ 45 / 95
抽象度と重要度と安定度は連動する 46 / 95
抽象度と重要度と安定度は連動する 。。のか? 46 / 95
🙅♀️ 47 / 95
🙅♀️ 違う、そうじゃない 47 / 95
🙆♀ 重要度に対して抽象度を調整し安定度をコントロ ールする 48 / 95
各層(レイヤー間)の 目指す関係性 円の内側は高レイヤーで外側は低レイヤーとする レイヤー 抽象度 [1] 重要度[2] 安定度[3] 具体的な 位置 コンポーネント 役割 内側 高い (抽象) 高い 高い ビジネスルール ドメインモデル ソフトウェアの本質的部分や目的を表すもの 外側 低い (具象) 低い 低い データソース (データベース,Web API) ソフトウェアの技術的詳細や手段を表すもの 1. 抽象度とは、コンポーネントが具体的な実装や詳細から独立している程度を表す指標 ↩︎ 2. 重要度とは、システムが解決しようとしている課題や問題領域の関心事にどれだけ適合しているかを表す指標。 高い場合は修正の影響が大きい(テスト含めて) ↩︎ 3. 安定度とは、修正されにくい度合い又は、依存性関係が少なく他のモジュールの修正の影響を受けづらい度合い ↩︎ 49 / 95
各層(レイヤー間)の 目指す関係性 重要度の高いものコアとし、中心に据える レイヤー 重要度[1] 抽象度 [2] 安定度[3] 具体的な 位置 コンポーネント 内側 高い 高い (抽象) 高い ビジネスルール ドメインモデル 役割 ソフトウェアの本質的部分や目的を表すもの 低い データソース 低い (具象) (データベース,Web API) ソフトウェアの技術的詳細や手段を表すもの 1. 重要度とは、システムが解決しようとしている課題や問題領域の関心事にどれだけ適合しているかを表す指標。 高い = 内部品質の高さが求められるコアとなるもの↩︎ 2. 抽象度とは、コンポーネントが他の具体的な実装や詳細から独立させた度合いの指標 ↩︎ 3. 安定度とは、修正されにくい度合い又は、依存性関係を少なくし、他のモジュールの修正に閉じている状態の度合い ↩︎ 外側 低い 50 / 95
SDP: 安定依存の原則 Stable Dependencies Principle パッケージ設計の原則の一つで、パッケージの依存は常により安定したパッケージに向くべきであるという原 則 安定したレイヤーに依存しないといけない 不安定なレイヤーに依存すると、その依存しているレイヤーが不安定になる 51 / 95
SDP: 安定依存の原則 Stable Dependencies Principle パッケージ設計の原則の一つで、パッケージの依存は常により安定したパッケージに向くべきであるという原 則 安定したレイヤーに依存しないといけない 不安定なレイヤーに依存すると、その依存しているレイヤーが不安定になる クリーンアーキテクチャでは、重要度が一番高いもの=ドメインモデルとしている ドメインモデルの設計を長期的に安定させるという考え方 51 / 95
🙃技術的な詳細で不安定なデー タソースにドメインが依存する のはおかしい! 抽象が具象に依存する 52 / 95
複雑で不安定なものから切り離す手法 制御の流れと依存の向きは連動してしまう 制御のフロー Application Domain Infrastructure 依存の向き Application Domain x 抽象が具象に依存している Infrastructure MEMO ※説明を単純にする為にレイヤーを3層にした例 PoEAA的に以下の関心事に対応 Application: Web Presentation Domain: Domain Logic Infrastructure: Data Source 53 / 95
複雑で不安定なものから切り離す手法 ドメインモデルの永続化するのに具象に依存させない 依存性逆転の原則 ドメイン層はインターフェースに依存させることで永続化技術の詳細から隔離させる Repositoryパターン RDBRepository Application Domain «interface» Infrastructure Repository RestRepository 54 / 95
✨これで本当にCleanといえるのか? 55 / 95
ドメインが持つ依存を排除し抽象度を上げる ドメインモデルをもっと安定させる アプリケーションサービス アプリケーションサービスは、ドメイン層とプレゼンテーション層の間に位置し、ドメインオブジェクトや レポジトリなどを利用して、アプリケーションの要求を満たす。 Application Layerを見直し Domain Controller UseCase «interface» Repository RDBRepository Infrastructure RestRepository 56 / 95
ドメインが持つ依存を排除し抽象度を上げる ドメインモデルをもっと安定させる アプリケーションサービス アプリケーションサービスは、ドメイン層とプレゼンテーション層の間に位置し、ドメインオブジェクトや レポジトリなどを利用して、アプリケーションの要求を満たす。 Application Layerを見直し Domain Controller UseCase «interface» Repository 完全に 独立 RDBRepository Infrastructure RestRepository 56 / 95
レイヤー間だけでなく クラス(コンポーネント)間にも SOLID・パッケージ設計の原則は適用可能 Clean Codeという考え方 57 / 95
ActiveRecordパターン の呪縛のまとめ 58 / 95
ActiveRecordパターンの呪縛 認知バイアス多め ドメインモデルとテーブルを1:1にしないといけない データ永続化とドメイン振る舞いを一体化させる思い込み テーブル設計を先にし、モデルに制約を後付けする それが一番早くて黄金パターンだという成功体験(シンプルなドメインのみという前提無視) レコード=ドメインオブジェクトでいつでも更新・削除は簡単 オブジェクトの属性を更新したり、saveやdeleteがいつでもカジュアルにできる(表面的な操作としては) 59 / 95
ああ・・・ クセになってんだ 60 / 95
学びほぐし事例集 61 / 95
アンラーニング、その前に… ここからの注意点 新しい用語が複数出てきます 詳細な説明はしません クリーンアーキテクチャでの設計に興味を持ってもらうことが目標です 気になった用語はワードだけ覚えて、後で自分で調べるスタイルでお願いします 🙇♀ アンチパターンとその対処法になります 以前の考え方や行動パターンと対比して新しいものに触れます 全てやらないとクリーンアーキテクチャにならないわけではありません[1] 設計パターンなので、思考の流れを理解するのが大事です クリーンアーキテクチャの経験者は一緒に内省しましょう😅 [2] 1. The Clean Architectureの誤解で触れた通り ↩︎ 2. 登壇者は紹介するアンチパターンの全てに心当たりがあります ↩︎ 62 / 95
テーブル定義 = Domain modelの プロパティ 表面的なデータ構造だけで設計してませんか? 63 / 95
学びほぐし事例 の変革の必要性 ActiveRecordは必ず1対1にしていたけれど テーブル設計 != モデル設計 論理設計と物理設計を同じと思っている[1] テーブル設計の変更に影響を受ける Valueオブジェクト[2]や集約[3]が表現されていない 振る舞いや制約がないか、分散してしまっている primitiveな型Onlyは要注意 テーブルの正規化対応できない 単純に関連モデルとして実装すると、データ構造が露出する 1. 日本語論理名を英語に翻訳するだけとか。。 ↩︎ 2. 同一性を持たず、属性が同じなら同じと扱うオブジェクト。例えば、日付や金額など ↩︎ 3. 一貫性を保つ必要があるドメインオブジェクトのまとまり。トランザクション境界にもなる ↩︎ 64 / 95
学びほぐし事例 のブレークスルー ドメインファーストな設計をしましょう。永続化はレイヤーを分けて次に考えましょう まずはドメインに向き合いましょう 1. ドメインの問題領域を理解する 2. ユビキタス言語を定義する 3. ドメインオブジェクトを識別する オブジェクトの種類を決めValueオブジェクトや集約を識別する。 振る舞いや関係(協調・制約)も洗い出す 65 / 95
モデルにsetterが 存在 プロパティが書き換え可能 66 / 95
学びほぐし事例 の変革の必要性 ActiveRecordはミュータブルなモデル 状態を変更した際に副作用が発生する可能性がある 制約をすり抜けてしまう コードの可読性が悪い ドメインイベントを見落とす 手続き的にsetterを呼び出しされても意図が伝わらない ビジネスルールがあっても認識されない[1] 1. 後からビジネスルールの追加があった場合などに対応漏れが発生する ↩︎ 67 / 95
学びほぐし事例 のブレークスルー オブジェクト作成後にその状態を変えることをできなくする(不変オブジェクトにする) イミュータブルなモデルにしましょう ドメインイベント毎に適切なメソッドを定義しましょう コンストラクタで制約を定義しましょう イミュータブルなモデルでコンストラクタに制約が定義されていれば、各種イベントで状態が変化しても制 約が必ず有効になります 68 / 95
コンストラクタ に制約がない 引数の型指定も制約になります 69 / 95
学びほぐし事例 の変革の必要性 ActiveRecordは制約違反があってもオブジェクト自体は存在できてしまう 制約違反したオブジェクトが存在できてしまう 制約をすり抜けて整合性や一貫性が失われる validメソッドが実装されていても制約違反を発見するタイミングが遅い validメソッドの呼び忘れも起こる ビジネスルールがわからなくなる 認知負荷が高くなる ドメインの制約を守る実装がそのドメインモデルを使う人任せになってしまう ビジネスルールが散らばってしまう MEMO ドメインモデルがgetter・setterしかない場合にドメイン貧血症と言われます 個人的にはコンストラクタの制約がないことが問題だと感じています 70 / 95
学びほぐし事例 のブレークスルー イミュータブルな設計とセットでコンストラクタに制約を実装する 適切にコンストラクタに制約を定義しましょう 制約違反したオブジェクトが存在しないことが保証される また、制約違反を発見するタイミングが早くなる イミュータブルなモデルにしましょう 71 / 95
読み込みと書き 込みのモデルが同じ データの永続化と読み込みの両方に同じモデルを使用する 72 / 95
学びほぐし事例 の変革の必要性 ActiveRecordではテーブルとの関連は原則1:1。分けれなくもないけれど普通はやらない 正規化したデータをうまく扱えない 書き込み時はコードのみ、名称は不要 逆に読み込み時には名称も一緒にロードして表示したい データ更新時に不要なデータまで必要になる モデルが複雑で関連するデータが多い場合に準備が大変 一括更新になりがちでデータの不整合が発生する 不要なカラムまで更新して、同時更新されると不整合となる 73 / 95
学びほぐし事例 のブレークスルー ReadオブジェクトとWriteオブジェクトを分ける データの更新(コマンド)と参照(クエリ)を別のモデルに分ける[1] コマンド・クエリ責務分離(Command-Query Responsibility Segregation, CQRS)の原則に従う。 コマンドは正規化してデータの一貫性と整合性を保つ クエリは非正規化してデータ参照時のパフォーマンスを上げる 1. モデルだけでなくデータソースも別にし、スケーラビリティやパフォーマンスを向上させる場合もありま す。 ↩︎ 74 / 95
ユースケース毎 に適切なコマンドが ない データの新規登録と更新の両対応のメソッドが定義されている 75 / 95
学びほぐし事例 の変革の必要性 ActiveRecordでCRUDが簡単にでき、最新状態を常に上書きしていたけれど ドメインのイベントを見落としている[1] 新規登録時と更新時の制約が違うケースがある 1. モデルにnull許可のプロパティが多い場合は情報が混ざっている可能性がある ↩︎ 76 / 95
学びほぐし事例 のブレークスルー 事実を失う操作(更新・削除)を行わせない、データの構造や関係とする イミュータブルデータモデルで設計してみる 更新と削除を行わないテーブル設計[1] エンティティをイベントとリソースに分けてモデリングする コマンドのオブジェクトを分けるようにする 新規登録用のオブジェクトと更新用のオブジェクト(イベント対象と内容のみ)で分ける MEMO イミュータブルデータモデルは、イベントソーシングパターンとも相性が良いです。 イベントの量が多くなった際に分散処理しやすくなります。 1. T字形ER データベース設計技法と相性が良いです ↩︎ 77 / 95
ユースケース毎 に適切な粒度のクエ リがない 集約がうまく扱えていない 78 / 95
学びほぐし事例 の変革の必要性 ActiveRecordはデフォルトでLazy Loadが前提になっているけれど データの関連を適切にクエリとして表現する必要がある 集約をクエリとして表現しないと、クエリ間 [1] でもN+1が起こる 集約データをRepositoryから取得する際の戻り値がEager Loadになる Lazy Loadでエンティティを返すにはテクニックが必要 1. 個々のクエリを再利用してしまい、APIでもN+1で呼び出される危険性がある ↩︎ 79 / 95
学びほぐし事例 のブレークスルー パフォーマンスに考慮したクエリを定義する 集約を扱うクエリを適切に定義する 集約ルートに対して一覧表示や詳細表示などのシーンに応じてクエリを最適化する 集計機能実装時にデータベースの力を借りる 大量データをメモリロードせずに、集計関数を利用する 80 / 95
Hello world!! Clean Architecture🎉 学習おつかれさまでした 81 / 95
Good by ActiveRecord?🤔 クリーンアーキテクチャとActiveRecordって相性悪くない? 82 / 95
ソフトウェア設計における戦術と戦略の違い 戦略には中長期的な視点が必要 戦術(Tactics) 個々のモジュールやクラスなどの小さなスコープで適用できる設計テクニック(Design Pattern) インターフェースと実装の分離や情報隠蔽など → SOLID原則も含まれます 戦略(Strategy) システム全体や大きなスコープで適用できる設計テクニック(Architectural Pattern) モジュール間の依存関係や抽象化のレベルなど → クリーンアーキテクチャ 83 / 95
ActiveRecordは戦術?それとも戦略? 戦術を組み合わせることで戦略に昇華することができる PoEAA的にはData Source Architectural Patterns データソースを中心にした設計で戦略的 ActiveRecordパターンは以下の設計思想・戦術を組み合わせるとより戦略的になる 1. CoC (設定より規約) 2. DRY (繰り返しを避ける) 3. REST (RDBMSのCRUDとHTTPのリクエストメソッドに関連させる) [1] 1. ActiveRecordのモデル名とエンドポイントのURIも関連させる ↩︎ 84 / 95
戦略の相違 🏃♂️レイヤーを分けずにDRYに書く戦略 ActiveRecord 🌱レイヤーに分けて依存関係を分離し、品質を高める クリーンアーキテクチャ データソースやビジネスロジックの分離は考えない 戦略 ドメインからデータソースを意識させなくする 85 / 95
【結論】 戦略として相性悪い 目指している方向性が違い、戦術的にもバッティングしてしまっている 戦略なので設計時の思考にも反映されがちで矛盾が生じる (アンチパターンの要因) 86 / 95
なので。。 87 / 95
今後のActiveRecord系ライブラリとの向き合い方 ActiveRecordは戦術的に使うだけに留める Infrastructure層のRepositoryでActiveRecordを使う [1] ORマッパー・クエリービルダとして使う ただしドメインオブジェクトとActiveRecordオブジェクトへのマッピングが必要 ドメインオブエジェクトではなくDTO(Data Transfer Object), DPO(Data Persistence Object)の場合も あり [2] 1. Data Mapperでマッピングするほうが良いが、関心事が混ざる問題がある ↩︎ 2. DPOはRepositoryのインターフェース側に定義させる ↩︎ 88 / 95
フレームワークとの関わり方にも通ずる 戦術なら変更は可能。戦略変更は計画的に 一番外側に配置して、ユースケース・ドメインからは依存させないようにする 戦略的ではなく、戦術として使う(薄く利用する) 戦術は局所的な影響に留めることができ、比較的容易に変更できることが多い 89 / 95
まとめ 90 / 95
設計パターンの適用条件 再利用可能なケース ActiveRecordパターンの場合 (DRY / 開発効率スピード重視 / 作るものがシンプルで明確) データベースのテーブルとオブジェクトの構造がほぼ同じ データベースの操作が単純 ビジネスロジックが複雑でない クリーンアーキテクチャの場合 (SOLID / 変更容易性・拡張性重視 / 作るものが複雑で不明確、又は変更多い) ビジネスロジックやドメイン知識が重要 外部リソースやフレームワークに依存しないようにしたい 変更や追加に柔軟に対応したい 91 / 95
ActiveRecordの呪縛から逃れるには? ActiveReocrdは責務が多いので分解する SOLIDでコアなドメインになるようにモデリングする ドメインファーストな設計をする 92 / 95
よりクリーンなコードにしていく 複雑なものは下位層に隠す データソースだけでなく他のものも モデルの表現を豊かにする Valueオブジェクト・集約・ドメインイベントを識別して、振る舞いや関係を定義する 構造や振る舞いがわかりやすくする イミュータブルなモデル・データ構造にして一貫性を保つ CQRSの原則に従いスケーラビリティやパフォーマンスを高める 将来的な分散システム(マイクロサービス等)への移行も視野に入れておく 切り出しやすくして変化に強くする → 複雑さが下位層に隠れている状態 93 / 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っぽいやつ 現場で役立つシステム設計の原則 〜変更を楽で安全にするオブジェクト指向の実践技法 94 / 95
ご清聴ありがとうございました 95 / 95