2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【書評】 Patterns of Enterprise Application Architecture

2
Posted at

はじめに

Active RecordData MapperUnit of WorkRepositoryDTOFront Controller。これらの言葉を、私たちは日常的に使っています。RailsのモデルはActive Recordですし、Spring MVCのDispatcherServletはFront Controller、JPAのEntityManagerはUnit of WorkとIdentity Mapそのものです。

では、これらの名前はどこから来たのでしょうか。そのほとんどの出典が、本書『Patterns of Enterprise Application Architecture』(通称 PoEAA)です。Martin Fowlerが2002年に著した、エンタープライズアプリケーションの設計判断を51個のパターンとして整理したカタログです。

原著から20年以上が経ち、EJB 2.0やCOM+といった記述は完全に歴史資料になっています。それでも本書が読まれ続けているのは、パターンそのものではなく「どういう判断軸でパターンを選ぶか」という思考の型が書かれているからだと感じました。

この記事では、本書全体を通読した上で、章立てに沿った要約と、現代の開発から見た読みどころを1本にまとめます。


書籍情報

項目 内容
原題 Patterns of Enterprise Application Architecture
邦題 エンタープライズ アプリケーションアーキテクチャパターン
著者 Martin Fowler(一部パターンは David Rice, Matt Foemmel, Edward Hieatt, Robert Mee, Randy Stafford による寄稿)
出版社 Addison-Wesley
原著刊行 2002年
収録パターン数 51
サンプルコード Java / C#

本書の構成

本書は明確に2部構成になっています。この構成を理解しておくと、読み方がぐっと楽になります。

第I部:ナラティブ(第1章〜第8章)

パターンを使うための「地図」にあたる部分です。約100ページで、エンタープライズアプリケーションが直面する問題領域を俯瞰します。ここは通読が前提です。

第II部:パターンカタログ(第9章〜第18章)

51個のパターンの詳細解説です。各パターンは「意図」「スケッチ(UML図)」「仕組み」「いつ使うべきか」「さらに読む」「例(Java / C#)」というフォーマットで統一されています。

こちらは通読不要で、必要になったときに引くリファレンスとして設計されています。Fowler自身も、第I部を読んでから興味のあるパターンだけ拾えばよい、という趣旨のことを書いています。


第I部:ナラティブの要点

第1章 レイヤリング

いきなり本書の土台となる話から始まります。

レイヤー分割の利点として、他のレイヤーの詳細を知らずに単一レイヤーを理解できること、代替実装に差し替えられること、依存を最小化できること、標準化しやすいことが挙げられています。一方で欠点も率直で、UIに1フィールド追加するために中間のすべてのレイヤーを触る羽目になる、という例は誰もが経験したことがあるはずです。

本書が採用するのは、以下の3層です。

レイヤー 責務
プレゼンテーション ユーザーへの情報表示、コマンドの解釈
ドメイン 業務ロジック、計算、検証、ディスパッチ判断
データソース DB、メッセージング、他システムとの通信

ここで印象的なのは、「レイヤー(layer)」と「ティア(tier)」を明確に区別している点です。レイヤーは論理的な分割であり、物理的な分離を意味しません。ノートPC1台の中でローカルDBを使っていても、3つのレイヤーは存在します。

また、ドメインロジックの判別法として紹介されている非公式テストが実用的でした。

アプリケーションに根本的に異なるレイヤーを追加すると想像してみる。たとえばWebアプリにコマンドラインインターフェースを足すとしたら、機能が重複するか?

売上が前月比10%以上伸びた商品を赤く表示する例が挙げられています。プレゼンテーション層で「今月と先月を比較して10%超なら赤」と書くのは、ドメインロジックの漏れ出しです。ドメイン層に「伸びているか」を返すメソッドを置き、プレゼンテーション層はその真偽で色を決める。判断と表示方法の選択を分離するという話です。

さらに、Alistair Cockburnのヘキサゴナルアーキテクチャに触れ、それが対称的な視点であるのに対し自分の階層化は非対称だと述べているのも面白い点です。「他者に提供するサービス」と「他者から受けるサービス」を区別することに価値がある、という立場です。

第2章 ドメインロジックの整理

本書の中で最も重要な章だと感じました。ドメインロジックの構成方法を3つに整理します。

パターン 構成の単位 特徴
Transaction Script ユーザーアクションごとの手続き 単純明快。ロジックが増えると重複が爆発
Domain Model ドメインの名詞ごとのオブジェクト 複雑さに強い。DBマッピングのコストが高い
Table Module DBテーブルごとの単一インスタンス RecordSetと相性が良い。継承や多態は使えない

Table Moduleは、Domain Modelと似て見えますが決定的に違います。Domain Modelは契約1件につき1インスタンスですが、Table Moduleはインスタンスが1つしかなく、常にIDを引数で渡します。.NETのDataSetを前提とした設計で、当時のMicrosoftスタックで主流だったスタイルです。

そして選択の指針として、有名な**「複雑さ vs 労力」のグラフ**が登場します。Fowler自身が「軸が定量化されていない非科学的なグラフ」と自嘲しているのが正直で好感が持てます。

  • ドメインロジックが単純なうちは、Domain Modelは学習コストとマッピングコストを回収できない
  • 複雑さが増すと、Transaction Script は指数関数的に苦しくなる
  • 交差点がどこかは誰にも測れない

「ドメインロジック複雑度が7.42を超えたらDomain Model」と言えたら楽なのに、測る方法を誰も知らない、というくだりは本書の誠実さを象徴しています。

Service Layerについての記述も重要です。Fowler自身は「サービス層はできるだけ薄くしたい」「まず不要と判断し、必要と分かってから追加する」という立場を明言しています。Controller-Entityスタイルについても「一般的だが自分は好きではない」と述べ、重複を促進すると批判しています。

同時に「多くの優れた設計者は常にロジックの詰まったService Layerを使っているので、この点については私の意見を無視してほしい」とも書き、Service Layerパターン自体はRandy Staffordに執筆を依頼しています。この距離感の取り方は見習いたいところです。

第3章 リレーショナルデータベースへのマッピング

いわゆるインピーダンスミスマッチに正面から取り組む章です。アーキテクチャパターンの選択(Gateway系、Active Record、Data Mapper)、挙動の問題(Unit of Work、Identity Map、Lazy Load)、構造マッピング、継承のマッピング、メタデータによるマッピング構築が扱われます。

現代のORMがやっていることが、ほぼそのまま言語化されています。JPAやEntity Frameworkを触ったことがあれば、「あの挙動はこれか」と腑に落ちる箇所が多いはずです。

第4章 Webプレゼンテーション

MVCの解説と、その分解が中心です。

  • 入力コントローラ:Page Controller(ページ単位)と Front Controller(単一入口)
  • ビュー:Template View(HTML内に埋め込む)と Transform View(データを変換する)と Two Step View(論理画面→物理画面の2段階)

Two Step Viewは、全画面の見た目を一括で変える必要がある場合に効くパターンで、デザインシステムやテーマ切り替えの文脈で再評価してもよいと感じました。

第5章 並行性

トランザクションとACID、分離レベル、デッドロックといった基礎に加え、本書独自の重要な区別が出てきます。DBが提供するシステムトランザクションと、ユーザーから見た一連の作業であるビジネストランザクションです。

ビジネストランザクションは複数リクエストにまたがるためシステムトランザクションで囲えず、独自の並行性制御が必要になります。これがOffline Lock系パターンの動機です。

第6章 セッション状態

セッション状態の置き場所を3つに整理します。

保存場所 メリット デメリット
Client Session State サーバがステートレスでスケールしやすい 転送量、セキュリティ、サイズ制限
Server Session State 実装が単純 クラスタリングとフェイルオーバーが面倒
Database Session State 耐障害性が高い 性能コスト、スキーマ管理

JWTを使うか、Redisに置くか、DBに持つか。この20年で技術は変わっても、トレードオフの構造はまったく変わっていません。

第7章 分散戦略

本書で最も引用される一節が登場します。

分散オブジェクト設計の第一法則:オブジェクトを分散させるな。

分散オブジェクト(当時のCORBAやEJBのリモートインターフェース)が「透過的に分散できる」と喧伝されたことへの批判です。細粒度のリモート呼び出しは性能を破壊するため、どうしても分散が必要ならRemote Facadeで粗粒度のインターフェースを設け、**Data Transfer Object(DTO)**でまとめて運べ、という処方箋になります。

マイクロサービス全盛の現在から見ると挑発的にも聞こえますが、実際には「サービス境界を安易に増やすな」「境界を跨ぐ呼び出しは粗粒度にせよ」という現代の教訓と同じことを言っています。

第8章 すべてをまとめる

ここまでの選択をどう組み合わせるかを示す章です。大原則はシンプルで、まずドメイン層のパターンを決め、それに合わせてデータソース層を選ぶ、という順序になります。

ドメイン層 相性の良いデータソース層
Transaction Script Row Data Gateway / Table Data Gateway
Table Module Table Data Gateway(+ RecordSet)
Domain Model(単純) Active Record
Domain Model(リッチ) Data Mapper

「リッチなDomain ModelにActive Recordを合わせるとつらくなる」という指摘は、実務でよく見る失敗パターンそのものです。


第II部:パターンカタログの俯瞰

51パターンを章ごとに一覧化します。手元の索引として使えるはずです。

第9章 ドメインロジックパターン

パターン 一行要約
Transaction Script 1リクエスト=1手続きで業務ロジックを構成する
Domain Model データと振る舞いを持つドメインのオブジェクトモデル
Table Module テーブル(またはビュー)1つにつき1インスタンスでロジックを持つ
Service Layer アプリケーション境界と利用可能な操作の集合を定義する

第10章 データソースアーキテクチャパターン

パターン 一行要約
Table Data Gateway テーブル1つへのゲートウェイ。全行を扱う
Row Data Gateway 1行につき1インスタンスのゲートウェイ
Active Record 行をラップし、データアクセスとドメインロジックの両方を持つ
Data Mapper ドメインとDBを相互に独立させるマッピング層

第11章 オブジェクトリレーショナル 挙動パターン

パターン 一行要約
Unit of Work 変更されたオブジェクトを追跡し、書き込みを協調させる
Identity Map 読み込んだオブジェクトをマップに保持し、1度だけ読む
Lazy Load 必要になるまでデータを読み込まない

第12章 オブジェクトリレーショナル 構造パターン

パターン 一行要約
Identity Field DBの主キーをオブジェクトのフィールドとして保持する
Foreign Key Mapping オブジェクト間の関連を外部キーにマップする
Association Table Mapping 多対多の関連を中間テーブルで表現する
Dependent Mapping あるクラスのDBマッピングを別クラスに委ねる
Embedded Value 小さなオブジェクトを所有側のテーブルにインライン展開する
Serialized LOB オブジェクトグラフをまとめて1カラムに直列化する
Single / Class / Concrete Table Inheritance 継承階層を1テーブル/クラスごと/具象クラスごとにマップする
Inheritance Mappers 継承階層のマッパーを構造化する

第13章 オブジェクトリレーショナル メタデータマッピングパターン

パターン 一行要約
Metadata Mapping マッピング詳細をメタデータとして持ち、コード生成や実行時反映を行う
Query Object クエリをオブジェクトとして表現する
Repository オブジェクトのコレクションのように振る舞う問い合わせ境界

Repositoryがここに置かれているのが興味深い点です。DDDの文脈で語られがちですが、本書ではあくまでQuery ObjectとMetadata Mappingの延長として位置づけられています。

第14章 Webプレゼンテーションパターン

パターン 一行要約
Model View Controller 入力・処理・出力を3つの役割に分ける
Page Controller ページ/アクションごとにコントローラを置く
Front Controller すべてのリクエストを単一のハンドラで受ける
Template View マークアップ内にプレースホルダを埋め込んで描画する
Transform View ドメインデータを1要素ずつ変換して描画する
Two Step View 論理画面を組み立ててから物理的な見た目に落とす
Application Controller 画面遷移とアプリケーションフローを一元管理する

第15章 分散パターン

パターン 一行要約
Remote Facade 細粒度オブジェクトの前に粗粒度のファサードを置く
Data Transfer Object 呼び出し回数を減らすためにデータをまとめて運ぶ

第16章 オフライン並行性パターン

パターン 一行要約
Optimistic Offline Lock コミット時に競合を検出する(バージョン番号など)
Pessimistic Offline Lock 同時に1セッションだけがデータを触れるようにする
Coarse-Grained Lock 関連オブジェクト群を1つの単位でロックする
Implicit Lock ロック取得をフレームワーク側で強制する

第17章 セッション状態パターン

パターン 一行要約
Client Session State セッション状態をクライアントに保存する
Server Session State サーバ側にシリアライズして保持する
Database Session State コミット済みデータとしてDBに保持する

第18章 基本パターン

パターン 一行要約
Gateway 外部システムやリソースを単純なオブジェクトで包む
Mapper 2つのオブジェクトを、互いに知らせずに橋渡しする
Layer Supertype レイヤー内の全型に共通する親クラスを置く
Separated Interface インターフェースと実装を別パッケージに分ける
Registry よく使うオブジェクトへのグローバルな入口を提供する
Value Object 等価性を値で判定する小さなオブジェクト
Money 通貨と金額を扱い、端数処理を正しく行う
Special Case nullの代わりに特別な振る舞いを持つサブクラスを返す
Plugin 設定によって実装クラスを差し替える
Service Stub テスト時に外部サービスを置き換える
Record Set 表形式データのインメモリ表現

MoneySpecial Case は、いま読んでも実務で即使える小粒だが強力なパターンです。特に Money は、金額を3分割すると1円が消えるという具体例とともに allocate メソッドが示されており、金融系でなくても知っておく価値があります。


2026年に読む意味:現代スタックとの対応表

「古い本」で終わらせないために、対応関係を整理してみました。

本書のパターン 現代での姿
Active Record Rails ActiveRecord, Laravel Eloquent, Django ORM
Data Mapper Hibernate / JPA, SQLAlchemy, TypeORM(DataMapperモード)
Unit of Work JPA EntityManager, EF Core DbContext
Identity Map 各種ORMの1次キャッシュ
Lazy Load JPAのFetchType.LAZY, N+1問題の温床
Repository Spring Data Repository, DDD文脈のRepository
Front Controller Spring DispatcherServlet, Rails Router
Optimistic Offline Lock @Version によるバージョン管理、ETag / If-Match
Client Session State JWT, Cookieセッション
Database Session State Redis/DBによるセッションストア
Remote Facade + DTO BFF、GraphQLのクエリ集約、gRPCのメッセージ設計
Service Stub WireMock, MSW, テストダブル全般

こうして並べると、フレームワークが「暗黙の選択」として提供しているものが、本書では明示的な選択肢として並んでいることが分かります。フレームワークを使うということは、これらのパターンを選択したということでもあります。


読んでいて特に響いた点

1. 判断の理由が必ず書かれている

本書のパターン記述は「いつ使うべきか」に多くのページを割き、むしろ「いつ使うべきでないか」の記述のほうが有用なことすらあります。Service Layerについて「不要と判断してから、必要になったときに追加する」と書くように、採用しない判断も設計判断として扱われているのが一貫しています。

2. 著者が自分の偏りを明示する

Fowlerは繰り返し「私はオブジェクト寄りの人間だ」と自認し、その上で「単純な問題ならTransaction Scriptで十分」「Table Moduleは.NET環境なら合理的」と述べます。自分の好みと状況に応じた妥当な選択を分けて書けるのは、技術書として稀有な誠実さだと感じました。

3. 「複雑性を高める要因」のリスト

第1章の締めくくりで、Jens Coldeweyの言葉として以下が挙げられています。

  • 分散
  • 明示的なマルチスレッド
  • パラダイムの断絶(オブジェクトとリレーショナルなど)
  • マルチプラットフォーム開発
  • 極端な性能要件

これらは必要なら採用するが、開発と継続的な保守の両方でコストがかかることを忘れるな、という警告です。マイクロサービス、非同期処理、ORM、クロスプラットフォーム、スケーラビリティ。2026年の技術選定でチェックリストとしてそのまま使えます。


注意点

正直に書くと、読みにくい部分もあります。サンプルコードはEJB 2.0やADO.NETのDataSetが前提ですし、本文中の相互参照が「Domain Model(116)」のようにページ番号で行われるため、電子版では追いにくい場面もあります。

ただし、コード例が古いのは本質的な問題ではありません。パターンの説明部分と「いつ使うべきか」だけ読めば、十分に価値を回収できます。


こんな人におすすめ

  • ORMの挙動を「なんとなく」で使っていて、内部の設計思想を知りたい方
  • ドメインロジックをどこに置くべきか、チーム内で議論が噛み合わない方
  • DDDに入る前に、その土台となる語彙を整理したい方
  • 「レイヤードアーキテクチャ」を言葉ではなく判断軸として理解したい方

逆に、すぐ使えるコードを求めている方には向きません。本書は設計判断の語彙集であって、レシピ集ではないからです。


まとめ

本書を通読して得た最大の収穫は、設計の選択肢に名前がついている状態の強さでした。

「ここはActive Recordだと厳しいのでData Mapperにしよう」と言えるチームと、「ORMの使い方を変えよう」としか言えないチームでは、議論の解像度がまったく違います。名前があるということは、比較検討ができるということです。

そして本書が一貫して伝えているのは、どのパターンにも「使うべきでない状況」があるというシンプルな事実です。銀の弾丸を提示しない代わりに、判断のための軸を与えてくれます。

原著から20年以上が経ち、フレームワークが多くの選択を代行してくれるようになりました。だからこそ、その選択が何をトレードオフしているのかを知るために、いま読む価値があると感じています。DDDやクリーンアーキテクチャに進む前の土台としても、日々の実装で「なぜこう書くのか」を言語化するためにも、手元に置いておきたい一冊です。


参考

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?