前提となる構成
上段は画面の流れ(View↔Controller→Model)。下段は業務フロー(UseCase)と、設定/依存(DI)と、抽出の根拠(SQLテンプレート)。
ベース(MVC)
既存の業務システムでは、MVCに近い構造(Controller / Model / View)が採用されていることが多いです。 ここでは「画面の入口」「表示」「永続化」の責務をまず分け、変更の影響を限定できる状態を作ります。
- View:テンプレート(フォーム/一覧/エラー表示)
- Model:ORM/ActiveRecordなど(DB操作)
- Controller:入力受付と画面制御
拡張(UseCase + DI)
バックオフィス機能は例外や条件追加が増えやすく、ControllerやViewに業務判断を書き始めると保守が難しくなります。 そこで「業務フロー」をまとめる UseCase(業務ロジック層) を置き、依存や設定はDI(YAML等)で管理します。
- UseCase:業務手順(抽出/判定/整形)を集約
- DI:設定値/差し替え点を明示し、変更を安全にする
- SQLテンプレート:抽出条件の根拠を管理しやすくする
全体アーキテクチャ
Controllerは薄く、業務フローはUseCaseへ。抽出条件はSQLテンプレートに寄せて差分が追える形にします。
このアーキテクチャの狙いは「変更を局所化」することです。Controllerを薄く保ち、業務フローはUseCaseに集約し、DB操作はModelに寄せます。 抽出条件はSQLテンプレートとして切り出しておくことで、条件追加・微調整・チューニングの影響を最小化できます。
- 「どこに何を書くか」を固定して迷子を防ぐ
- 業務ルールはUseCaseに集約して拡張の軸を作る
- SQLテンプレートで抽出条件を安定化し差分を追いやすくする
- バックオフィス中心(抽出・出力・運用画面)
- 例外と条件追加が多い業務領域
- レガシー基盤を活かして段階的に改善したいケース
各レイヤーの責務
Controller
Controllerは「入口」と「画面制御」を担当します。処理の中心を持たせず、入力の受け取りとバリデーションを行い、 UseCaseへ渡して結果を表示します。CSV出力のようにレスポンス制御が必要な機能はControllerが担当するのが自然です。
- リクエスト受信、入力整理
- バリデーション(必須/形式)
- UseCase呼び出し
- テンプレートへデータ渡し
- CSVダウンロードのヘッダ付与・出力
View(Template)
Viewは表示専用です。業務判断やDBアクセスを持たせると、「表示条件」と「業務条件」が混ざって改修難易度が上がります。 テンプレートはフォームと一覧表示に専念し、条件分岐は必要最小限に留めます。
- 抽出条件フォーム
- 抽出結果の一覧・件数表示
- バリデーションエラーの表示
- CSV出力ボタン
UseCase(業務ロジック層)
UseCaseはユースケース(業務フロー)の中核です。抽出条件を受け取り、SQLテンプレートを実行して候補を取得し、 対象外ルールを適用したうえでCSV出力用のデータに整形します。ここに業務ルールを集約することで、仕様追加や例外対応が局所化します。
- 抽出(SQL実行)
- 対象外適用(対象外Model/参照SQL)
- CSV列定義・フォーマット整形(列順/0埋め/結合等)
- 例外・エラーハンドリングの統一
※帳票生成や履歴登録は、UseCaseを拡張ポイントとして後から積み上げられます。
Model(ORM / ActiveRecord)
ModelはDB操作に専念させます。対象外マスタの一覧取得・追加・削除など、永続化の詳細を隠蔽し、 UseCase側は「何をしたいか」だけを記述できるようにします。業務ルール(重複扱い、削除可否など)はUseCase側で決めると責務が明確になります。
- 対象外マスタの参照/追加/削除(推奨:論理削除)
- 永続化の詳細(テーブル/カラム)を隠蔽
- UseCaseが必要とする最小I/Fを提供
SQLテンプレート
抽出条件は複雑化しやすく、条件追加や微調整も頻繁に発生します。SQLテンプレートとして切り出しておくと、 改修時の差分が明確になり、チューニングもしやすくなります。UseCase側はSQLを呼び出して結果を加工する役に徹します。
DIと命名規約の併用
DIは Dependency Injection(依存性注入)の略です。設定値や差し替え可能な依存(外部I/F、実装の切り替え点など)を、 コンテナ(設定)側から注入して管理しやすくします。
- 外部I/Fクライアント(API/通知/ストレージ等)
- 設定値が必要なもの(URL/timeout/リトライ)
- 将来差し替えたい実装(帳票生成、出力方式など)
- 単純CRUD中心のModel
- 差し替えが不要なユーティリティ
- フレームワーク側で自動解決される範囲
実務では「差し替えが必要なもの・設定が必要なものはDIへ」「単純CRUDは命名規約運用でもOK」のバランスが扱いやすいです。 ただし暗黙依存が増えすぎると追跡性が下がるため、重要な依存は明示する運用がおすすめです。
データ抽出・CSV出力の設計例
要件(例)
- 条件に基づいて対象データを抽出し、CSVとして出力する
- 対象外エンティティ(マスタ)を管理画面から参照/追加/削除する
- 帳票(PDF等)は将来拡張
- 操作履歴の登録も将来拡張
処理フロー(安全側)
CSV出力は、プレビュー表示の結果をそのまま出すのではなく、 出力時に同条件で再抽出 するのがおすすめです(画面改ざんやセッション差分による事故を減らせます)。
1) 画面で条件入力 → UseCaseが抽出 → 対象外適用 → プレビュー表示
2) CSV出力アクション → 同条件で再抽出(安全)
3) UseCaseがCSV行データを生成(列順/フォーマット統一)
4) ControllerがCSVとしてダウンロード返却(header付与・必要に応じて文字コード変換)- ファイル名規則(例:YYYYMMDD)
- 文字コード(Shift_JIS/UTF-8)
- 改行(CRLF推奨)
- ヘッダ行の有無
- 対象外を含めるか(別CSVにするか)
- CSV行データ生成はUseCase、HTTP出力はController
- 対象外適用はUseCase(材料の取得はModel/SQL)
- 抽出条件はSQLテンプレートに寄せ、差分を追いやすくする
対象外マスタの管理(追加/削除)
対象外マスタは、参照だけでなく追加・削除が運用上必要になることが多いです。 追加/削除は「Modelだけでも実装できる」一方で、重複登録・削除の扱い(復活させたい等)・権限・監査といった要件が増えがちです。 そのため責務としては Model=CRUD、UseCase=業務ルール の分担が安全です。
- 一覧取得(有効な対象外)
- 追加(登録 or 再有効化)
- 削除(推奨:論理削除)
- 重複時の扱い(エラー/無視/再有効化)
- 削除可否(運用ルールに従う)
- メッセージ・例外の統一
- 将来の監査/履歴要件への拡張
早期はModelに「insertOrReactivate」「deactivate」のような運用を見据えたI/Fを用意し、 ルールはUseCaseに寄せると、要件が増えた際にも崩れにくくなります。
将来拡張(帳票生成・履歴管理)
まずは「抽出」「対象外適用」「CSV出力」を安定させると、その後の拡張(帳票生成・操作履歴)が積み上げやすくなります。 最初から全部を同時に作ると、要件が揺れた時に設計が崩れやすいので、段階的に進めるのが安全です。
- ReportGeneratorサービスを追加
- DIで設定/実装を注入(テンプレ・フォーマット等)
- UseCaseから呼び出し(I/F固定)
- 履歴テーブル(ヘッダ/明細)
- 抽出条件の保存(再現性)
- 作成者/作成日時など監査項目
拡張は「UseCaseに機能を追加する」形で積み上げると、影響範囲が読みやすくなります。
この構成が向いているケース
- バックオフィス中心の業務システム(抽出、データ出力、運用画面)
- 例外対応や運用ルールの追加が多い領域
- レガシー基盤を活かしつつ段階的に改善したい
- 「どこに何を書くか」を固定して保守性を上げたい
- 設計〜実装〜検証の境界を明確にしたい
まとめ
本ページでは、レガシーMVC基盤の上でも「責務を分けて変更を局所化する」ことで、バックオフィス機能を安全に拡張していくための設計パターンを整理しました。 重要なのは、最新の技術を詰め込むことではなく 変更に強い構造を先に作る ことです。
Controllerは薄く、業務フローはUseCaseに集約し、ModelはCRUDに専念。DIは差し替えや設定が必要な依存に絞り、 命名規約・autoloadと併用して、レガシー基盤でも安全に拡張できるバックオフィスを作る。