2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

MVCアーキテクチャってなんだ?AIエージェント時代に蘇る45年前の設計思想

2
Posted at

ねらい

CursorやClaude Codeといった「AIエージェント」が当たり前になった今、なぜか古典的なMVCアーキテクチャの理解が急務になっている。その理由と実践的な活用法を解説する。

対象

  • AIコーディングツールを使い始めたが、生成されるコードが「なんか散らかってる」と感じている人
  • Cursor RulesやCLAUDE.mdの設定で何を書けばいいかわからない人
  • 「設計」という言葉にアレルギーがあるが、そろそろ向き合わないとまずいと思っている人

ゴール

この記事を読み終えると、MVCの基本概念を理解し、AIエージェントに「きれいなコード」を書かせるための設定ファイルを自分で書けるようになる。

TL;DR

MVCは「データ」「見た目」「操作」を分離する設計パターン。1978年にXerox PARCで生まれた。AIエージェントにコードを書かせる際、この分離原則をルールファイルに明記することで、保守性の高いコードが生成されるようになる。記事後半でCursor RulesとCLAUDE.mdの具体的な設定例を紹介する。


AIにコードを書かせて、カオスになっていませんか

正直に告白しよう。

僕がCursorを使い始めた頃、生成されるコードは動くけど「なんか気持ち悪い」ものばかりだった。APIの呼び出しとHTMLの生成が同じファイルに同居している。データベースのクエリがUIコンポーネントの中に埋まっている。動くは動くんだけど、1週間後に見返すと何がどこにあるのかさっぱりわからない。

最初は「AIの限界だな」と思っていた。でも違った。

問題は僕が「どういう構造で書いてほしいか」を一切伝えていなかったことにある。人間の新人エンジニアに仕事を頼むとき、「適当に書いて」とは言わないだろう。プロジェクトの構成、コーディング規約、設計方針を説明するはずだ。

AIエージェントも同じ。いや、むしろ明示的に伝えないと、彼らは「動くコード」を最短距離で生成しようとする。結果、スパゲッティの山ができあがる。

じゃあ何を伝えればいいのか?

その答えの一つが、45年前に生まれた設計思想——MVCアーキテクチャだ。


MVCの誕生:1978年、Xerox PARCにて

ノルウェーの造船業から生まれた発想

MVCの生みの親は、ノルウェーのコンピュータ科学者Trygve Reenskaug(トリグヴェ・リーンスカウ)。

彼は1978年から1979年にかけて、Xerox PARC(パロアルト研究所)に客員研究員として滞在していた。当時のPARCは、GUIやマウスといった革新的な技術が次々と生まれていた伝説的な場所だ。

Reenskaug本人の言葉を引用しよう。

I made the first implementation and wrote the original MVC reports while I was a visiting scientist at Xerox Palo Alto Research Laboratory (PARC) in 1978/79. MVC was created as an obvious solution to the general problem of giving users control over their information as seen from multiple perspectives.

(私は1978/79年にXerox PARCの客員研究員として、MVCの最初の実装とオリジナルのレポートを作成しました。MVCは、ユーザーが複数の視点から自分の情報をコントロールできるようにするという一般的な問題に対する、ある意味で当然の解決策として生まれました)

出典: Trygve/MVC

面白いのは、MVCのルーツがソフトウェアではなく造船業にあったこと。Reenskaugはノルウェーで船の設計に関わる情報システムを構築しており、そこで「複雑なデータを様々な視点から見せる」という課題に直面していた。

最初の名前は「THING-MODEL-VIEW-EDITOR」という長ったらしいものだったが、Adele Goldberg(Smalltalkの共同開発者)との議論を経て「Model-View-Controller」に落ち着いた。

MVCの本来の目的

ここで重要なのは、MVCが単なる「コードの整理術」ではないということ。

Reenskaugはこう述べている。

The essential purpose of MVC is to bridge the gap between the human user's mental model and the digital model that exists in the computer.

(MVCの本質的な目的は、ユーザーの頭の中にあるメンタルモデルと、コンピュータ内に存在するデジタルモデルとの間のギャップを埋めることです)

つまりMVCは「人間とコンピュータの橋渡し」のための設計思想なのだ。


MVCを料理で理解する

抽象的な話が続いたので、具体例で説明しよう。

レストランの厨房をイメージしてほしい。

Model(モデル)= 食材と冷蔵庫

Modelは「データそのもの」と「データを操作するロジック」を担当する。

レストランで言えば、食材を保管している冷蔵庫と、その在庫管理システム。トマトが何個あるか、賞味期限はいつか、新しい食材が入荷したら記録する。客席から直接見えることはないが、レストランの根幹を支えている。

プログラミングでは、データベースとの連携、ビジネスロジック、データのバリデーションがここに入る。

View(ビュー)= 料理の盛り付けと提供

Viewは「見た目」「表示」を担当する。

同じステーキでも、高級店ならソースを芸術的にあしらい、ファミレスならシンプルに皿に載せる。食材(Model)は同じでも、見せ方(View)で印象は変わる。

プログラミングでは、HTMLテンプレート、UIコンポーネント、CSSがここに該当する。

Controller(コントローラー)= ホールスタッフ

Controllerは「ユーザーの入力を受け取り、適切な処理を振り分ける」係だ。

客が「ステーキをミディアムレアで」と注文したら、ホールスタッフがそれを厨房に伝え、できあがった料理を客のテーブルに届ける。客が直接厨房に入ることはないし、シェフが客席に出てくることもない。すべてホールスタッフが仲介する。

プログラミングでは、ルーティング、リクエストの受け取り、レスポンスの返却を担当する。

なぜ分けるのか

「全部一人でやればいいじゃん」と思うかもしれない。小さな屋台なら確かにそうだ。

でも規模が大きくなると破綻する。シェフが接客しながら在庫管理もやっていたら、料理の質は落ちるし、客は待たされるし、在庫は合わなくなる。

役割を分けることで、

  • シェフは料理に集中できる(Modelの独立性)
  • 盛り付け担当は見た目に集中できる(Viewの独立性)
  • ホールは客対応に集中できる(Controllerの独立性)

それぞれが自分の仕事に専念でき、問題が起きたときも「どこが悪いか」が明確になる。


コードで見るMVC

具体的なコードで見てみよう。シンプルなTodoアプリを例にする。

MVCなし(すべてが混在したカオス)

# chaos.py - すべてが一つのファイルに混在
import sqlite3
from flask import Flask, render_template_string

app = Flask(__name__)

@app.route('/')
def index():
    # データベース接続(Model的な処理)
    conn = sqlite3.connect('todos.db')
    cursor = conn.cursor()
    cursor.execute('SELECT * FROM todos')
    todos = cursor.fetchall()
    conn.close()
    
    # HTMLの生成(View的な処理)
    html = '''
    <html>
    <head><title>Todo</title></head>
    <body>
    <h1>Todoリスト</h1>
    <ul>
    '''
    for todo in todos:
        # ビジネスロジック(Model的な処理)もここに混在
        status = "完了" if todo[2] else "未完了"
        html += f'<li>{todo[1]} - {status}</li>'
    
    html += '</ul></body></html>'
    return html

一見シンプルに見えるが、このコードには問題が山積みだ。

データベースの種類を変えたい?HTML全体を書き直すことになる。デザインを変えたい?ビジネスロジックに触れずにはいられない。テストを書きたい?どこからテストすればいいのかわからない。

MVC適用後(責務が分離された状態)

# models/todo.py - データとロジックのみ
import sqlite3

class TodoModel:
    def __init__(self, db_path='todos.db'):
        self.db_path = db_path
    
    def get_all(self):
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        cursor.execute('SELECT id, title, completed FROM todos')
        todos = cursor.fetchall()
        conn.close()
        return [
            {'id': t[0], 'title': t[1], 'completed': t[2]}
            for t in todos
        ]
    
    def get_status_text(self, completed):
        return "完了" if completed else "未完了"
# views/todo_view.py - 表示ロジックのみ
def render_todo_list(todos, get_status_func):
    items = ''.join(
        f'<li>{todo["title"]} - {get_status_func(todo["completed"])}</li>'
        for todo in todos
    )
    return f'''
    <html>
    <head><title>Todo</title></head>
    <body>
    <h1>Todoリスト</h1>
    <ul>{items}</ul>
    </body>
    </html>
    '''
# controllers/todo_controller.py - 仲介役のみ
from flask import Flask
from models.todo import TodoModel
from views.todo_view import render_todo_list

app = Flask(__name__)
model = TodoModel()

@app.route('/')
def index():
    todos = model.get_all()
    return render_todo_list(todos, model.get_status_text)

ファイルは増えた。でも得られるものは大きい。

  • Modelのテストはデータベース処理だけを検証すればいい
  • Viewを変更してもModelに影響しない
  • 新しいエンドポイントを追加するときはControllerだけ触ればいい

AIエージェント時代になぜMVCが重要か

さて、ここからが本題だ。

AIエージェント(Cursor、Claude Code、GitHub Copilotなど)は、与えられた文脈から「最も確率の高いコード」を生成する。設計方針を伝えなければ、彼らは動くコードを最短距離で書こうとする

結果、最初に見せた「カオス」なコードが量産される。

逆に言えば、設計方針を明確に伝えれば、AIは驚くほどきれいなコードを書いてくれる。

Cursor Rules:AIへの指示書

CursorにはRulesという機能がある。プロジェクトごとにAIへの指示を書いておくと、コード生成時に自動で参照してくれる。

公式ドキュメントによると、

Rules provide persistent, reusable context at the prompt level. When applied, rule contents are included at the start of the model context. This gives the AI consistent guidance for generating code, interpreting edits, or helping with workflows.

(ルールはプロンプトレベルで永続的かつ再利用可能なコンテキストを提供します。適用されると、ルールの内容はモデルコンテキストの先頭に含まれます。これにより、AIはコード生成、編集の解釈、ワークフローの支援において一貫したガイダンスを得られます)

出典: Cursor Docs - Rules

CLAUDE.md:Claude Codeの設定ファイル

Claude CodeにはCLAUDE.mdという特殊なファイルがある。

Anthropicの公式ベストプラクティスによると、

CLAUDE.md is a special file that Claude automatically pulls into context when starting a conversation. This makes it an ideal place for documenting: Common bash commands, Core files and utility functions, Code style guidelines, Testing instructions, Repository etiquette

(CLAUDE.mdは、Claude Codeが会話を開始する際に自動的にコンテキストに取り込む特殊なファイルです。ここにはbashコマンド、コアファイル、コードスタイルガイドライン、テスト手順、リポジトリの作法などを記載するのに最適です)

出典: Claude Code: Best practices for agentic coding


実践:AIエージェント向けMVCルールの書き方

では実際に設定ファイルを書いてみよう。

Cursor Rules(.cursor/rules/architecture.mdc)

---
description: MVCアーキテクチャのガイドライン
globs:
  - "**/*.py"
  - "**/*.js"
  - "**/*.ts"
---

# アーキテクチャ原則

このプロジェクトはMVCアーキテクチャに従う。

## ディレクトリ構造

src/
├── models/ # データとビジネスロジック
├── views/ # 表示・UIコンポーネント
├── controllers/ # リクエスト処理・ルーティング
└── utils/ # 共通ユーティリティ


## 責務の分離(厳守)

### Model
- データベースアクセスはここだけ
- ビジネスロジック(計算、バリデーション)はここだけ
- ViewやControllerをimportしてはならない
- 外部APIの呼び出しもここで行う

### View
- HTML/JSXのレンダリングのみ
- Modelから受け取ったデータを表示するだけ
- データベースに直接アクセスしてはならない
- ビジネスロジックを含めてはならない

### Controller
- リクエストを受け取り、適切なModelを呼び出す
- Modelの結果をViewに渡す
- ビジネスロジックを書かない(Modelに委譲)
- SQLを直接書かない(Modelに委譲)

## アンチパターン(絶対に避ける)

- ViewコンポーネントにSQL文を書く
- Controllerでデータの加工処理を行う
- ModelからViewを直接操作する
- 1つのファイルに複数の責務を混在させる

## コード例

良い例:
```python
# controller
def get_users():
    users = UserModel.get_active_users()  # Modelに委譲
    return render_template('users.html', users=users)  # Viewに渡す

悪い例:

# controller内でSQLを直接実行(NG)
def get_users():
    conn = sqlite3.connect('db.sqlite')
    users = conn.execute('SELECT * FROM users WHERE active=1').fetchall()
    return f'<ul>{"".join(f"<li>{u[1]}</li>" for u in users)}</ul>'

### CLAUDE.md(Claude Code用)

```markdown
# プロジェクトガイドライン

## アーキテクチャ

このプロジェクトはMVCアーキテクチャを採用。

### ディレクトリ構成
- `src/models/` - データモデルとビジネスロジック
- `src/views/` - UIコンポーネントとテンプレート
- `src/controllers/` - ルーティングとリクエスト処理

### 重要な原則
1. Modelはデータベースアクセスとビジネスロジックのみ
2. ViewはModelから受け取ったデータの表示のみ
3. ControllerはModelとViewの仲介のみ
4. 各層は他の層に依存しすぎない

### 新しいファイルを作成する際
- どの層(M/V/C)に属するか必ず判断すること
- 適切なディレクトリに配置すること
- 他の層の責務を侵さないこと

## Bashコマンド

- `npm run dev` - 開発サーバー起動
- `npm run test` - テスト実行
- `npm run lint` - リンター実行

## コードスタイル

- TypeScript/ESモジュール構文を使用
- 関数は単一責務を守る
- エラーハンドリングは必ず行う

より高度な設定例(ドメイン駆動設計との組み合わせ)

大規模プロジェクトでは、MVCだけでなくドメイン駆動設計(DDD)の概念も取り入れると効果的だ。

---
description: 高度なアーキテクチャガイドライン
globs:
  - "src/**/*"
---

# アーキテクチャガイドライン

## レイヤー構成(依存の方向は内側へ)

src/
├── domain/ # ドメインモデル(最も内側)
│ ├── entities/ # エンティティ
│ ├── valueObjects/ # 値オブジェクト
│ └── repositories/ # リポジトリインターフェース
├── application/ # アプリケーションサービス
│ ├── usecases/ # ユースケース
│ └── dto/ # データ転送オブジェクト
├── infrastructure/ # 外部サービス連携
│ ├── database/ # DB実装
│ └── api/ # 外部API
└── presentation/ # UI層(最も外側)
├── controllers/ # コントローラー
└── views/ # ビュー


## 依存関係のルール

### 許可される依存
- presentation → application → domain
- infrastructure → domain(インターフェース経由)

### 禁止される依存
- domain → infrastructure(逆方向)
- domain → presentation(逆方向)
- presentation → infrastructure(直接参照)

## ユースケース実装パターン

```typescript
// application/usecases/CreateUserUseCase.ts
export class CreateUserUseCase {
  constructor(
    private userRepository: IUserRepository,  // 依存性注入
    private emailService: IEmailService
  ) {}

  async execute(input: CreateUserInput): Promise<CreateUserOutput> {
    // 1. バリデーション(ドメインルール)
    const email = Email.create(input.email);  // 値オブジェクト
    
    // 2. ビジネスロジック
    const user = User.create({ email, name: input.name });
    
    // 3. 永続化(リポジトリ経由)
    await this.userRepository.save(user);
    
    // 4. 副作用(サービス経由)
    await this.emailService.sendWelcome(user.email);
    
    return { userId: user.id };
  }
}

テスト戦略

  • domain: 単体テスト(外部依存なし)
  • application: 統合テスト(モック使用)
  • infrastructure: E2Eテスト
  • presentation: UIテスト

---

## AIエージェントを「教育」する

Cursor RulesやCLAUDE.mdを書くことは、AIエージェントを「教育」することに等しい。

新人エンジニアにプロジェクトの方針を説明するように、AIにも丁寧に伝える必要がある。一度設定を書いてしまえば、以降の開発ではAIが自動的にその方針に従ってコードを生成してくれる。

Anthropicの公式ブログでも、こう述べられている。

> Claude Code is intentionally low-level and unopinionated, providing close to raw model access without forcing specific workflows. This design philosophy creates a flexible, customizable, scriptable, and safe power tool.
>
> (Claude Codeは意図的にローレベルで意見を押し付けず、特定のワークフローを強制せずにモデルへのほぼ生のアクセスを提供します。この設計哲学により、柔軟でカスタマイズ可能、スクリプト化可能で安全なパワーツールが生まれます)

出典: [Claude Code: Best practices for agentic coding](https://www.anthropic.com/engineering/claude-code-best-practices)

つまり、AIエージェントは白紙のキャンバスのようなもの。どんな絵を描くかは、指示を出す僕たちにかかっている。

---

## まとめ:45年前の知恵を現代に活かす

MVCは1978年に生まれた設計パターンだが、その本質——「責務の分離」「人間とコンピュータの橋渡し」——は45年経った今でも色褪せない。

AIエージェント時代において、この古典的な知恵は新しい価値を持つ。

- AIに「どう書くか」を伝えることで、保守性の高いコードが生成される
- Cursor RulesやCLAUDE.mdという仕組みで、その指示を永続化できる
- 一度設定すれば、プロジェクト全体で一貫した設計方針が維持される

最後に、Reenskaugの言葉を改めて引用しよう。

> MVC was created as an obvious solution to the general problem of giving users control over their information as seen from multiple perspectives.

「ユーザーが自分の情報を複数の視点からコントロールできるようにする」——この思想は、AIとコードを書く今日においても、まったく同じ重要性を持っている。

AIエージェントは強力なツールだ。でも、どう使うかを決めるのは僕たちだ。古典を学び、現代に応用する。それがエンジニアリングの醍醐味じゃないだろうか。

---

## 参考リンク

- [Trygve/MVC - オリジナルのMVC解説](https://folk.universitetetioslo.no/trygver/themes/mvc/mvc-index.html)
- [The original MVC reports (PDF)](https://www.duo.uio.no/handle/10852/9621)
- [Cursor Docs - Rules](https://cursor.com/docs/context/rules)
- [Claude Code: Best practices for agentic coding](https://www.anthropic.com/engineering/claude-code-best-practices)
- [awesome-cursorrules - Cursor Rules集](https://github.com/PatrickJS/awesome-cursorrules)
2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?