第30章 データベースは詳細
著者見解
アーキテクチャの観点から言うと、「データベースそのものはエンティティではなく、あくまで詳細(detail)である」という点をまず押さえておくべきです。重要なのは「扱うデータそのもの(データモデル)」であり、データを格納・取得する道具としてのデータベース(RDBMSやファイルストア等)は実装上の選択に過ぎません。アーキテクチャ設計では、ビジネスのユースケースやルールはデータベースの種類や内部形式を意識してはいけません。逆に、データベースの行やテーブル構造がドメインやユースケースに直接浸透してしまうと、ドメイン全体がデータベース技術に縛られてしまいます。
歴史的に見るとリレーショナルデータベースが広く採用された背景には、磁気ディスクの遅さというハードウェア特性があります。ディスクアクセスはミリ秒単位の遅延を伴うため、インデックスやキャッシュ、クエリ最適化といった工夫が生まれ、結果としてデータを効率的に扱うためのソフトウェア(RDBMS)が必要になりました。つまり「ディスクを効率的に使うための仕組み」がデータベース技術の本質であり、それ自体は優れた技術ですが、アーキテクチャの中核(方針)ではありません。
将来に目を向けると、ストレージ技術は変わりつつあります。ディスク中心の時代が終わり、データがメモリ上で扱われるようになると、データはリンクリストやツリー、ハッシュなどのデータ構造で表現され、ポインタや参照で高速にアクセスされるようになります。そうなれば「行やテーブルという表現に縛られたまま設計する意味」は薄れます。だからこそ、データの物理的な置き場やアクセス手段(データベースの種類、永続化アルゴリズム等)は「詳細」として扱い、ユースケースやビジネスルールはそれらに依存しない設計にするべきです。
実務上の注意点としては、ORMやデータフレームワークが「テーブル行やDBのオブジェクトをそのままドメインへ渡す」設計は避けるべきだということです。こうするとドメインやユースケースがストレージの形式に縛られ、変更耐性が失われます。代わりに、データアクセスはインターフェイス(リポジトリやゲートウェイ)を通して行い、外側の永続化層でSQLやDB固有の構造を閉じ込め、内側にはドメインに適したデータ構造(エンティティやDTO)を渡すべきです。
要約すると、データそのもの(内容・モデル)はアーキテクチャ上重要ですが、データベースという実装技術は詳細にすぎません。アーキテクトはデータの意味を中心に設計し、データベース固有の知識をアーキテクチャの外側に閉じ込めることで、将来の技術変化に強いシステムをつくるべきです。
筆者所感
私の前職では、プロジェクトを始めるとまずDB設計から入ることが常でした。これは中心メンバーが設計を学んだ時代の制約(ディスク性能や運用慣行)や当時の技術トレンドに基づく合理的な判断が背景にありました。しかしこのやり方は結果的に「データベース主導」の構造を生み、アプリケーションのドメインやビジネスルールがストレージ設計に引きずられてしまうことがありました。とはいえ当時のメンバーは意図的にそうしていたわけではなく、持っている知識の範囲内で最善の判断をしていただけだと理解しています。今日ではハードや設計思想が変わり、ドメイン中心設計やクリーンアーキテクチャが普及していますが、古い慣習が残る現場では用語や考え方のアップデートを促すことが重要だと感じています。
第31章 ウェブは詳細
著者見解
IT業界の歴史を俯瞰すると、ウェブは新発明というよりも「振り子運動」の一断面にすぎません。計算資源を中央サーバに集中させる時期と、端末側に分散させる時期を行き来する流れの延長線上に、ウェブの変遷があります。初期はサーバ集中型でブラウザは表示だけ、次いでアプレットやクライアント実行が増え、再びサーバ側へ戻り、Web 2.0 でブラウザ側に処理が戻り……いまや JavaScript がサーバ(Node.js)でも動くようになり、往復を繰り返しています。
アーキテクトの視点から重要なのは、この振り子運動そのものは短期的な技術潮流であり、ビジネスルール(ドメインの本質)から切り離して扱うべきだ、ということです。ウェブは GUI の一形態にすぎず、GUI は「詳細(detail)」です。したがってウェブ固有の振る舞いや技術選択をシステムの中心に据えるべきではありません。可能な限り、GUI(ウェブ含む)をビジネスロジックから分離しておくことが長期的な柔軟性を保ちます。
「でも、ウェブの UI はリッチで特殊だから、完全に抽象化するのは無理では?」という反論もあります。確かにブラウザとデスクトップの GUI ではやり取りの様式が異なり、細部は異なります。しかし、UI とアプリケーション(ビジネスロジック)の間には別の、抽象化可能な境界があります。ビジネスロジックはユースケースの集合であり、各ユースケースは「入力データ → 処理 → 出力データ」という形で記述できます。入力と出力を適切なデータ構造(DTO など)で表現すれば、ユースケースはどのデバイスが使われているかを気にせず実行できるようになります。
この方法により、ウェブ・モバイル・デスクトップ・コマンドラインなど異なる UI を同じユースケースに接続でき、UI の変化がビジネスロジックに波及しにくくなります。ただし、適切な抽象化を見つけるのは簡単ではなく、試行錯誤と設計の反復が必要です。UI ごとの特殊性を全て隠蔽するわけではなく、どのレベルで共通化し、どの差分を許容するかを判断することが肝要です。
筆者所感
個人の意見としても、ウェブは短期間で大きく変わる領域なので、コアとなるビジネスロジックはウェブ固有の実装から独立させるのが望ましいと考えています。もう少し踏み込みと、例えば、ウェブという特性上、UIの操作はブラウザに依存することになりますが、その仕様や挙動はベンダー側の変更やトレンドに左右されやすく、UIとロジックが密結合していると、保守対応や複数クライアント対応で手間が増加します。実務的にはユースケースをサーバ側の API(あるいは共有ライブラリ)で実装し、ウェブは入力を収集して DTO を送る・表示を受け取ってレンダリングする役割だけに留めると良いでしょう。こうすれば、ウェブ/モバイル/デスクトップなど異なる UI を同じ業務ロジックに接続でき、保守性と柔軟性が高まります。
第32章 フレームワークの詳細
著者見解
フレームワークは便利な道具だが、それ自体が「アーキテクチャ」ではない、という点をまず押さえておくべきです。フレームワークの作者は自分や身近な開発者が抱える問題を解決するために作っており、あなたのシステム固有の要件や将来の変化に責任を持ってくれるわけではありません。したがって、フレームワークを導入するときのコミットメントは基本的に一方的で、リスクや負担の大部分は利用者側に残ります。
具体的なリスクとしては、フレームワークが依存性のルールに反する構造を強いること、プロダクトの成長に伴ってフレームワークの提供機能だけでは対応できなくなること、あるいはフレームワーク自身の進化やサポート方針が望む方向とずれてしまうことなどが挙げられます。こうした事情から、フレームワークをシステムの中心(コア)に直接結びつけてしまうと、将来的な乗り換えや保守が非常に困難になります。
だからこそ設計上は、フレームワークをアーキテクチャの外側に置き、コアのビジネスロジックと強く結合させないことが重要です。実務的には、フレームワーク依存のコードはインフラやアダプターといった外側のコンポーネントにまとめ、ドメインやユースケース層は抽象インターフェイスにのみ依存させます。たとえば DI(依存注入)を使うなら、Main(起動処理)など最外層で注入を完了し、その先はフレームワークに関する知識を含まない通常のコードで動かすのがよいでしょう。
また、ビジネスオブジェクトやドメインモデルにフレームワーク固有の注釈や型(例:@Autowired やフレームワーク特有の基底クラス)を散りばめるのは避けるべきです。代わりにプロキシ/アダプターを用意してフレームワークとの橋渡しを行い、コア側は抽象に対してプログラムするだけにします。こうすることで、フレームワークを将来的に差し替えたい場合や、フレームワークのバグや仕様変更に対処する際の影響を小さくできます。
結論として、フレームワークとは「便利な詳細」であり、すぐに飛びついて深く結婚するべきではありません。まずは抽象化やアダプタ層による時間稼ぎを行い、設計的な距離を保ったうえで慎重に取り込む。これが、長期的に柔軟で保守しやすいシステムを作るための賢いアプローチです。
筆者所感
現在のプロジェクトでは Laravel を使っています。Laravel は Rails の影響を受けた設計(ファサード、グローバルヘルパー、Eloquent の Active Record スタイルなど)を取り入れており、フレームワークのベストプラクティスに従うと自然とプロジェクト内部が密結合になりやすいと感じます。それ自体はフレームワークの思想なので否定するものではありませんが、プロダクトが成長するとテストが書きにくくなったり、コードの理解や変更が難しくなったりするリスクがあります。そこで私は個人アプリで一から開発する時は最初に Laravel の DI 機能だけを使い、必要になった機能を段階的に取り込む方針をとっています。こうすることでコアをフレームワークに汚染せず、後で差し替えや移植をしやすく保てます。
第33章 事例:動画販売サイト
著者見解
動画販売サイトを題材にした本章では、アーキテクチャ設計の出発点と具体的な分解の考え方を示しています。
まず、システムの目的と関係者を明確にします。扱う商品は動画で、個人向けにはストリーミングとダウンロード(永続ライセンス)を、法人向けにはストリーミング中心のライセンス(ボリューム割引あり)を提供します。付属資料や価格設定、コンテンツ登録といった運用も必要です。ここから主要なアクターとして「作者」「購入者」「視聴者」「管理者」が浮かび上がり、各アクターがシステム変更の主な理由(責務)になることがわかります。単一責任の観点では、各アクターに対応する機能群を分離しておくことが望ましいとされます。
次にユースケースを列挙します。例として「ダウンロードライセンスを購入する」「MP4 を登録する」「ログイン/ログアウト」などがあり、類似する振る舞いは抽象ユースケースとして共通化できることもあります(たとえば「カタログ閲覧」を購入者/視聴者がそれぞれ使う、など)。ただし、初期段階で無理に抽象化する必要はなく、実情に応じて共通化を進めれば良い、と著者は述べています。
これらを踏まえてコンポーネントアーキテクチャを検討すると、よくある境界(ビュー/プレゼンター/インタラクター/コントローラ)に沿った分割が有効です。さらにアクターごとに責務を分けることで、あるアクターへの変更が他のアクターに波及しにくくなります。
実装とデプロイの単位については柔軟性を残すことが重要です。理想的には上記のコンポーネントを .jar や .dll として独立させてビルド・配布できるように設計しますが、運用上は複数のコンポーネントをまとめて一つの配布単位にする選択肢も残しておくべきです。たとえばビューとプレゼンターを一つにまとめ、インタラクターやコントローラは別にする、あるいはビュー+プレゼンターとその他で二つにまとめる、といった具合です。重要なのは、将来の変更に応じてまとめ方やデプロイ単位を柔軟に変えられることです。
制御の流れは「コントローラ → インタラクター → プレゼンター → ビュー」の順で進みますが、ソースコード上の依存関係は必ずしも制御の流れと同じ方向にはなりません。アーキテクチャの基本原則として、ソースコードの依存は常に上位レベル(方針)に向けるべきであり、継承関係などは制御の流れと逆向きになることもあります。こうした設計により、下位レベルの詳細が変わっても上位のビジネス方針に影響を与えないようにできます。
まとめると、本章で示されたアーキテクチャ設計は二つの視点での分割を組み合わせることが肝要です。ひとつはアクターに基づく分割(変更理由ごとに切る)、もうひとつは方針レベルと詳細レベルの分割(変更頻度・抽象度による層分け)。これらを意識してコンポーネントを構成すれば、機能追加や運用要件の変化に対して、望むデプロイ戦略や構成に柔軟に対応できるシステムを作れます。
筆者所感
著者の示すようにアクター抽出やユースケース列挙が済んだら、まずはビジネスユースケース(インタラクター)の実装に集中すべきだと考えています。ビュー/プレゼンター/コントローラやDBアクセスの実装はあと回しでかまいません。ユースケースはインターフェイス(ポート)を定義して実装し、ユニットテストとモックで動作を担保すれば、後からビューや永続化をアダプタとして差し替えて接続できます。こうすることで初期段階で詳細に気を取られず、保守性・拡張性の高い核を先に固められます。
第34章 書き残したこと
著者見解(Simon Brown)
ここまでの助言を踏まえれば、クリーンなソフトウェアを設計できる余地は大きく広がります。クラスやコンポーネントを明確に分け、責務をはっきりさせ、依存関係を管理できていれば理想に近づきます。しかし実際には「悪魔は実装の詳細に宿る」──設計の良さは実装の仕方しだいで簡単に崩れてしまう、というのが本章の主題です。ここでは、よく使われる設計スタイルと、それぞれの長所・短所、現実的な運用上の注意点を整理します。
まず最も単純な手法が水平方向のレイヤード(層状)アーキテクチャです。Web 層、ビジネスロジック層、永続化層といった技術的役割で分けるやり方は、素早く動作するプロトタイプや小規模システムには有効です。しかしシステムが大きくなると「単に同種のコードを三つの桶に分けただけ」では追いきれなくなり、ドメインの意図が見えにくくなる欠点があります。
それに対して「機能(フィーチャ)によるパッケージング」は垂直分割に近く、ドメインの概念や機能ごとにまとまりを作るアプローチです。こうするとトップレベルの構成が業務(ドメイン)について「叫ぶ」ようになり、どのコードを修正すればよいか把握しやすくなります。
「ポートとアダプター(ヘキサゴナル)」「BCE」などは、ドメイン(内側)とインフラ(外側)を明確に分離し、外側が内側に依存する形を徹底する考え方です。ここでの目的は、フレームワークや DB といった技術的詳細をドメインから切り離すことにあります。
著者が最終的に推すのは「コンポーネントによるパッケージング」です。これは上記手法の良い点を取り込み、粒度の粗いコンポーネント単位で関連責務をまとわせる考え方です。コンポーネントは「よく整ったインターフェイスの後ろに隠された機能群」であり、内部は隠蔽され、利用者はコンポーネント境界だけを意識すればよくなります。C4 モデルのようにコンテナ → コンポーネント → クラスという階層で考えるとわかりやすく、うまく設計されたモノリシックのコンポーネント群はマイクロサービス化への第一歩にもなります。
ただし、どのスタイルを採るにしても「実装時の粒度や可視性(アクセス制御)が肝心」だという点を忘れてはなりません。アクセス修飾子を安易に public にしすぎると、パッケージやモジュールの意味が消え、せっかくの分離が無効になります。各種パッケージング方式ごとに「どのインターフェイスを public にするか」「どこまでパッケージ内に閉じるか」を慎重に設計し、コンパイラや言語機能で守らせるのが現実的です。
言語やビルドの機能も活用できます。Java のモジュールシステムや別々のソースツリー/プロジェクト分割を使えば、物理的に依存を制限できます。例えば「ドメイン」と「インフラ」を別ソースツリーにすることで、インフラがドメインを迂回して呼び出すことを防げます。しかし、モジュールを細かく分けすぎるとビルドやパフォーマンス、運用の負荷が増すため、分割の度合いはチームの規模・スキル・プロダクトの複雑さ・工期と予算を考慮して決める必要があります。
結論としてはこうなります。どのアーキテクチャスタイルも正しく使えば有益ですが、実装を甘く扱うとどれも同じように崩れます。言語のアクセス制御やビルド構成を使って依存ルールを強制し、過度な公開を避け、コンパイラに守らせる設計を心がけてください。また理想を追いすぎて過剰に分割すると現実の開発・保守コストが跳ね上がるため、実態に即したトレードオフ判断が重要です。最終的には「選んだスタイルを実際に守れるか」を基準に選び、悪魔(実装の落とし穴)を避けることが肝要です。
筆者所感
私の経験では、アーキテクトが実装から離れていると設計が現実と乖離しやすいと感じます。前職の下請け時代は、元請け企業が設計を担当していましたが、ウォーターフォールで後から設計を変更しにくいという事情もあってか、プロジェクト初期に大仰なアーキテクチャを導入してしまい、実装者のスキルが追いつかず、プロジェクトが炎上するケースを見てきました。だからこそ、アーキテクトは机上の設計だけで終わらせず、実際にコードに触れて現状を把握し、プロジェクトの規模や成長段階に合わせて身の丈に合ったアーキテクチャを段階的に導入していくべきだと思います。