この記事で分かること
- Active RecordパターンとData Mapperパターンは、それぞれどういう考え方か
- なぜActive Recordパターンは「最も直感的なアプローチ」とされているのか
- なぜData Mapperパターンでは、オブジェクトとデータベースを分離する必要があるのか
- なぜDoctrine ORMは、Active RecordパターンからData Mapperパターンへ実装を変更したのか
「フレームワークの変遷」シリーズの6回目として、Martin Fowlerが著書『Patterns of Enterprise Application Architecture』のカタログページで説明しているActive RecordパターンとData Mapperパターンの内容、そしてRuby on RailsとDoctrine ORMがそれぞれの考え方をどのように採用してきたかを、公式ドキュメントに基づいて整理します。
エンタープライズアプリケーションにおけるデータアクセスの課題
Martin Fowlerは、Active Recordパターンのカタログページで次のように述べています。「オブジェクトはデータと振る舞いの両方を持つ。このデータの多くは永続的であり、データベースに保存する必要がある」["An object carries both data and behavior. Much of this data is persistent and needs to be stored in a database."]1。つまり、業務ロジックを持つオブジェクトの状態を、どうやってデータベースに保存し、読み書きするかが課題だったことになります1。
Active Recordパターンとは何か
Fowlerは、Active Recordを「データベースのテーブルやビューの1行をラップし、データベースアクセスをカプセル化して、そのデータにドメインロジックを追加するオブジェクト」["An object that wraps a row in a database table or view, encapsulates the database access, and adds domain logic on that data."]と定義しています1。
なぜActive Recordパターンが生まれたのか
Fowlerは、この設計を選んだ理由を次のように説明しています。「Active Recordは最も分かりやすいアプローチを取り、データアクセスのロジックをドメインオブジェクトの中に置く。これにより、誰もが自分のデータをデータベースとの間で読み書きする方法を理解できる」["Active Record uses the most obvious approach, putting data access logic in the domain object. This way all people know how to read and write their data to and from the database."]1。つまりFowlerによれば、データを扱うロジックをオブジェクト自身に持たせるのが、最も直感的で分かりやすい方法だったから、というのがActive Recordパターンが生まれた理由です1。
Ruby on RailsによるActive Recordの実践
Ruby on Railsの公式ガイドは、Active Recordパターンについて、Fowlerの定義「データベースのテーブルの行をラップし、データベースアクセスをカプセル化し、そのデータにドメインロジックを追加するオブジェクト」["an object that wraps a row in a database table, encapsulates the database access, and adds domain logic to that data."]を引用したうえで、「Active Recordオブジェクトはデータと振る舞いの両方を持つ。Active Recordのクラスは、基盤となるデータベースのレコード構造に非常に近い形で対応する。これにより、利用者はデータベースへの読み書きを簡単に行える」["Active Record objects carry both data and behavior. Active Record classes match very closely to the record structure of the underlying database. This way users can easily read from and write to the database"]と説明しています2。
同ガイドは、モデルクラスを定義する最初の例として次のコードを示しています2。
class Book < ApplicationRecord
end
このようにApplicationRecordを継承するだけで、Bookクラスは自動的に対応するテーブルと結び付きます。ガイドはこの背景にある考え方を、「同じやり方でアプリケーションを設定することが多いなら、そのやり方をデフォルトにすべきだ、というアイデアをRailsは採用している」["Rails adopts the idea that if you configure your applications in the same way most of the time, then that way should be the default."]と述べており、この考え方は「設定より規約(Convention over Configuration)」と呼ばれています2。
なお、Ruby on Rails 1.0は2005年12月13日に公式ブログで発表されています3。
Data Mapperパターンとは何か
Fowlerは、Data Mapperを「オブジェクトとデータベースの間でデータを移動させつつ、両者およびマッパー自身を互いに独立させておく、マッパーの層」["A layer of mappers that moves data between objects and a database while keeping them independent of each other and the mapper itself."]と定義しています4。
なぜData Mapperパターンが必要とされたのか
Fowlerは、その理由を次のように説明しています。「オブジェクトとリレーショナルデータベースは、データを構造化する仕組みが異なる。コレクションや継承といったオブジェクトの多くの部分は、リレーショナルデータベースには存在しない」["Objects and relational databases have different mechanisms for structuring data. Many parts of an object, such as collections and inheritance, aren't present in relational databases."]としたうえで、「業務ロジックの多いオブジェクトモデルを作るときは、こうした仕組みを使ってデータと振る舞いを整理する方が価値がある。そうすると、オブジェクトのスキーマとリレーショナルのスキーマが一致しない『variant schemas』が生じる」["When you build an object model with a lot of business logic it's valuable to use these mechanisms to better organize the data and the behavior that goes with it. Doing so leads to variant schemas; that is, the object schema and the relational schema don't match up."]と述べています4。さらに、「インメモリのオブジェクトがリレーショナルデータベースの構造を知っていると、片方の変更がもう片方に波及しやすくなる」["If the in-memory objects know about the relational database structure, changes in one tend to ripple to the other."]とも指摘しています4。つまりFowlerによれば、オブジェクトとデータベースの構造の違いが大きくなるほど、両者を直接結び付けることの弊害も大きくなるため、両者を分離するData Mapperという層が必要になった、ということです4。
Data Mapperを使うと、「インメモリのオブジェクトは、データベースが存在することさえ知る必要がなく、SQLインタフェースのコードも、データベースのスキーマの知識も一切必要ない」["the in-memory objects needn't know even that there's a database present; they need no SQL interface code, and certainly no knowledge of the database schema."]とFowlerは述べています4。
Doctrine ORMによるData Mapperの実践
PHPのDoctrine ORMは、公式ドキュメントの「Getting Started」で「Doctrine ORMは、PHPオブジェクトに透過的な永続化を提供する、PHP向けのオブジェクト-リレーショナルマッパー(ORM)である。中心にはData Mapperパターンを使い、ドメイン/業務ロジックを永続化から完全に分離することを目指している」["Doctrine ORM is an object-relational mapper (ORM) for PHP that provides transparent persistence for PHP objects. It uses the Data Mapper pattern at the heart, aiming for a complete separation of your domain/business logic from the persistence in a relational database management system."]と説明しています5。
同ページは、Entityの最初の例として次のようなシンプルなクラスを示しています5。
<?php
// src/Product.php
class Product
{
private int|null $id = null;
private string $name;
}
このEntityクラス自身はデータベースへのアクセス方法を持たず、公式ドキュメントは「Doctrineの公開インタフェースはEntityManagerを通じて提供される。このクラスは、エンティティの完全なライフサイクル管理へのアクセスポイントを提供する」["Doctrine's public interface is through the EntityManager. This class provides access points to the complete lifecycle management for your entities"]と説明しています5。
なぜDoctrineはActive RecordからData Mapperへ実装を変更したのか
Doctrineプロジェクトの公式ブログは、2010年12月21日付の記事で、Doctrine 2.0のリリースについて「最終的にDoctrine 1のコードは、見分けがつかないほど作り直され、元々のActiveRecordだったDoctrine 1は、新しいDataMapperの実装に置き換えられた」["In the end the Doctrine 1 code was refactored beyond recognition, replacing the original ActiveRecord Doctrine 1 with a new DataMapper implementation."]と述べています6。
同記事は、変更の内容を次のように説明しています。「Doctrine_Recordインスタンスに対してsave()やdelete()メソッドを呼ぶのではなく、EntityManagerと呼ばれるData Mapperオブジェクトにオブジェクトを渡すようになった。EntityManagerは、データベースと現在メモリ上にあるオブジェクトとの同期を要求するまで、すべての変更を追跡し続ける」["Instead of calling save() or delete() methods on your Doctrine_Record instances you now pass objects to the data mapper object called EntityManager and it keeps track of all changes until you request a synchronisation between database and the current objects in memory."]と述べたうえで、「この処理は非常に効率的で、一貫した意味論を持つ。これはDoctrine 1と比べて、パフォーマンスと開発者にとっての使いやすさの両面で大きな改善だ」["This process is very efficient and has consistent semantics. This is a significant improvement over Doctrine 1 in terms of performance and developer ease-of-use."]と述べています6。つまりDoctrineの公式発表によれば、Active Recordパターンの実装をData Mapperパターンに置き換えた理由は、パフォーマンスと開発者にとっての使いやすさを改善するためだった、ということです6。
図:Active RecordとData Mapperの構造の違い
Active RecordとData Mapperの比較
| 項目 | Active Record | Data Mapper |
|---|---|---|
| 定義 | データベースの行をラップし、データベースアクセスをカプセル化して、ドメインロジックを追加するオブジェクト1 | オブジェクトとデータベースの間でデータを移動させつつ、両者を互いに独立させておくマッパーの層4 |
| データベース構造の知識 | ドメインオブジェクトがデータアクセスのロジックを直接持つ1 | インメモリのオブジェクトはデータベースが存在することさえ知らなくてよい4 |
| 出典に登場する実装 | Ruby on Rails(Active Recordパターンを採用)2 | Doctrine ORM(Data Mapperパターンを中心に採用)5 |
年表
| 日付 | できごと | 出典 |
|---|---|---|
| 2003年3月5日 | martinfowler.comに、Active RecordとData Mapperのカタログページが掲載されている状態が確認できる | Fowler14 |
| 2005年12月13日 | Ruby on Rails 1.0が公式ブログで発表される | rubyonrails.org3 |
| 2010年12月21日 | Doctrine 2.0がリリースされ、Doctrine 1のActive Record実装がData Mapper実装に置き換わる | Doctrine公式ブログ6 |
まとめ
- Fowlerによると、オブジェクトはデータと振る舞いの両方を持ち、その永続的なデータをデータベースに保存する必要があるという課題に対して、Active RecordとData Mapperという2つのパターンが説明されています14。
- Active Recordは、データアクセスのロジックをドメインオブジェクトの中に直接置く「最も分かりやすいアプローチ」だとFowlerは述べています1。Ruby on Railsは、この考え方を採用し、モデルクラスとデータベースのテーブルを規約に基づいて対応させています2。
- Data Mapperは、オブジェクトとリレーショナルデータベースのデータ構造の違い(コレクションや継承の扱いなど)から生じる問題を避けるため、両者をマッパーの層で分離する考え方だとFowlerは説明しています4。
- Doctrine ORMは、当初Active Recordパターンを採用していたDoctrine 1から、2010年にData Mapperパターンを中心とするDoctrine 2へと実装を変更しました。公式ブログによれば、これはパフォーマンスと開発者にとっての使いやすさを改善するための変更でした6。
用語解説
- ORM(オブジェクト・リレーショナル・マッパー): PHPオブジェクトに透過的な永続化を提供する、オブジェクトとリレーショナルデータベースを対応付けるための仕組みです5。
- Convention over Configuration(設定より規約): 同じやり方でアプリケーションを設定することが多いなら、そのやり方をデフォルトにするという考え方です。Ruby on Railsが採用している方針です2。
- Entity: 一意の識別子や主キーによって、複数のリクエストをまたいで識別できるPHPオブジェクトのことです。Doctrine ORMにおけるデータの単位です5。
- 永続化: プログラムの実行が終わったあとも、オブジェクトのデータが失われないように、データベースなどに保存しておくことです15。
出典
- Active Record | martinfowler.com / 参照日: 2026-09-30
- Data Mapper | martinfowler.com / 参照日: 2026-09-30
- Active Record Basics - Ruby on Rails Guides / 参照日: 2026-09-30
- Rails 1.0: Party like it's one oh oh! - Ruby on Rails / 参照日: 2026-09-30
- Getting Started - Doctrine Object Relational Mapper (ORM) / 参照日: 2026-09-30
- Doctrine 2 First Stable Release - Doctrine: PHP Open Source Project / 参照日: 2026-09-30
この記事はAIの支援で下書きを作成し、公開前に人が確認しています。
-
Active Record | martinfowler.com ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 ↩11
-
Active Record Basics - Ruby on Rails Guides ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Data Mapper | martinfowler.com ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10
-
Getting Started - Doctrine Object Relational Mapper (ORM) ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7
-
Doctrine 2 First Stable Release - Doctrine: PHP Open Source Project ↩ ↩2 ↩3 ↩4 ↩5