Slide 1

Slide 1 text

クリーンアーキテクチャか ら見る依存の向きの大切さ 2024/02/22 ペチコン名古屋2025 by しまぶ@shimabox 


Slide 2

Slide 2 text

自己紹介 { "NAME": "しまぶ", "SNS": "@shimabox", "TAMASHII": "沖縄", "COMPANY": "カオナビ", "SKILL": [ "PHP", "Go" ] } whoami.json 2

Slide 3

Slide 3 text

自己紹介 📖 共著で本を書きました 📖 3

Slide 4

Slide 4 text

1. クリーンアーキテクチャと依存の向き 2. 依存性逆転の原則(DIP) 3. クリーンアーキテクチャが伝えたいこと アジェンダ 4

Slide 5

Slide 5 text

1. クリーンアーキテクチャと依存の向き 2. 依存性逆転の原則(DIP) 3. クリーンアーキテクチャが伝えたいこと 1. クリーンアーキテクチャと依存の向き 5

Slide 6

Slide 6 text

1. クリーンアーキテクチャと依存の向き ● エンティティ ○ ドメインモデル ○ 最上位、一番内側 と言われる ● ユースケース ● インターフェイスアダプター ● フレームワークとドライバー ○ インフラストラクチャー ○ 最下位、一番外側 と言われる クリーンアーキテクチャと言えば? 6

Slide 7

Slide 7 text

レイヤー 説明 役割 エンティティ 企業のビジネスルール(方針)を表す。 業務そのもの。最重要。 ● 方針 ● ビジネスロジック ユースケース アプリケーション固有のビジネスルー ル。エンティティを組み合わせてやりた いことを達成する。 ● 処理フローを管理 インターフェイス アダプター 外部と内部の仲介役。外部の要求を内部 に伝え、内部の結果を外部に返す。 ● データのやり取りを橋 渡しする フレームワークと ドライバー フレームワーク, UI, DB, API, テスト, 外部ライブラリなど。技術詳細。 ● 外部と連携 ● 詳細(些細なもの) 1. クリーンアーキテクチャと依存の向き クリーンアーキテクチャと言えば? 7

Slide 8

Slide 8 text

1. クリーンアーキテクチャと依存の向き ● エンティティ ○ ドメインモデル のほうが馴染みある ○ 最上位、一番内側 と言われる ● ユースケース ● インターフェイスアダプター ● フレームワークとドライバー ○ インフラストラクチャー のほうが馴 染みある ○ 最下位、一番外側 と言われる 各レイヤーの話をしましたが... 8

Slide 9

Slide 9 text

1. クリーンアーキテクチャと依存の向き ● エンティティ ○ ドメインモデル のほうが馴染みある ○ 最上位、一番内側 と言われる ● ユースケース ● インターフェイスアダプター ● フレームワークとドライバー ○ インフラストラクチャー のほうが馴 染みある ○ 最下位、一番外側 と言われる 各レイヤーの話をしましたが... 9 “Only Four Circles?” “No, the circles are schematic. You may find that you need more than just these four. There’s no rule that says you must always have just these four. However, The Dependency Rule always applies. ” 「円は4つ?」 「いや、円はあくまで概念だよ。実際には4つ以上必要な場 合もあるし、4つだけじゃなきゃいけないって決まりはな い。ただし、依存性のルールだけはいつも守らなきゃね。」 https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html より意訳

Slide 10

Slide 10 text

依存性のルールとは? ● ソースコードの依存性は常に内側(上位レベ ルの方針)にむける ● クリーンアーキテクチャが伝えているのは これ 1. クリーンアーキテクチャと依存の向き 10

Slide 11

Slide 11 text

1. クリーンアーキテクチャと依存の向き ● エンティティ ○ ちょうぜつだと、ドメインモデル ○ 最上位、一番内側 と言われる ● ユースケース ● インターフェイスアダプター ● フレームワークとドライバー ○ ちょうぜつだと、インフラストラク チャー ○ 最下位、一番外側 と言われる 依存の向きとは? これが依存の向き (依存性) 11

Slide 12

Slide 12 text

依存の向きが与える影響、関係性 状況 安定度 影響範囲 変更のしやすさ 依存している クラス 低い 依存先の影響を受ける 変更しやすい (変わる) 依存されている クラス 高い 依存元の影響を受けない 変更しにくい (変りにくい) 依存されている クラス (依存先) 1. クリーンアーキテクチャと依存の向き 依存している クラス (依存元) 12

Slide 13

Slide 13 text

安定度が低いとは ● Fooは安定度が低い Baz Bar Foo ・・・ 1. クリーンアーキテクチャと依存の向き 13

Slide 14

Slide 14 text

● Fooは安定度が低い ● Barなどに変更が入るとFooに影響 が出る ● FooはBarのことを知っている ● FooはBarに依存する Baz Bar Foo ・・・ ❌ 変更 ❌ 影響 安定度が低いとは 1. クリーンアーキテクチャと依存の向き 14

Slide 15

Slide 15 text

● Fooは安定度が高い Baz Bar Foo ・・・ 安定度が高いとは 1. クリーンアーキテクチャと依存の向き 15

Slide 16

Slide 16 text

● Fooは安定度が高い ● Barなどに変更が入ってもFooには 関係ない ● FooはBarのことを知らない Baz Bar Foo ・・・ ❌ 変更 安定度が高いとは 1. クリーンアーキテクチャと依存の向き 16

Slide 17

Slide 17 text

● 依存されているものが多いほど安 定度は高いが... Baz Bar Foo ・・・ 安定度が高いとは 1. クリーンアーキテクチャと依存の向き 17

Slide 18

Slide 18 text

● 依存されているものが多いほど安 定度は高いが... ○ Fooに変更が入ったら依存されている ものに影響が出る ○ 安定していることが求められている ○ なるべく変更されにくいものとしたい Baz Bar Foo ・・・ ❌ 影響 ❌ 変更 ❌ 影響 ❌ 影響 依存の向きから見る影響 1. クリーンアーキテクチャと依存の向き 18

Slide 19

Slide 19 text

● 依存しているほうの変更は影響を 与えない ○ 影響を与えないということは変更しや すい Baz Bar Foo ・・・ ❌ 変更 依存の向きから見る影響 1. クリーンアーキテクチャと依存の向き 19

Slide 20

Slide 20 text

ここまでをいったん整理 ● 依存している ○ 依存先の影響を受ける ■ 安定度が低い ○ 変更しても依存先に影響を与えない ■ 変更しやすい ● 依存されている ○ 依存元の影響を受けない ■ 安定度が高い ○ 変更すると依存元に影響を与える ■ なるべく変更したくない Bar Foo 依存している (依存元) 依存されている (依存先) 1. クリーンアーキテクチャと依存の向き 20

Slide 21

Slide 21 text

ここまでをいったん整理 ● 依存している (技術詳細) ○ 依存先の影響を受ける ■ 安定度が低い ○ 変更しても依存先に影響を与えない ■ 変更しやすい ○ 変わる ● 依存されている (ビジネスルール) ○ 依存元の影響を受けない ■ 安定度が高い ○ 変更すると依存元に影響を与える ■ なるべく変更したくない ○ 変わりにくい 変わりにくい (ビジネスルール) 変わる (技術詳細) 依存している (依存元) 依存されている (依存先) 1. クリーンアーキテクチャと依存の向き 21

Slide 22

Slide 22 text

1. クリーンアーキテクチャと依存の向き ここまでをいったん整理 変わる (技術詳細) 変わりにくい (ビジネスルール) 22

Slide 23

Slide 23 text

依存性のルールとは? ● ソースコードの依存性は常に内側(上位レベ ルの方針)にむける ● クリーンアーキテクチャが伝えているのは これ 1. クリーンアーキテクチャと依存の向き ● 技術詳細の変更でビジネスルールに 影響を与えたくない(安定性の向上) 23

Slide 24

Slide 24 text

依存性のルールとは? ● ソースコードの依存性は常に内側(上位レベ ルの方針)にむける ● クリーンアーキテクチャが伝えているのは これ 1. クリーンアーキテクチャと依存の向き ● 技術詳細の変更でビジネスルールに 影響を与えたくない(安定性の向上) これでなぜ安定性につながるのか 24

Slide 25

Slide 25 text

1. クリーンアーキテクチャと依存の向き 2. 依存性逆転の原則(DIP) 3. クリーンアーキテクチャが伝えたいこと 2. 依存性逆転の原則(DIP) 25

Slide 26

Slide 26 text

● 依存していた外部ライブラリに後方互換性のない、 アップデートやセキュリティパッチが入った ● 調べてみると至るところで使われていた ○ 影響範囲が不明で対応できず放置されがち お客様の中でこんな経験がある方はいらっしゃいませんか〜? 変わりにくい (ビジネスルール) 変わる (技術詳細) 2. 依存性逆転の原則(DIP) 26

Slide 27

Slide 27 text

2. 依存性逆転の原則(DIP) 簡単な注文システムから見てみる ● この注文システムはベン ダーのSDKを使って決済を 行っている ● ユースケースから直接利用 している ○ こんなのが何故かたくさんある 27 by smeghead/php-class-diagram

Slide 28

Slide 28 text

決済処理を行うSDK 2. 依存性逆転の原則(DIP)

Slide 29

Slide 29 text

2. 依存性逆転の原則(DIP) paymentSdk->charge($amount, $token); if ($success) { echo "注文処理に成功!\n"; } else { echo "注文処理に失敗!\n"; } } } ユースケースが直接SDKに依存 29

Slide 30

Slide 30 text

2. 依存性逆転の原則(DIP) placeOrder(1000, 'TOKEN'); // 注文処理に成功! 利用例 30

Slide 31

Slide 31 text

2. 依存性逆転の原則(DIP) 依存の向きを再確認 ● ビジネスルールが技術詳細 に依存している ● SDKに後方互換性のない修 正が入ったら? 31

Slide 32

Slide 32 text

決済処理を行うSDKのバージョン2 2. 依存性逆転の原則(DIP) true, 'transaction_id' => 'txn_123456'] */ class PaymentSDK { public function pay(float $amount, string $token): array { // 決済処理(実装省略) // 配列が返るように変更された return [ 'success' => true, 'transaction_id' => 'txn_' . uniqid(), ]; } } 破壊的変更💣💥 ● メソッド名の変更 ● 戻り値の変更 32

Slide 33

Slide 33 text

2. 依存性逆転の原則(DIP) paymentSdk->pay($amount, $token); if ($result['success'] ?? false) { echo "注文処理に成功!\n"; } else { echo "注文処理に失敗!\n"; } } } ユースケースを変更することになる 33

Slide 34

Slide 34 text

2. 依存性逆転の原則(DIP) paymentSdk->pay($amount, $token); if ($result['success'] ?? false) { echo "注文処理に成功!\n"; } else { echo "注文処理に失敗!\n"; } } } ユースケースを変更することになる 34 技術詳細の変更で ビジネスルールに影響を 与えている

Slide 35

Slide 35 text

2. 依存性逆転の原則(DIP) paymentSdk->pay($amount, $token); if ($result['success'] ?? false) { echo "注文処理に成功!\n"; } else { echo "注文処理に失敗!\n"; } } } ユースケースを変更することになる 35 ● 要は「注文したら結 果が返ってくればい い」だけ ● 振り回されたくない

Slide 36

Slide 36 text

2. 依存性逆転の原則(DIP) paymentSdk->pay($amount, $token); if ($result['success'] ?? false) { echo "注文処理に成功!\n"; } else { echo "注文処理に失敗!\n"; } } } ユースケースを変更することになる 36 そこで 依存性逆転の原則 (DIP)

Slide 37

Slide 37 text

2. 依存性逆転の原則(DIP) どうするか 37  

Slide 38

Slide 38 text

2. 依存性逆転の原則(DIP) こうする 38  

Slide 39

Slide 39 text

ドメイン層に抽象と処理結果格納用クラスを用意 2. 依存性逆転の原則(DIP) success; } } 39

Slide 40

Slide 40 text

2. 依存性逆転の原則(DIP) paymentSdk->pay($amount, $token); // 決済処理結果を生成して返す return new Result((bool)($result['success'] ?? false)); } } 抽象の実装をインフラストラクチャに用意 40

Slide 41

Slide 41 text

2. 依存性逆転の原則(DIP) paymentSdk->pay($amount, $token); // 決済処理結果を生成して返す return new Result((bool)($result['success'] ?? false)); } } 抽象の実装をインフラストラクチャに用意 41 ● 外部ライブラリの処 理を吸収 ● ドメインの型に変更 して返却

Slide 42

Slide 42 text

2. 依存性逆転の原則(DIP) payment->processPayment($amount, $token); if ($result->isSuccess()) { echo "注文処理に成功!\n"; } else { echo "注文処理に失敗!\n"; } } } ユースケースは抽象に依存する 抽象に依存 42

Slide 43

Slide 43 text

2. 依存性逆転の原則(DIP) payment->processPayment($amount, $token); if ($result->isSuccess()) { echo "注文処理に成功!\n"; } else { echo "注文処理に失敗!\n"; } } } ユースケースは抽象に依存する ● 「注文したら結果が 返ってくればいい」 ● 裏で何が行われてい るかには無関心 43

Slide 44

Slide 44 text

2. 依存性逆転の原則(DIP) placeOrder(1000, 'TOKEN'); // 注文処理に成功! 利用例 44

Slide 45

Slide 45 text

2. 依存性逆転の原則(DIP) placeOrder(1000, 'TOKEN'); // 注文処理に成功! 利用例 依存を生成して注入して いる(DI) 45

Slide 46

Slide 46 text

2. 依存性逆転の原則(DIP) 依存の向きを再確認 46 Infrastructure Domain UseCase 抽象 ❌

Slide 47

Slide 47 text

2. 依存性逆転の原則(DIP) A: 依存性逆転の原則(DIP) に従うと、技術詳細の影響 をビジネスルールに与えな いようにできるから Q: 内側に依存すると、なぜ安定性につながるのか 変わる (技術詳細) 変わりにくい (ビジネスルール) 47

Slide 48

Slide 48 text

● エンティティ ● ユースケース ● インターフェイスアダプター ● フレームワークとドライバー 赤ペン先生  で見たことある! 2. 依存性逆転の原則(DIP) 48

Slide 49

Slide 49 text

● エンティティ ● ユースケース ● インターフェイスアダプター ● フレームワークとドライバー 赤ペン先生  で見たことある! 2. 依存性逆転の原則(DIP) 49

Slide 50

Slide 50 text

1. クリーンアーキテクチャと依存の向き 2. 依存性逆転の原則(DIP) 3. クリーンアーキテクチャが伝えたいこと 3. クリーンアーキテクチャが伝えたいこと 50

Slide 51

Slide 51 text

依存の制御 3. クリーンアーキテクチャが伝えたいこと 変わる (技術詳細) 変わりにくい (ビジネスルール) ❌ 51

Slide 52

Slide 52 text

依存の制御 3. クリーンアーキテクチャが伝えたいこと 変わる (技術詳細) 変わりにくい (ビジネスルール) 抽象 52

Slide 53

Slide 53 text

依存の制御 3. クリーンアーキテクチャが伝えたいこと 変わる (技術詳細) 変わりにくい (ビジネスルール) 抽象 無関心 53

Slide 54

Slide 54 text

payment->processPayment($amount, $token); if ($result->isSuccess()) { echo "注文処理に成功!\n"; } else { echo "注文処理に失敗!\n"; } } } 再掲: 抽象に依存する ● 「注文したら結果が 返ってくればいい」 ● 裏で何が行われてい るかには無関心 3. クリーンアーキテクチャが伝えたいこと 54

Slide 55

Slide 55 text

つまり、これは 3. クリーンアーキテクチャが伝えたいこと 55

Slide 56

Slide 56 text

極論、こう(通称: エンジニアドーナツ、諸説あり) 3. クリーンアーキテクチャが伝えたいこと 56

Slide 57

Slide 57 text

エンジニアドーナツがあらわすもの 3. クリーンアーキテクチャが伝えたいこと ● 変わるものと変わりにくいもの に分ける ● 変わるものに依存しない ● 抽象を使って会話する ○ 抽象は契約 57

Slide 58

Slide 58 text

この考えはビジネスロジックにもあてはまる 3. クリーンアーキテクチャが伝えたいこと // CurrencyConverterクラス: 通貨換算処理 readonly class CurrencyConverter { public function convert(string $currency, float $amount): float { if ($currency === 'USD') { return $amount * 1.1; } elseif ($currency === 'EUR') { return $amount * 0.9; } // 新しい通貨が追加されるたびに条件が増える return $amount; } } CurrencyConverter 依存A (USD) 依存B (EUR) 依存C (?) ● 対応通貨が増えるたびに Converterが修正されていく... ● USDの修正なのに、EURに影響が でるかもしれない... ● Converterを触るのが怖い... 58 CurrencyConverter

Slide 59

Slide 59 text

変わるものに依存しない、抽象に依存する 3. クリーンアーキテクチャが伝えたいこと CurrencyConverterInterface UsdConverter CurrencyConverter ● Usdの修正はUsdConverterの修正だけで済む → 責務を分ける ● 抽象(契約)を守っている限り、影響は伝播しない 59 EurConverter ?Converter

Slide 60

Slide 60 text

3. クリーンアーキテクチャが伝えたいこと // CurrencyConverterInterface: 通貨換算のためのイン ターフェース interface CurrencyConverterInterface { public function convert(float $amount): float; } // USD換算クラス: CurrencyConverterInterfaceを実装 class UsdConverter implements CurrencyConverterInterface { public function convert(float $amount): float { return $amount * 1.1; } } // EUR換算クラス: CurrencyConverterInterfaceを実装 class EurConverter implements CurrencyConverterInterface { public function convert(float $amount): float { return $amount * 0.9; } } readonly class CurrencyConverter { public function __construct(private CurrencyConverterInterface $converter) {} public function convert(float $amount): float { // 各通貨換算クラスが自身のconvertメソッドを // 実装しているため、条件分岐が不要 return $this->converter->convert($amount); } } // 使用例 $usdConverter = new UsdConverter(); $processor = new CurrencyConverter($usdConverter); echo $processor->convert(100); // 110 抽象に依存 60 変わるものに依存しない、抽象に依存する

Slide 61

Slide 61 text

Q: 依存の制御が柔軟性と保守性にどうつながるのか 3. クリーンアーキテクチャが伝えたいこと A: ● 柔軟性 ○ ビジネスルールを守りつつ、技術の変 化に対応しやすくなる ○ 新たな要求に対して柔軟に対応できる ようになる ● 保守性 ○ 技術的な変更の影響が局所化され問題 の特定がしやすくなる 61

Slide 62

Slide 62 text

ただし、ドーナツは適切に 3. クリーンアーキテクチャが伝えたいこと A: ● 柔軟性 ○ ビジネスルールを守りつつ、技術の変 化に対応しやすくなる ○ 新たな要求に対して柔軟に対応できる ようになる ● 保守性 ○ 技術的な変更の影響が局所化され問題 の特定がしやすくなる 62

Slide 63

Slide 63 text

まとめ 3. クリーンアーキテクチャが伝えたいこと ● 依存性のルールが、システムの安定性にどう貢献するのか   ✅ 技術的な変更がビジネスルールに影響を与えないようにできる ● 依存の制御がシステムの柔軟性と保守性にどうつながるのか    ✅ 柔軟性 ■ ビジネスルールを守りながら技術の変化に対応しやすくなる ✅ 保守性 ■ 技術的な変更の影響を局所化し、問題の特定がしやすくなる 63

Slide 64

Slide 64 text

まとめ 3. クリーンアーキテクチャが伝えたいこと ● クリーンアーキテクチャの本質とは ○ 構造や配置ではなく、オブジェクトの依存関係を制御 することで得られる柔軟性や保守性を高める考え方 ○ "依存の向きを管理すること" 64

Slide 65

Slide 65 text

● Clean Architecture 達人に学ぶソフトウェアの構造と設計 ○ https://www.amazon.co.jp/dp/B07FSBHS2V ● ちょうぜつソフトウェア設計入門 ○ https://www.amazon.co.jp/dp/B0BNH1J2W2 ● アーキテクトの教科書 価値を生むソフトウェアのアーキテクチャ構築 ○ https://www.amazon.co.jp/dp/4798184772 ● Software Design 2023年6月号 ○ クリーンアーキテクチャとは何か? ○ https://gihyo.jp/magazine/SD/archive/2023/202306 ● smeghead/php-class-diagram ○ https://github.com/smeghead/php-class-diagram 参考資料 65

Slide 66

Slide 66 text

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