ArchitectureBackofficeMVCDIUseCaseCSV

安全に拡張できるバックオフィス設計— レガシーMVC基盤に「業務ロジック層」を追加する設計パターン —

バックオフィス(管理画面)系の業務システムでは、「動く」ことよりも 壊れない・説明できる・後から変更できる ことが重要になります。運用で例外が増えやすく、条件追加や手順変更も発生しやすい領域だからです。

本ページでは、NEBULABが実務で重視している「責務を明確に分離し、変更を局所化する」ための設計の型を、 特定の案件・顧客・プロダクトに依存しない形で整理します。

想定読者:業務システムの開発・保守に関わるエンジニア/Tech Leadテーマ:責務分離・DI運用・拡張設計

前提となる構成

前提:2段構成(上:MVC / 下:UseCase・DI・SQL)

上段は画面の流れ(View↔Controller→Model)。下段は業務フロー(UseCase)と、設定/依存(DI)と、抽出の根拠(SQLテンプレート)。

画面フロー(MVC)業務/設定/抽出(下段)
上段:MVC(画面の流れ)View(Template)入力フォーム / 一覧表示エラー表示テンプレートController入力整理 / バリデーションCSVなどレスポンス制御Model(ORM / ActiveRecord)CRUD / 取得 / 永続化DB操作の集約下段:UseCase / DI / SQL(業務・設定・抽出)UseCase(業務ロジック層)抽出 → フィルタ → 整形例外/メッセージ統一・拡張の軸DI(YAML設定 / 注入)設定値 / 差し替え点の管理環境差分・実装差分を吸収SQLテンプレート抽出SQL / 参照SQL差分管理・調整を明確化ルール:画面の入口はController、業務判断はUseCase、永続化はModel、条件はSQLテンプレで差分を追う。

ベース(MVC)

既存の業務システムでは、MVCに近い構造(Controller / Model / View)が採用されていることが多いです。 ここでは「画面の入口」「表示」「永続化」の責務をまず分け、変更の影響を限定できる状態を作ります。

  • View:テンプレート(フォーム/一覧/エラー表示)
  • Model:ORM/ActiveRecordなど(DB操作)
  • Controller:入力受付と画面制御

拡張(UseCase + DI)

バックオフィス機能は例外や条件追加が増えやすく、ControllerやViewに業務判断を書き始めると保守が難しくなります。 そこで「業務フロー」をまとめる UseCase(業務ロジック層) を置き、依存や設定はDI(YAML等)で管理します。

  • UseCase:業務手順(抽出/判定/整形)を集約
  • DI:設定値/差し替え点を明示し、変更を安全にする
  • SQLテンプレート:抽出条件の根拠を管理しやすくする

全体アーキテクチャ

全体像:画面(MVC)→ 業務(UseCase)→ 永続化(Model/DB)

Controllerは薄く、業務フローはUseCaseへ。抽出条件はSQLテンプレートに寄せて差分が追える形にします。

画面フロー業務/永続化フロー
上段:画面(MVC)View(Template)フォーム / 一覧 / エラー表示Controller入力整理 / バリデーション / 出力制御下段:業務(UseCase)/ 永続化(Model・SQL)UseCase(業務ロジック層)抽出 → 対象外適用 → 整形(CSV行生成など) / 例外とメッセージの統一拡張点:帳票生成・操作履歴などを後から積み上げるModelCRUDSQLTemplatesControllerは「入口」と「出力制御」だけに寄せる抽出条件はSQLテンプレで差分管理しやすくポイント:業務ルールはUseCaseに集約し、永続化はModel/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、実装の切り替え点など)を、 コンテナ(設定)側から注入して管理しやすくします。

DIで管理しやすい依存
  • 外部I/Fクライアント(API/通知/ストレージ等)
  • 設定値が必要なもの(URL/timeout/リトライ)
  • 将来差し替えたい実装(帳票生成、出力方式など)
命名規約・autoloadで運用しやすい領域
  • 単純CRUD中心のModel
  • 差し替えが不要なユーティリティ
  • フレームワーク側で自動解決される範囲

実務では「差し替えが必要なもの・設定が必要なものはDIへ」「単純CRUDは命名規約運用でもOK」のバランスが扱いやすいです。 ただし暗黙依存が増えすぎると追跡性が下がるため、重要な依存は明示する運用がおすすめです。

データ抽出・CSV出力の設計例

要件(例)

  • 条件に基づいて対象データを抽出し、CSVとして出力する
  • 対象外エンティティ(マスタ)を管理画面から参照/追加/削除する
  • 帳票(PDF等)は将来拡張
  • 操作履歴の登録も将来拡張

処理フロー(安全側)

CSV出力は、プレビュー表示の結果をそのまま出すのではなく、 出力時に同条件で再抽出 するのがおすすめです(画面改ざんやセッション差分による事故を減らせます)。

1) 画面で条件入力 → UseCaseが抽出 → 対象外適用 → プレビュー表示
2) CSV出力アクション → 同条件で再抽出(安全)
3) UseCaseがCSV行データを生成(列順/フォーマット統一)
4) ControllerがCSVとしてダウンロード返却(header付与・必要に応じて文字コード変換)
CSVで決めておくと良い項目
  • ファイル名規則(例:YYYYMMDD)
  • 文字コード(Shift_JIS/UTF-8)
  • 改行(CRLF推奨)
  • ヘッダ行の有無
  • 対象外を含めるか(別CSVにするか)
実装上のポイント
  • CSV行データ生成はUseCase、HTTP出力はController
  • 対象外適用はUseCase(材料の取得はModel/SQL)
  • 抽出条件はSQLテンプレートに寄せ、差分を追いやすくする

対象外マスタの管理(追加/削除)

対象外マスタは、参照だけでなく追加・削除が運用上必要になることが多いです。 追加/削除は「Modelだけでも実装できる」一方で、重複登録・削除の扱い(復活させたい等)・権限・監査といった要件が増えがちです。 そのため責務としては Model=CRUD、UseCase=業務ルール の分担が安全です。

Modelが担当(CRUD)
  • 一覧取得(有効な対象外)
  • 追加(登録 or 再有効化)
  • 削除(推奨:論理削除)
UseCaseが担当(業務ルール)
  • 重複時の扱い(エラー/無視/再有効化)
  • 削除可否(運用ルールに従う)
  • メッセージ・例外の統一
  • 将来の監査/履歴要件への拡張

早期はModelに「insertOrReactivate」「deactivate」のような運用を見据えたI/Fを用意し、 ルールはUseCaseに寄せると、要件が増えた際にも崩れにくくなります。

将来拡張(帳票生成・履歴管理)

まずは「抽出」「対象外適用」「CSV出力」を安定させると、その後の拡張(帳票生成・操作履歴)が積み上げやすくなります。 最初から全部を同時に作ると、要件が揺れた時に設計が崩れやすいので、段階的に進めるのが安全です。

帳票生成(後日)
  • ReportGeneratorサービスを追加
  • DIで設定/実装を注入(テンプレ・フォーマット等)
  • UseCaseから呼び出し(I/F固定)
履歴管理(後日)
  • 履歴テーブル(ヘッダ/明細)
  • 抽出条件の保存(再現性)
  • 作成者/作成日時など監査項目

拡張は「UseCaseに機能を追加する」形で積み上げると、影響範囲が読みやすくなります。

この構成が向いているケース

  • バックオフィス中心の業務システム(抽出、データ出力、運用画面)
  • 例外対応や運用ルールの追加が多い領域
  • レガシー基盤を活かしつつ段階的に改善したい
  • 「どこに何を書くか」を固定して保守性を上げたい
  • 設計〜実装〜検証の境界を明確にしたい

まとめ

本ページでは、レガシーMVC基盤の上でも「責務を分けて変更を局所化する」ことで、バックオフィス機能を安全に拡張していくための設計パターンを整理しました。 重要なのは、最新の技術を詰め込むことではなく 変更に強い構造を先に作る ことです。

一文で言うと

Controllerは薄く、業務フローはUseCaseに集約し、ModelはCRUDに専念。DIは差し替えや設定が必要な依存に絞り、 命名規約・autoloadと併用して、レガシー基盤でも安全に拡張できるバックオフィスを作る。

NEBULABの設計支援

既存基盤を活かしつつ、保守性の高い構成へ段階的に移行する設計も支援できます。