DAOとは?Data Access Objectパターンの役割・仕組み・Repositoryとの違いを解説
アプリケーション開発において、データアクセスは非常に重要でありながら、複雑になりやすい領域です。ユーザー情報、商品情報、注文情報、請求情報、在庫情報など、多くのアプリケーションはデータベースとやり取りしながら動作します。しかし、画面処理や業務ロジックの中にSQLやデータベース接続処理が直接書かれていると、コードはすぐに読みにくくなり、変更にも弱くなります。
DAO、つまりデータアクセスオブジェクトは、この問題を解決するための設計パターンです。DAOは、データベースや外部データソースへのアクセス処理を専用のオブジェクトに分離し、業務ロジックから隠蔽します。これにより、アプリケーションは「ユーザーを取得する」「商品を保存する」「注文を検索する」といった目的に集中でき、SQLやORM、接続処理の詳細を直接意識せずに済みます。
1. DAOとは
DAOとは、データアクセスロジックを業務ロジックから分離するための設計パターンです。アプリケーションがデータベースへ直接アクセスするのではなく、DAOを経由してデータの作成、取得、更新、削除を行います。DAOは、アプリケーションとデータソースの間に置かれる中間層として機能します。
DAOを理解するときに重要なのは、「DAOはデータそのものではなく、データへアクセスするための入口である」という点です。DAOはSQL文、ORMの呼び出し、データベース接続、検索条件、結果の変換処理などを内部に閉じ込め、外側には分かりやすいメソッドだけを提供します。
| 観点 | DAOの意味 |
|---|---|
| 正式名称 | Data Access Object |
| 日本語表現 | データアクセスオブジェクト |
| 主な役割 | データアクセスロジックを隠蔽する |
| 位置づけ | アプリケーションとデータソースの間の中間層 |
| 扱う対象 | エンティティ、テーブル、レコード、ドキュメント |
| 主な操作 | 作成、読取、更新、削除、検索 |
| 向いている領域 | CRUDアプリケーション、データ中心システム、社内ツール |
1.1 DAOの目的
DAOの目的は、データアクセスロジックを一箇所に集約し、業務ロジックから切り離すことです。たとえば、ユーザー登録処理の中にSQL文、接続処理、例外処理、結果セットの変換処理が混ざっていると、業務として何をしているのか分かりにくくなります。DAOを使えば、ユーザー登録処理はDAOのメソッドを呼び出すだけになり、データベース処理の詳細を隠せます。
また、DAOは変更に強い設計を作るためにも役立ちます。データベースのテーブル構造が変わった場合、SQLを修正する必要がありますが、DAOに処理が集約されていれば修正範囲を限定できます。業務ロジックの複数箇所にSQLが散らばっている場合と比べて、保守性は大きく向上します。このように、DAOの目的は単にコードを分けることではなく、変更しやすく、読みやすく、テストしやすい構造を作ることにあります。
1.2 DAOの動作イメージ
DAOは、アプリケーション層とデータベース層の間で動作します。アプリケーション層はDAOに対して「ユーザーを保存してほしい」「IDで商品を取得してほしい」「注文一覧を検索してほしい」と依頼します。DAOはその依頼を受け取り、SQLやORMを使ってデータソースへアクセスし、結果をアプリケーションへ返します。
この構造により、アプリケーション層はデータベースの実装を意識しにくくなります。RDBを使っているのか、NoSQLを使っているのか、ORMを使っているのか、JDBCやADO.NETを使っているのかを、業務ロジック側が直接知る必要はありません。DAOは、データアクセスの窓口として責務を引き受けることで、アプリケーション全体の依存関係を整理します。
| 層 | 役割 | DAOとの関係 |
|---|---|---|
| プレゼンテーション層 | UI、API、入力受付 | 通常はDAOを直接呼ばない |
| サービス層 | 業務処理、ユースケース | DAOを使ってデータを取得・保存する |
| DAO層 | データアクセスの隠蔽 | SQL、ORM、外部APIを扱う |
| データベース層 | データ保存 | DAOからアクセスされる |
1.3 DAOが重要になる場面
DAOが重要になるのは、データアクセス処理が複数箇所で繰り返される場合です。たとえば、ユーザー情報を取得するSQLが画面処理、バッチ処理、API処理にそれぞれ重複して書かれていると、仕様変更時にすべてを修正しなければなりません。DAOに集約すれば、同じデータ取得処理を再利用できます。
また、データアクセスと業務ロジックの分離は、テストにも効果があります。サービス層のテストでは、本物のデータベースではなく、テスト用DAOやモックを使って処理を検証できます。これにより、テストの実行速度を上げ、データベース状態に依存しないテストを書きやすくなります。DAOは、規模が大きくなるほど価値が見えやすくなる設計パターンです。
2. なぜDAOが必要なのか
DAOが必要になる理由は、データベースアクセスをアプリケーション全体に散らばらせないためです。小さなアプリケーションでは、画面処理やサービス処理の中にSQLを直接書いても動きます。しかし、機能が増え、テーブルが増え、検索条件が複雑になると、同じようなSQLが複数箇所に重複し、修正が難しくなります。
DAOは、データアクセス処理を一つの責務として切り出すことで、コード全体を整理します。業務ロジックは業務ルールに集中し、DAOはデータ取得や保存に集中します。この責務分離によって、保守性、再利用性、テスト容易性が向上します。
2.1 データベースへ直接アクセスする問題
データベースへ直接アクセスするコードが業務ロジック内に散らばると、コードベースはすぐに複雑になります。SQLが画面処理、サービス処理、バッチ処理に点在すると、テーブル名やカラム名が変わっただけで大量の修正が必要になります。さらに、似たような検索条件が複数箇所に重複し、バグの原因にもなります。
もう一つの問題は、テストの難しさです。業務ロジックが直接データベースに依存していると、テストのたびにデータベースを準備しなければなりません。テストデータの作成、削除、トランザクション管理が必要になり、単純なロジックの検証にも時間がかかります。DAOは、このような依存を一段分離することで、コードの見通しを改善します。
2.2 DAOが解決すること
DAOは、データアクセス処理をカプセル化します。カプセル化とは、内部の詳細を隠し、外側からは分かりやすい操作だけを提供することです。たとえば、サービス層は findById や save のようなメソッドを呼ぶだけで、内部のSQLやORM処理を意識せずに済みます。
また、DAOは再利用性を高めます。ユーザーをIDで取得する処理、商品を検索する処理、注文を保存する処理をDAOにまとめておけば、複数のサービスから同じ処理を使えます。これにより、重複コードが減り、変更時の影響範囲も小さくなります。DAOは「データアクセスの共通窓口」として、アプリケーション全体の構造を安定させます。
| 問題 | DAOによる解決 |
|---|---|
| SQLが散らばる | DAOに集約する |
| 業務ロジックが読みにくい | データアクセス処理を分離する |
| テストしにくい | DAOを差し替えやすくする |
| DB変更に弱い | 修正箇所をDAOに限定する |
| 重複クエリが増える | 共通処理として再利用する |
| ORM依存が広がる | DAO内に閉じ込める |
2.3 DAOが向いているシステム
DAOは、データ中心のアプリケーションに向いています。たとえば、管理画面、社内ツール、業務CRUDアプリケーション、マスタ管理、レガシー企業システムなどでは、テーブルやエンティティに対して作成、読取、更新、削除を行う処理が中心になります。このようなシステムでは、DAOによってデータアクセスを整理しやすくなります。
一方で、複雑な業務ルールを持つドメイン駆動設計のシステムでは、DAOよりもRepositoryの方が適している場合があります。DAOはデータソース中心の抽象化であり、Repositoryはドメインモデルや集約中心の抽象化です。どちらを使うかは、システムの性質によって判断する必要があります。
3. DAOパターンの構成
DAOパターンは、主にエンティティ、DAOインターフェース、DAO実装、データベースで構成されます。エンティティはアプリケーション内で扱うデータを表し、DAOインターフェースはデータ操作の契約を定義します。DAO実装は、SQLやORMを使って実際にデータベースへアクセスします。
この構成により、サービス層はDAOインターフェースに依存し、具体的なデータアクセスの詳細から切り離されます。たとえば、最初はSQLベースのDAO実装を使い、後からORMベースのDAO実装に変更する場合でも、インターフェースが安定していれば、サービス層への影響を抑えられます。
| 構成要素 | 役割 | 例 |
|---|---|---|
| エンティティ | データを表すオブジェクト | User, Product, Order |
| DAOインターフェース | データ操作の契約を定義 | UserDao, ProductDao |
| DAO実装 | 実際のデータアクセスを行う | JdbcUserDao, JpaUserDao |
| データベース | データを保存する場所 | PostgreSQL, MySQL, MongoDB |
| サービス層 | 業務処理を行う | UserService, OrderService |
3.1 エンティティ
エンティティは、アプリケーションで扱うデータのまとまりです。たとえば、ユーザー、商品、注文、請求、在庫などがエンティティとして表現されます。DAOは、これらのエンティティをデータベースから取得したり、データベースへ保存したりします。
ただし、DAOにおけるエンティティは、必ずしもドメイン駆動設計のリッチなドメインエンティティと同じではありません。DAOはデータアクセス中心のパターンであるため、エンティティがテーブル構造に近くなることもあります。複雑な業務ルールを持つ場合は、エンティティの設計とドメインモデルの設計を分けて考える必要があります。
3.2 DAOインターフェース
DAOインターフェースは、データ操作の契約を定義します。たとえば、ユーザーDAOであれば、ユーザーを作成する、IDで取得する、メールアドレスで検索する、更新する、削除する、といったメソッドを定義します。サービス層は、このインターフェースを通じてデータにアクセスします。
インターフェースを用意することで、実装を差し替えやすくなります。たとえば、本番環境ではSQLベースのDAOを使い、テストではメモリ上のDAOを使うことができます。このような差し替えが可能になるため、テスト容易性が高まります。DAOインターフェースは、データアクセスの詳細ではなく「何ができるか」を表すための設計要素です。
3.3 DAO実装
DAO実装は、実際にデータベースや外部データソースへアクセスする部分です。SQLを直接書く実装、ORMを使う実装、NoSQLを使う実装、外部APIを呼び出す実装などがあります。DAO実装には技術的な詳細が含まれますが、その詳細はサービス層から隠されます。
たとえば、JdbcUserDao はJDBCでSQLを実行し、JpaUserDao はJPAでエンティティを保存し、MongoUserDao はMongoDBへアクセスする、といった形です。インターフェースが同じであれば、利用側は実装の違いを意識せずに使えます。DAO実装は、変更されやすい技術詳細を閉じ込めるための場所です。
3.4 データベース
DAOがアクセスするデータベースは、リレーショナルデータベースだけではありません。MySQL、PostgreSQL、SQL Server、Oracle DatabaseのようなRDBに加えて、MongoDB、DynamoDB、FirestoreのようなNoSQL、ファイル、外部APIもデータソースになり得ます。
DAOの目的は、データソースの違いをアプリケーションから隠すことです。ただし、すべてのデータソースを完全に同じように扱えるわけではありません。RDBとNoSQLでは検索方法やトランザクションの性質が異なるため、DAO設計でもそれぞれの特性を考慮する必要があります。
4. DAOでよく使われるメソッド
DAOでよく使われるメソッドは、CRUDを中心に構成されます。CRUDとは、作成、読取、更新、削除を意味します。多くのデータ中心アプリケーションでは、この四つの操作が基本になります。さらに、検索、ページング、フィルタリング、件数取得、存在確認などのメソッドが追加されることもあります。
ただし、DAOに何でも入れればよいわけではありません。画面ごとの細かい表示条件や複雑な業務判断までDAOに入れると、DAOが肥大化します。DAOはデータアクセスに関する処理を担当し、業務判断はサービス層やドメイン層に置くのが基本です。
| メソッド種別 | 役割 | 例 |
|---|---|---|
| Create | データを作成する | insert, create, save |
| Read | データを取得する | findById, findAll |
| Update | データを更新する | update |
| Delete | データを削除する | deleteById |
| Search | 条件で検索する | findByEmail, searchByKeyword |
| Pagination | ページ単位で取得する | findPage |
| Filtering | 条件で絞り込む | findByStatus |
| Count | 件数を取得する | countAll |
| Exists | 存在確認を行う | existsById |
4.1 作成
作成メソッドは、新しいデータをデータベースに追加します。たとえば、ユーザー登録処理では、入力されたユーザー情報をエンティティに変換し、DAOの作成メソッドを通じて保存します。SQLベースであればINSERT文を実行し、ORMベースであれば永続化コンテキストにエンティティを保存します。
作成処理では、重複チェック、必須項目、生成ID、作成日時、監査ログなどをどう扱うかが重要です。ただし、業務ルールそのものをDAOに入れすぎるべきではありません。DAOは保存処理を担当し、業務的な入力検証やルール判断はサービス層やドメイン層で扱うのが自然です。
4.2 読取
読取メソッドは、データベースからデータを取得します。最も一般的なのは、IDによる取得です。たとえば、findById は指定されたIDのユーザーや商品を取得します。検索条件がある場合は、メールアドレス、ステータス、日付範囲、カテゴリなどで取得するメソッドを用意します。
読取処理では、存在しないデータをどう扱うかが重要です。nullを返すのか、Optionalを返すのか、例外を投げるのかは、言語やプロジェクトの方針に合わせて決めます。また、大量データを一度に取得しないよう、ページングや条件指定を適切に使う必要があります。
4.3 更新
更新メソッドは、既存データを変更します。ユーザー名の変更、商品の価格変更、注文ステータスの更新などが代表例です。SQLであればUPDATE文を実行し、ORMであればエンティティの状態変更を保存します。
更新処理では、同時更新や整合性に注意が必要です。複数ユーザーが同じデータを同時に更新する場合、楽観ロックや悲観ロックを検討することがあります。DAOは更新処理を実行しますが、更新してよいかどうかの業務判断はサービス層やドメイン層で行うべきです。
4.4 削除
削除メソッドは、データを削除します。物理削除としてレコードを完全に消す場合もあれば、論理削除として削除フラグを立てる場合もあります。業務システムでは、監査や復元のために論理削除を採用することが多くあります。
削除処理では、関連データへの影響を考える必要があります。ユーザーを削除したときに注文履歴をどうするのか、商品を削除したときに既存注文に影響するのか、といったルールがあります。DAOは削除操作を実行しますが、削除可能かどうかの判断は業務ロジック側で扱うべきです。
4.5 検索
検索メソッドは、条件に基づいてデータを取得します。メールアドレスでユーザーを探す、商品名で検索する、注文ステータスで絞り込む、日付範囲で取引を取得する、といった処理です。DAOでは、検索条件に応じたクエリを実装します。
検索条件が複雑になると、DAOメソッドが増えすぎることがあります。この場合、検索条件オブジェクトやクエリビルダーを使うこともあります。ただし、DAOが検索ロジックのすべてを抱え込みすぎると、巨大なDAOになりやすいため注意が必要です。
4.6 ページング
ページングは、大量データを少しずつ取得するための仕組みです。ユーザー一覧、商品一覧、注文履歴、ログ一覧などでは、一度に全件取得すると性能問題が発生します。DAOでは、ページ番号、件数、並び順を受け取り、必要な範囲だけを取得するメソッドを用意します。
ページング処理では、総件数の取得、ソート条件、インデックス設計が重要です。特に大規模データでは、OFFSETベースのページングが遅くなる場合があるため、カーソルベースのページングを検討することもあります。DAOは単にデータを取るだけでなく、性能を意識した取得方法も担当します。
4.7 フィルタリング
フィルタリングは、ステータス、カテゴリ、日付、ユーザー種別、権限などの条件でデータを絞り込む処理です。業務アプリケーションでは、一覧画面や管理画面でよく使われます。DAOでは、これらの条件を受け取り、適切なクエリへ変換します。
フィルタリング処理をDAOに入れる場合、条件が増えすぎないように設計する必要があります。画面固有の複雑な条件が多い場合は、DAOではなく専用のクエリサービスや検索サービスとして分離する方が管理しやすい場合があります。
5. DAOの実例
DAOの実例として分かりやすいのは、UserDAO、ProductDAO、OrderDAOです。これらは、それぞれユーザー、商品、注文に関するデータアクセスを担当します。多くのCRUDアプリケーションでは、このようにエンティティやテーブルに近い単位でDAOを作ることが一般的です。
ただし、DAOは便利な反面、エンティティごとに機械的に作ると数が増えすぎることがあります。特に大規模システムでは、DAOが大量に乱立し、似たようなクエリやメソッドが重複しやすくなります。設計時には、どの単位でDAOを作るのが自然かを考える必要があります。
| DAO例 | 主な役割 | 代表的なメソッド |
|---|---|---|
| UserDAO | ユーザー情報の取得・保存 | createUser, findById, findByEmail, updateUser |
| ProductDAO | 商品情報や在庫の取得 | findById, searchProducts, updateStock |
| OrderDAO | 注文情報の保存・検索 | createOrder, findById, findByUserId |
| InvoiceDAO | 請求情報の取得・保存 | findByOrderId, saveInvoice |
| LogDAO | ログデータの保存・検索 | insertLog, searchLogs |
5.1 UserDAO
UserDAOは、ユーザー情報に関するデータアクセスを担当します。ユーザー登録、IDによる取得、メールアドレスによる検索、プロフィール更新、削除、ログイン関連情報の取得などが代表的な処理です。多くのアプリケーションで最初に作られるDAOの一つです。
UserDAOを用意すると、ユーザーに関するSQLやORM操作を一箇所に集約できます。たとえば、メールアドレスでユーザーを検索する処理がログイン、パスワードリセット、管理画面で必要になったとしても、同じDAOメソッドを再利用できます。これにより、ユーザー関連のデータアクセスが整理されます。
5.2 ProductDAO
ProductDAOは、商品情報や在庫情報に関するデータアクセスを担当します。商品IDによる取得、キーワード検索、カテゴリ検索、価格帯検索、在庫数の更新などが代表的な処理です。ECサイトや在庫管理システムでは、ProductDAOの設計が性能や保守性に大きく関わります。
商品検索は条件が増えやすいため、ProductDAOが複雑になりやすい領域です。キーワード、カテゴリ、価格、在庫状態、公開状態、並び順などをすべて個別メソッドにすると、メソッド数が増えすぎます。この場合は、検索条件オブジェクトを使い、DAOの入口を整理することで扱いやすくできます。
5.3 OrderDAO
OrderDAOは、注文情報に関するデータアクセスを担当します。注文作成、注文詳細の取得、ユーザーごとの注文履歴、注文ステータスの更新、日付範囲による検索などが代表的な処理です。注文はユーザー、商品、支払い、配送など複数のデータと関係するため、DAO設計では関連データの扱いが重要になります。
OrderDAOでは、単純なCRUDだけでなく、集計や履歴取得も必要になることがあります。ただし、売上集計や分析のような複雑な読み取り処理は、通常のOrderDAOに詰め込みすぎない方がよい場合があります。用途に応じて、読み取り専用のDAOやクエリサービスに分けると、構造を保ちやすくなります。
6. DAOとORMの関係
DAOとORMは、同じものではありません。DAOはデータアクセスを隠蔽する設計パターンであり、ORMはオブジェクトとデータベースを対応付けるための技術です。DAOの内部でORMを使うことはできますが、ORMを使っているからといって必ずDAOが不要になるわけではありません。
現代の開発では、JPA、Hibernate、Entity Framework、Prisma、TypeORMなどのORMが広く使われています。これらのORMはデータアクセスを便利にしますが、ORMの使い方がサービス層やコントローラー層に直接広がると、アプリケーションが特定のORMに強く依存してしまいます。DAOは、その依存を抑えるための境界として機能します。
| 観点 | DAO | ORM |
|---|---|---|
| 種類 | 設計パターン | 技術・ライブラリ |
| 主な目的 | データアクセスを隠蔽する | DBとオブジェクトを対応付ける |
| 担当範囲 | データアクセスの入口を設計する | SQL生成、マッピング、永続化を支援する |
| 例 | UserDao, OrderDao | Hibernate, JPA, Entity Framework, Prisma |
| 関係 | ORMを内部で利用できる | DAOの実装手段になり得る |
6.1 ORMを使うDAO
ORMを使うDAOでは、SQLを直接書く代わりに、ORMのAPIを使ってデータを取得・保存します。たとえば、JPAであればEntityManagerやSpring Data JPA、Entity FrameworkであればDbContext、PrismaであればPrisma Clientを使ってデータアクセスを実装します。DAOは、それらのORM操作をサービス層から隠す役割を持ちます。
ORMを使うDAOの利点は、SQLの記述量を減らし、エンティティ中心にデータ操作を書けることです。一方で、ORM特有の遅延読み込み、N+1問題、トランザクション境界、変更追跡などを理解しないと、性能問題や予期しない挙動につながることがあります。DAOはORMを隠すだけでなく、ORMの使い方をプロジェクト内で統一する役割も持てます。
6.2 ORMがあってもDAOを使う理由
ORMがあると、簡単なCRUDは少ないコードで実装できます。そのため、小規模なアプリケーションでは、DAOを明示的に作らず、ORMのRepositoryやClientを直接使うこともあります。しかし、アプリケーションが大きくなると、ORMの呼び出しが複数のサービスに散らばり、変更時の影響範囲が広がることがあります。
DAOを使えば、ORMへの依存を一箇所に閉じ込めやすくなります。たとえば、将来的にSQLを手書きに変える、ORMのバージョン変更に対応する、一部の検索だけ最適化する、といった場合でも、DAO内の修正で済む可能性が高まります。ORMはデータアクセスを便利にする道具であり、DAOはデータアクセスの境界を作る設計です。
7. DAOとRepositoryパターンの違い
DAOとRepositoryは混同されやすい設計パターンです。どちらもデータの保存や取得に関わりますが、抽象化の視点が異なります。DAOはデータソースやテーブル寄りの抽象化であり、Repositoryはドメインモデルや集約寄りの抽象化です。
簡単に言えば、DAOは「データベースからどうデータを取るか」に近く、Repositoryは「ドメインオブジェクトの集合をどう扱うか」に近い考え方です。CRUD中心のアプリケーションではDAOが自然に使えますが、複雑な業務ルールやドメインモデルを重視する設計ではRepositoryが適している場合があります。
| 比較項目 | DAO | Repository |
|---|---|---|
| 主な視点 | データソース中心 | ドメインモデル中心 |
| 抽象化の対象 | テーブル、レコード、クエリ | 集約、ドメインオブジェクト |
| よく使う場面 | CRUD、管理画面、データ中心システム | DDD、複雑な業務システム |
| メソッド名 | findById, insert, update | save, find, removeなどドメイン寄り |
| 業務ルールとの距離 | 比較的遠い | 比較的近い |
| 実装の中身 | SQL、ORM、外部API | DAOやORMを内部で使うこともある |
7.1 DAOはデータアクセス中心
DAOは、データベースや外部データソースへのアクセスを中心に設計されます。たとえば、UserDAOはusersテーブル、ProductDAOはproductsテーブル、OrderDAOはordersテーブルに近い単位で作られることが多くあります。メソッド名も、findById、insert、update、deleteById のようにデータ操作を表す名前になりやすいです。
このようなDAOは、データ構造が比較的シンプルで、CRUD処理が中心のアプリケーションに向いています。管理画面、社内業務ツール、マスタ管理、レポート用データ取得などでは、DAOの方が直感的に設計しやすいことがあります。DAOは、実装寄りのデータアクセスを整理するための現実的なパターンです。
7.2 Repositoryはドメイン中心
Repositoryは、ドメインモデルをコレクションのように扱うためのパターンです。たとえば、注文という集約を保存する、顧客というドメインオブジェクトを取得する、契約という業務上のまとまりを永続化する、といった考え方になります。Repositoryは、テーブル単位ではなく、業務上の意味を持つモデル単位で設計されます。
Repositoryの内部では、DAOやORMを使ってデータアクセスを実装することもあります。つまり、RepositoryとDAOは完全に排他的なものではありません。Repositoryがドメイン層に近い抽象を提供し、その内側でDAOが実際のデータアクセスを担当する構成も可能です。重要なのは、どの層が何を責務として持つかを明確にすることです。
7.3 DAOとRepositoryの使い分け
DAOとRepositoryの使い分けは、アプリケーションの性質によって決まります。単純なCRUD中心のシステムでは、DAOだけで十分な場合があります。テーブル単位でデータを取得・保存し、サービス層で業務処理を行う構成であれば、DAOは分かりやすく実装しやすい選択肢です。
一方で、業務ルールが複雑で、ドメインモデルの整合性を重視するシステムでは、Repositoryを使う方が適している場合があります。Repositoryは、データベースの都合ではなく、業務上のまとまりを基準に設計できます。DAOとRepositoryの違いは名前の違いではなく、抽象化の向きの違いだと理解すると判断しやすくなります。
8. DAOのメリット
DAOのメリットは、データアクセスロジックを分離することで、コードの見通しを良くし、保守しやすくする点にあります。データベースに関する処理が一箇所にまとまるため、SQLやORMの変更、テーブル構造の変更、検索条件の調整などに対応しやすくなります。
また、DAOを使うことで、サービス層や業務ロジックがデータベースの詳細に依存しにくくなります。これは、テストのしやすさや再利用性にもつながります。DAOは特別に難しいパターンではありませんが、アプリケーションの成長に合わせて効果が大きくなる設計です。
8.1 保守性が高まる
DAOを使うと、データアクセスに関する処理が特定のクラスやモジュールに集約されます。これにより、テーブル名、カラム名、SQL文、ORMの呼び出しなどを修正する場所が分かりやすくなります。業務ロジックの中にSQLが散らばっている状態と比べると、変更時の確認範囲が小さくなります。
保守性が高いコードは、機能追加や仕様変更に強くなります。たとえば、ユーザー検索の条件を変更したい場合、UserDAOの該当メソッドを見ればよい構造になっていれば、修正が素早く安全に行えます。DAOは、将来の変更を見越してデータアクセスの責務を整理するための手段です。
8.2 テストしやすくなる
DAOをインターフェースとして定義しておくと、サービス層のテストで本物のデータベースを使わずに済む場合があります。テスト用のDAO実装やモックを用意すれば、サービス層のロジックだけを検証できます。これにより、テストの実行速度が上がり、失敗原因も特定しやすくなります。
特に、業務ロジックのテストでは、データベース接続やSQLの正しさではなく、条件分岐や計算、状態遷移を確認したいことが多くあります。DAOを分離しておけば、データアクセスの詳細を切り離してテストできます。DAOは、単体テストと統合テストの役割を分けるためにも役立ちます。
8.3 再利用性が高まる
DAOに共通のデータ取得処理をまとめることで、複数のサービスや画面から同じ処理を再利用できます。たとえば、ユーザーをメールアドレスで検索する処理がログイン、通知、管理画面で使われる場合、UserDAOに findByEmail を用意しておけば重複を避けられます。
再利用性が高まると、コード量が減るだけでなく、仕様変更時の修正漏れも防ぎやすくなります。同じようなSQLが複数箇所にあると、一部だけ修正されずに不整合が起きることがあります。DAOは、データアクセスの再利用単位を明確にすることで、コードベース全体を安定させます。
9. DAOのデメリット
DAOには多くのメリットがありますが、使い方を誤ると逆に設計が複雑になることがあります。特に、小さなアプリケーションで過剰にインターフェースや実装を分けると、ファイル数が増え、実装を追いにくくなることがあります。DAOは便利なパターンですが、常に必要とは限りません。
また、DAOに何でも入れてしまうと、巨大なDAOが生まれます。検索、集計、画面専用クエリ、業務判断、外部API連携などをすべて一つのDAOに詰め込むと、責務が曖昧になります。DAOを使う場合は、どこまでをDAOの責務にするかを意識する必要があります。
9.1 クラスやファイルが増える
DAOを導入すると、エンティティ、DAOインターフェース、DAO実装、テスト用実装など、関連するファイルが増えることがあります。小規模なアプリケーションでは、この構成がやや重く感じられる場合があります。単純なCRUDだけであれば、フレームワーク標準のRepositoryやORM機能で十分なこともあります。
重要なのは、DAOを形式だけで導入しないことです。データアクセスが複雑でない段階では、過剰な抽象化が開発速度を下げる可能性があります。DAOは、重複や変更の負担が見えてきたときに価値を発揮します。プロジェクトの規模やチームの運用に合わせて、必要な粒度で導入することが大切です。
9.2 DAOが肥大化しやすい
DAOはデータアクセスをまとめる場所であるため、放置すると多くのメソッドが集まりやすくなります。たとえば、UserDAOにユーザー検索、権限検索、ログイン履歴、通知設定、管理画面用の集計まで入れてしまうと、DAOの役割が広がりすぎます。これにより、DAO自体が読みにくくなります。
DAOが肥大化した場合は、責務を分割することが必要です。通常のCRUD用DAO、検索専用DAO、集計用クエリサービス、読み取りモデル用DAOなどに分けることで、複雑さを抑えられます。DAOはデータアクセスを整理するためのパターンであり、すべてのデータ関連処理を無制限に入れる場所ではありません。
9.3 ORM機能と重複することがある
近年のORMやフレームワークは、標準でRepositoryやデータアクセス抽象を提供していることがあります。たとえば、Spring Data JPA、Entity Framework、Prisma、TypeORMなどを使う場合、基本的なCRUDはフレームワーク側で簡単に実装できます。その上に独自DAOを重ねると、同じような役割が重複する場合があります。
ただし、重複するからDAOが不要とは限りません。ORMのAPIをアプリケーション全体に直接広げたくない場合、DAOを境界として置く価値があります。大切なのは、DAOが何を隠し、何を統一し、どの層から使われるのかを明確にすることです。目的が曖昧なDAOは、単なるラッパーになってしまいます。
10. DAOの現代的な使い方
現代的な開発では、DAOは単独で使われるだけでなく、ORM、Repository、Service Layer、Clean Architecture、DDDなどと組み合わせて使われます。昔ながらのN層アーキテクチャではDAOがデータアクセス層の中心になることが多く、近年の設計ではRepositoryやGatewayの内側にDAO的な実装が置かれることもあります。
重要なのは、DAOという名前にこだわることではなく、データアクセスの責務をどこに置くかを明確にすることです。ORMを直接使う場合でも、サービス層にクエリが散らばるなら、DAOやRepositoryのような境界を設ける価値があります。DAOは、現代のバックエンド開発でも十分に意味のある考え方です。
10.1 レイヤードアーキテクチャでのDAO
レイヤードアーキテクチャでは、アプリケーションをプレゼンテーション層、サービス層、データアクセス層、データベース層のように分けます。この構成において、DAOはデータアクセス層に配置されます。サービス層はDAOを呼び出し、DAOはデータベースや外部データソースにアクセスします。
この構成の利点は、責務が分かりやすいことです。APIや画面は入力と出力を担当し、サービス層は業務処理を担当し、DAOはデータアクセスを担当します。チーム開発でも役割を分けやすく、保守しやすい構造になります。DAOは、レイヤードアーキテクチャにおける代表的なデータアクセスの実装パターンです。
10.2 Clean ArchitectureでのDAO
Clean Architectureでは、依存関係を内側のビジネスルールへ向けることが重視されます。この場合、DAOは外側のインフラ層に配置されることがあります。ユースケース層やドメイン層は、DAOの具体実装ではなく、抽象インターフェースに依存します。
この設計では、DAOはデータベース技術の詳細を隠す実装として扱われます。たとえば、ユースケース層が UserRepository インターフェースに依存し、その実装として UserDao や JpaUserRepository が存在する形です。DAOは、アーキテクチャの外側でデータアクセスを担当し、内側の業務ルールを技術詳細から守ります。
10.3 マイクロサービスでのDAO
マイクロサービスでは、各サービスが自分のデータを管理することが一般的です。このとき、各サービス内部でデータアクセスを整理するためにDAOを使うことがあります。たとえば、ユーザーサービスにはUserDAO、注文サービスにはOrderDAO、請求サービスにはInvoiceDAOのような形です。
ただし、マイクロサービスでは他サービスのデータベースを直接参照しない設計が重要です。DAOはあくまで自サービスのデータソースに対するアクセスを担当し、他サービスとの連携はAPIやメッセージングを通じて行うべきです。DAOはサービス内部のデータアクセスを整理するためのものであり、サービス間の境界を越えるための仕組みではありません。
11. DAO設計のベストプラクティス
DAOを効果的に使うには、責務を明確にし、過剰に複雑化させないことが重要です。DAOはデータアクセスを担当する層であり、業務判断や画面表示の都合まで抱え込むべきではありません。DAOの設計が曖昧になると、サービス層との境界が崩れ、結果として保守しにくいコードになります。
また、DAOはプロジェクトの規模に応じて適切な粒度で導入する必要があります。小規模なアプリケーションではシンプルなDAOで十分ですが、大規模なシステムでは読み取り専用DAO、検索専用DAO、集計用クエリサービスなどに分けることが有効です。DAOは一度作れば終わりではなく、アプリケーションの成長に合わせて見直すべき設計要素です。
11.1 DAOに業務ロジックを入れすぎない
DAOには、データを取得する、保存する、更新する、削除する、といったデータアクセスに関する処理を置きます。一方で、割引計算、権限判定、注文可能かどうかの判断、承認フローの制御などは業務ロジックであり、DAOに入れるべきではありません。これらはサービス層やドメイン層で扱うのが自然です。
DAOに業務ロジックを入れすぎると、データアクセス層がアプリケーションの中心になってしまいます。すると、業務ルールの変更時にDAOを修正する必要が増え、責務が不明確になります。DAOは「データをどう扱うか」を担当し、サービス層やドメイン層は「そのデータを使って何を判断するか」を担当するべきです。
11.2 メソッド名を分かりやすくする
DAOのメソッド名は、何を取得し、何を保存し、どの条件で検索するのかが分かる名前にする必要があります。たとえば、getData や process のような曖昧な名前では、利用側が意図を理解しにくくなります。findById、findByEmail、save、updateStatus のように、目的が伝わる名前を使うことが重要です。
メソッド名が分かりやすいと、サービス層のコードも読みやすくなります。サービス層を読んだときに、DAOがどのデータを扱っているのかが自然に理解できるためです。DAOは内部のSQLを隠しますが、外部に提供するメソッドの意図は明確でなければなりません。
11.3 トランザクション境界を意識する
DAOを設計するときは、トランザクションをどこで管理するかを決める必要があります。一般的には、DAO単体ではなく、サービス層やユースケース層でトランザクションを管理することが多くあります。なぜなら、一つの業務処理で複数のDAOを呼び出すことがあるからです。
たとえば、注文作成では、OrderDAOで注文を保存し、ProductDAOで在庫を更新し、PaymentDAOで支払い情報を保存するかもしれません。このような処理では、どれか一つだけ成功して他が失敗すると不整合が起きます。そのため、DAOごとではなく、業務処理全体を一つのトランザクションとして扱う設計が重要です。
11.4 読み取りと書き込みを分ける
大規模なアプリケーションでは、読み取り処理と書き込み処理を分けると設計しやすくなる場合があります。通常のDAOに検索、一覧、集計、CSV出力用データ取得などをすべて入れると、DAOが肥大化します。読み取り専用のDAOやクエリサービスを作ることで、責務を分離できます。
特に、一覧画面やレポート画面では、複数テーブルを結合した表示専用データが必要になることがあります。このような処理をエンティティ単位のDAOに入れると、不自然な設計になる場合があります。読み取り用のモデルや専用クエリを用意することで、通常のCRUDと複雑な検索を分けて管理できます。
11.5 フレームワーク標準機能と役割を重複させない
Spring Data JPA、Entity Framework、Prisma、TypeORMなどのフレームワークは、標準でデータアクセスを支援する機能を持っています。これらを使う場合、DAOを追加することで本当に設計が良くなるのかを確認する必要があります。単にフレームワークのメソッドを一行呼ぶだけのDAOであれば、価値が薄い場合があります。
一方で、ORM依存を隠したい、クエリを統一したい、テストしやすくしたい、将来的な変更に備えたい、といった明確な理由があるならDAOを置く価値があります。DAOは「何となく作るもの」ではなく、依存関係や責務を整理するために作るものです。フレームワークの機能とDAOの役割を整理してから導入することが大切です。
12. DAOを使うべきか判断するポイント
DAOを使うべきかどうかは、アプリケーションの規模、データアクセスの複雑さ、チームの設計方針によって変わります。DAOは有用な設計パターンですが、すべてのプロジェクトで必須ではありません。小規模なアプリケーションでは、ORMやフレームワーク標準のRepositoryで十分な場合もあります。
一方で、データアクセスが複雑になり、複数のサービスから同じ処理を使い、SQLやORMの呼び出しが広がっている場合は、DAOを導入する価値があります。DAOは、コードの整理だけでなく、将来的な変更に備えるための境界として機能します。
| 判断ポイント | DAOが向いている状態 | DAOが不要な場合 |
|---|---|---|
| アプリ規模 | 中規模以上 | 小規模な試作 |
| データアクセス | 複雑・重複が多い | 単純なCRUDのみ |
| チーム開発 | 責務分離が必要 | 少人数で短期開発 |
| ORM依存 | 隠したい | 直接使っても問題ない |
| テスト | モックや差し替えが必要 | DB込みの簡易テストで十分 |
| 将来変更 | DBやORM変更の可能性がある | 技術選定が固定されている |
12.1 DAOを使うべきケース
DAOを使うべきなのは、データアクセスの重複や複雑さが見えている場合です。複数のサービスで同じようなSQLを書いている、ORMの呼び出しがサービス層に散らばっている、検索条件が増えて保守しにくい、といった状況ではDAOが役立ちます。DAOに処理を集約することで、変更箇所を限定できます。
また、テスト容易性を重視する場合もDAOは有効です。サービス層のテストでデータベースを直接使いたくない場合、DAOインターフェースを差し替えることでテストを簡単にできます。特に、業務ロジックが複雑なシステムでは、DAOによってデータアクセスとロジックを切り分ける価値が高くなります。
12.2 DAOを使わなくてもよいケース
DAOを使わなくてもよいのは、アプリケーションが小さく、データアクセスが単純で、フレームワークの標準機能だけで十分に保守できる場合です。たとえば、短期の試作、小さな管理画面、単純なCRUDだけのAPIでは、DAOを作ることでかえって構造が重くなることがあります。
また、既にフレームワーク標準のRepositoryが十分な抽象を提供している場合も、独自DAOを追加する必要はないかもしれません。ただし、将来的にビジネスロジックが増えたり、データアクセスが複雑になったりする見込みがあるなら、早めに境界を設計しておくことも選択肢になります。
おわりに
DAOとは、データアクセスロジックを業務ロジックから分離するための設計パターンです。DAOを使うことで、SQL、ORM、データベース接続、検索条件、結果変換などの詳細を一箇所に集約できます。その結果、コードの保守性、再利用性、テスト容易性が向上します。
一方で、DAOは万能ではありません。小規模なアプリケーションでは過剰な抽象化になることがあり、ORMやフレームワーク標準のRepositoryと役割が重複する場合もあります。重要なのは、DAOという名前を使うことではなく、データアクセスの責務を適切な場所に置くことです。
DAOとRepositoryの違いを理解し、ORMとの関係を整理し、サービス層やドメイン層との責務分担を明確にすれば、より保守しやすいバックエンド設計ができます。DAOは古い考え方ではなく、現代のアプリケーション開発でも、データアクセスを整理するための実用的な設計パターンです。
EN
JP
KR