はじめに
本記事では、ドメイン駆動設計とソフトウェアアーキテクチャ(以降、アーキテクチャと呼ぶ)の関係について、初心者向けに解説したものです。
対象読者層
- アーキテクチャがよくわからない方
- ドメイン駆動設計とアーキテクチャの関係を知りたい方
結論
- アーキテクチャは、ソフトウェアの設計をより理解しやすくするための概念
- どのアーキテクチャも、ビジネスロジックを独立させることを目的としている
- ドメイン駆動設計における、ドメインオブジェクトを守ることを体現している
理解しやすいソフトウェア設計をするための推奨ガイドライン
アーキテクチャが達成しようとしている目的は、この一言に尽きます。
本記事では一部のアーキテクチャのみ解説しますが、全てのアーキテクチャはソフトウェア設計を理解しやすい構造にすることを目的としています。
では、アーキテクチャの方針に従わないとどうなるか見てみましょう。
アンチパターンその1 神クラス
悪い例として、すべての処理が1つのクラスに書かれた肥大クラス(通称:神クラス)を見てみましょう。
これは、ログイン画面を表現する「ログイン画面クラス」に書かれているロジックを可視化したものです。
これは、一番ありがちな理解しづらいソフトウェア設計です。
つのクラスにログインに関する全ての処理を書いたことにより、以下のような弊害が生まれます
- ソースコードの肥大化
- ソースコードが肥大化することで、コードの可読性が低下
-
ソースコードの修正が困難
(コードに変更を加える際、変更箇所とは関係ないロジックに影響を与える可能性がある)
アンチパターンその2 楽観的な未来予知
このロジックは今後も増えることはないだろうと考えてロジックの分離を行うと、将来的に失敗する可能性が高いです。
さきほどのログイン画面を例に、楽観的な未来予知の結果を見てみましょう
開発当初のログインはメールアドレス/パスワードを用いたログインしかありませんでした。
そのため開発者は、今後、ログイン処理はこれ以上増えることはないだろうと考えて、ログイン画面クラスに全てのログイン処理を書きました。
しかし、翌年にICカードを使ってログインを簡略化したいとの要望を受け、ICカードログイン画面を実装することになりました。
ICカードに記録されたユーザーIDを使ってログインをするだけで、
これまでのログイン処理と大きな違いはないと仮定します
従来のログイン処理と大きな違いがなければ、ある程度ロジックの流用が可能です。
しかし、ログイン処理はログイン画面クラス(メールアドレス/パスワード)に書かれており、直接呼び出せません。
その結果、開発者は最初のログイン処理と似て非なる別のロジックを複製しました
当時の開発者は、いつか直せばいいだろうと考えたかもしれません。
しかし、開発において、その『いつか』はほとんどの場合訪れません。
ソフトウェアの未来を予知することは非常に困難です。
このコードに変更が入ることはないだろう、似たようなロジックが増えることはないだろう と思ってもそれは起こり得るのです。
ドメイン駆動設計がアーキテクチャに求めるもの
アーキテクチャを理解すると、開発者は何をどこに書くか悩む機会を減らすことができます。
では、ドメイン駆動設計におけるアーキテクチャの恩恵は何でしょうか?
それは、ビジネスロジックを独立させることです
ビジネスロジックが含まれたドメインオブジェクトを他オブジェクトから守ることは、ドメイン駆動設計において重要です。
これが達成できるなら、どのアーキテクチャを採用しても問題ありません。
ここからは代表的なアーキテクチャを紹介しつつ、各アーキテクチャがどのようにしてビジネスロジックの独立を果たしているのか、見ていきましょう。
代表的なアーキテクチャ
レイヤードアーキテクチャ
ソフトウェアを複数の層(レイヤー)に分けて設計する手法です。
具体的には、以下のレイヤーが定義されています。
-
ユーザーインターフェース層
画面や表示等のユーザーインターフェースを担当するクラスが該当
-
アプリケーション層
ユースケースに関する処理を担当するクラスが該当する
ビジネスロジックは書かず、各クラスに処理を依頼して特定のユースケースを実現するのが責務
ドメイン駆動設計なら、アプリケーションサービスが該当する
-
ドメイン層
業務ルールや重要なビジネスロジックを担当するクラスが該当する
-
インフラストラクチャ層
データベースや外部API等の技術基盤を用いた連携を担当するクラスが該当する
各レイヤーには矢印が伸びており、上位レイヤーから下位レイヤーに依存の方向が固定されているのが特徴です。
詳しい解説は、以下の記事がとてもわかり易いです。
クラスの役割を明確にすることで、ビジネスロジックを独立させる
レイヤードアーキテクチャで一番重要なのは、各クラスの役割を簡潔にすることです。
さきほどのサンプルで紹介したログイン画面クラスを見てみましょう。
ログイン画面クラスは、本来ユーザーインターフェース層に分類されるべきクラスです。
しかし、アプリケーション層やインフラストラクチャ層に分類させる処理も書かれているため、
各クラスの役割(関心ごと)がバラバラかつ複雑な構成になっています。
レイヤードアーキテクチャに従ってクラスを分けると、以下のような構成にできます。
- ログイン画面クラス
- ログイン画面の表示に関する処理のみ記載する
- ログインユースケースクラス
- ログイン処理の制御処理のみ記載する
- サーバーとの認証クラス
- 実際にサーバと通信する処理のみ記載する
こうすることで、各クラスのコード量を減らしつつクラスの役割を明確にすることができます。
ログインに関する重要なルールがある場合は、ドメイン層に該当するクラスも作成します。
例:メールアドレス/パスワードの文字数チェック処理
これを意識して設計すると、ビジネスロジックを扱うクラスには、関係ないロジックが含まれなくなるはずです。
各クラスの役割を明確にすることは、ビジネスロジックを独立させることに繋がるのです。
ヘキサゴンアーキテクチャ
六角形をモチーフにしたアーキテクチャです。
ソフトウェアの核となる部分はそのままに、インターフェースや保存媒体は取り外し可能にすることを推奨しています。
ゲーム機で例えるとわかりやすい
ソフトウェアの核となる部分はそのままに、インターフェースや保存媒体は取り外し可能にする
これだけ聞くとイメージしづらいですが、
Javaでクリーンアーキテクチャする方法 Part.1:ヘキサゴナルアーキテクチャでゲーム機を例にされているのがとてもわかりやすいです。
例え話しましょう。テレビゲームを思い浮かべてください。テレビゲームってあるじゃないですか。ゲーム機があって、あとコントローラとディスプレイがあります。
このコントローラとかディスプレイって、もちろん純正品がありますけど、それ以外にもサードパーティのものとかもありますよね。いろいろなメーカーがあります。これも交換できる。コントローラもディスプレイもサードパーティと変えられます。
もう1個、データを保存する場所です。もちろんゲーム機の中のディスクドライブとかメモリカードとかありますよね。ほかにも最近はクラウドに保存できたりしますよね。これがまさにヘキサゴナルアーキテクチャと同じアイデアです。
ソシャゲを例にしましょう。
昨今のソシャゲは、スマートフォン以外の端末でもプレイできるように設計されているケースが多いです。
- スマホ
- PC キーボード・マウス
- PS5
使用するデバイスによって操作方法は異なりますが、どのデバイスでもそのゲーム本来の楽しさを味わえるようになっています
ソフトウェア設計にもこの考え方を応用し、ソフトウェアのコアとなる部分と繋がるモジュールは抽象化し、モジュールの取り換えを可能とするのがヘキサゴンアーキテクチャの考え方です。
実はリポジトリーパターンですでに学習済み
モジュールの取り換えを可能にする方法の代表例として、インターフェースを使う事が挙げられます。
実は、この手法はリポジトリーパターンですでに取り扱っています。
リポジトリーパターンでは、インターフェースを使うことでビジネスロジックとデータ操作ロジックを分離していました。
ログイン処理を例にしましょう。
以下の図では、アプリケーションのコア部分から行うログイン処理にインターフェースを挟んでいます。
インターフェースの先では、2つの方法を用いたログイン処理が定義されています
- DBへアクセスしてログインを行う
- 外部APIを用いてログインを行う
この2つは、処理の詳細は違えど同じログインを行っています
そのため、インターフェースを挟むことで2つのログイン処理を取り替え可能としています。
モジュールを取り替え可能にすることで、ドメインオブジェクトの独立性が高まる
抽象度が高いインターフェースへ依存することで、ソフトウェアを変更しやすくすることができます。
ヘキサゴンアーキテクチャは、アプリケーションのコア部分との接続領域にインターフェースを挟むことで、アプリケーションのコア部分(重要なビジネスロジック)を独立させているのです。
クリーンアーキテクチャ
4つの同心円的な図が特徴なアーキテクチャです。
個人的な解釈ですが、
クリーンアーキテクチャはレイヤードアーキテクチャとヘキサゴンアーキテクチャをいいとこ取りしたハイブリット型アーキテクチャだと考えています。
クリーンアーキテクチャの概念図「Clean Architecture 達人に学ぶソフトウェアの構造と設計」より引用
クリーンアーキテクチャは主に3つのルールがあります
- ソフトウェアを複数のレイヤーに分割して各クラスの役割を明確にする
- 中央のエンティティ(ビジネスロジック)を中心に、内側へ依存する
- 依存関係を逆転させて依存の方向性を制御する
すでにわかりやすく解説されている記事があるので、ここではかつあいしまs。
(決して解説を放棄しているわけではないです...汗)
ビジネスロジックを中心に据えることで独立させる
クリーンアーキテクチャもビジネスロジックを独立させることを目的としています。
レイヤードアーキテクチャやヘキサゴンアーキテクチャに比べると複雑ですが、
他のアーキテクチャと目的は変わりません。
どのアーキテクチャを採用するにしても、重要なことはドメイン知識・ビジネスルール(ビジネスロジック)を独立させることです。
他のオブジェクトから隔離することで、変更に対する影響を最小限に抑えることができます。
まとめ
ここまで、各アーキテクチャがどのようにしてビジネスロジックを独立させているか 見てきました。
ビジネスロジックを独立させる これをもっと抽象的に言い換えると、1度に多くのことを考えないことです。
何も考えずに1つのクラスに全ての処理を書くことは、そのクラスが多くのことを考えている と言い換えられます。
どのアーキテクチャも、クラスの役割を明確にすることが基本理念としてあります。
考えることを少なくすれば、それだけシンプルな設計にすることができます。
1度に多くのことを考えない を念頭に置いて設計をすると、自然とビジネスロジックが独立した設計になります。
それは、ビジネスルールを中心に据えるドメイン駆動設計において重要なアプローチです。
シリーズ化していたドメイン駆動設計の解説も、これで最後になります。
ここまで読んでいただき、ありがとうございました!