システム開発(2年)の第36週は、設計したデータベースを実際に作り、アプリケーションから操作できる形へ進める実習でした。
前週に作成したER図とSQLを出発点に、DDLの実行、DAOの設計と実装、Service層との連携、ミニ動作テストまでを一つの流れとして確認しました。
第36週の到達点
今回の実習では、データベースとアプリケーションの間にある処理を、役割ごとに分けて実装しました。
- DDLを実行し、テーブルと制約が設計どおりに作られたか確認する
- DAOにデータベース操作を集め、CRUDの処理を整理する
- SQLの値をプレースホルダで渡し、安全な問い合わせの基本を押さえる
- コミットとロールバックを含むトランザクションの境界を決める
- DAO、Service、UIの責務を分け、テストしやすい構造を考える
実習前に押さえた用語
用語の関係を先に整理すると、実装のどの部分を確認しているのか判断しやすくなります。
| 用語 | この授業での意味 |
|---|---|
| DDL | テーブルや制約など、データベースの構造を定義するSQLです。 |
| マイグレーション | DDLを実行して、データベースの構造を必要な状態へ作成または変更する作業です。今回はテーブル作成が中心でした。 |
| DAO | Data Access Objectの略で、SQLの実行や検索結果の取得など、データベースへのアクセスを担当する層です。 |
| CRUD | Create、Read、Update、Deleteの頭文字で、データの作成、読み取り、更新、削除を表します。 |
| トランザクション | 関連するデータベース操作を一つのまとまりとして扱い、成功時はコミットし、失敗時はロールバックする考え方です。 |
DDLを実行してテーブルと制約を確かめる
各班は、前週に作成したDDLを学習環境で実行し、テーブルが期待した構造になっているかを確認しました。
作成できたかどうかだけで終わらせず、制約とデータの関係まで試すことが今回の確認点です。
確認した五つの項目
- 主キー(PK):各行を識別する値が正しく設定されているか
- 外部キー(FK):関連するテーブル同士の参照関係が設定されているか
- NOT NULL、UNIQUE、CHECK:空値、重複、条件外の値を制約できるか
- サンプルINSERT:想定したデータを登録できるか
- JOIN:複数テーブルから必要なデータを期待どおりに取得できるか
実行結果を確認する中で、外部キーの設定漏れや制約の不足が見つかりました。
設計図を眺めるだけでは気づきにくい点も、実際にデータを登録し、検索すると確認できます。
マイグレーションを発展的に学ぶ場合は、DBマイグレーションの作成、適用、ロールバックの手順も参考になります。
DAOにデータベース操作を集める
続いて、図書館システムの書籍データを扱うBooksDaoを例に、DAOのインターフェースを検討しました。
画面や業務ルールの中へSQLを散在させず、データベース操作の窓口をDAOに集めることが狙いです。
設計した基本メソッド
get(book_id):IDを指定して一冊の書籍を取得するfind_by_title(keyword):タイトルの条件に合う書籍を探すcreate(book):書籍データを登録するupdate(book):既存の書籍データを更新するdelete(book_id):IDを指定して書籍データを削除する
メソッド名だけでなく、引数、返り値、データが見つからない場合の扱いまで決めると、DAOを呼び出す側の実装が曖昧になりません。
学習用の実装例
class BooksDao:
def __init__(self, conn):
self.conn = conn
def get(self, book_id):
cur = self.conn.cursor()
cur.execute(
"SELECT id, title, author, is_available FROM books WHERE id = %s",
(book_id,),
)
return cur.fetchone()
def create(self, title, author):
cur = self.conn.cursor()
try:
cur.execute(
"INSERT INTO books (title, author) VALUES (%s, %s) RETURNING id",
(title, author),
)
new_id = cur.fetchone()[0]
self.conn.commit()
return new_id
except Exception:
self.conn.rollback()
raise
このコードは、授業で扱った内容を示すための簡略例です。
掲載例は%s形式のプレースホルダを使っているため、SQLiteを含む別の接続ライブラリで試す場合は、そのライブラリに合う記法へ読み替えます。
プレースホルダとトランザクションの役割
プレースホルダは、SQLの命令部分と値を分けて渡す仕組みです。
検索IDや書籍名を文字列の連結でSQLへ埋め込まず、実行メソッドの引数として渡すことが、SQLインジェクション対策の基本になります。
トランザクションでは、処理が完了したときにコミットし、途中で例外が発生したときにロールバックします。
DAOの各メソッドで完結させるのか、複数のDAO操作をまとめるService側で管理するのかを決め、境界を統一することが設計上の確認点です。
ログについては、SQLや入力値を無制限に記録するのではなく、原因調査に必要な情報をそろえながら、取り扱いに注意すべき値を残さない方針を確認しました。
DAO、Service、UIの責務を分ける
Service層は、貸出可能かの判定や延滞時の処理など、アプリケーションの業務ルールを担当します。
UIからDAOを直接呼ぶのではなく、Serviceを間に置き、各層が担当する判断を分けました。
| 層 | 主な役割 | 今回の例 |
|---|---|---|
| DAO | データベースの読み書き | 書籍の取得、登録、更新、削除 |
| Service | 業務ルールと処理の順序 | 貸出可否の確認、延滞時の処理、検索条件の検証 |
| ControllerまたはUI | 入力の受け取りと結果の表示 | 検索語の受け取り、一覧やエラーの表示 |
この分け方により、SQLを変更する場面と業務ルールを変更する場面を区別して考えられます。
テーブルからServiceまでを通して動かす
最後に、個別に作った部品を次の順序でつなぎ、ミニ動作テストを行いました。
- サンプルデータをテーブルへ登録する
- DAOでデータを読み取る
- Serviceで業務ルールを適用する
- 返された結果を確認する
例外が発生した班は、ログを手がかりに原因を探し、その場で修正しました。
特に確認が必要だったのは、外部キーによる参照関係と、NULLを許可しない列へ渡す値です。
レビューで見えた次の改善点
- DAOの責務を保ち、SQLを一か所に整理できていた
- 例外時にロールバックする流れを実装できていた
- 返り値の型や、データがない場合の返し方を明確にする余地があった
- Serviceへ処理を集めすぎず、業務上の役割ごとに整理する必要があった
宿題と次週の実装
今回の宿題は、実装したクラスとユースケースの対応を図にして、次週の結合へ備える内容です。
- 各班のDAOクラス一覧とメソッド表を作成する
- DAOとServiceの簡易クラス図を一枚にまとめる
- 主要なユースケースを一つ選び、処理フロー図を作成する
次週はDAOとServiceをWebまたはCLIのUIへ接続し、ユースケース単位で動かします。
続きは、第37週のDAO、Service、UIをつなぐ実習で紹介しています。
第36週の振り返り
第36週では、ER図とDDLで表した設計を、DAOとServiceを通じて動く処理へ変えました。
データベース操作と業務ルールを分け、例外が起きた場合の戻し方まで決めることで、各コードが何を担当するのかを説明できる状態に近づきました。
この記事に関連する株式会社greedenの取り組み
DAOとServiceの責務を分ける学びは、保守しやすいWebシステム開発の土台になります。株式会社greedenは、要件定義からDBやAPIの設計、実装、テスト、保守運用まで一気通貫で支援しています。

