PHPの開発で最初に学んだのは「MVCモデル」(Model-View-Controller)。
MVCはモデル(データ・ビジネスロジック)、ビュー(画面表示)、コントローラ(入力処理・調整役)に役割を分ける有名なアーキテクチャ。
こういった考え方って一つじゃないよな。
と思って調べたらまあ出てきたもんで、それをチョットだけまとめていこうと思ったわけです。
プロジェクトの性質や規模に応じて代表的なアーキテクチャパターンを5種類下記にまとめていきます。
⸻
1. 三層アーキテクチャ(Layered Architecture)
┌───────────────┐ プレゼンテーション層
│ View/UI │
└───────────────┘
│
┌───────────────┐ ビジネスロジック層
│ Application │
│ Service │
└───────────────┘
│
┌───────────────┐ データアクセス層
│ Repository / │
│ Gateway │
└───────────────┘
典型的な三層アーキテクチャの概念図(プレゼンテーション層〜データベース層)
三層アーキテクチャはソフトウェアを大きく
- プレゼンテーション層(表示やUI)
- ビジネスロジック層(業務処理)
- データ層(データ永続化)
の層(レイヤー)に分割する構造。
さらに細かく4層(プレゼンテーション層、ビジネス層、永続化層、データベース層)に分類されることもあるらしい。
各層は基本的に自分の一つ下の層にのみアクセス。
隣接しない層同士は直接やりとりはしない。
全体をシンプルに理解できるため実装も比較的容易。
PHPのWebアプリでも、ビュー -> コントローラ(UI層) -> モデル(ビジネス層) -> データベースというオーソドックスな形。
メリット:
高い凝集度、低い結合度
層が明確に別れていて、特に隣接しない層間で結びつきが弱い設計にできる。
ある層の内部変更が他層に波及しにくくなり保守性が高い傾向。
理解しやすく初学者向き
アプリケーションを「入力・処理・データ保存」という流れで段階分けするので、直感的に理解しやすい。
小規模な単一構成システムではこの方式で設計すると把握しやすい。
デメリット:
層を飛び越えた設計崩れ
原則以外の使い方をするとデメリットが顕在化する。
ビジネス層で処理すべきことをプレゼンテーション層から直接データ層に渡すような実装を混入させてしまうと層間の独立性が崩れる。
これを陥没穴(sinkhole)アンチパターンと呼ぶ。
大規模システムには非効率...らしい
大規模になると各層内のコード量が膨大になる。
そのため変更のたびに全体をデプロイ(再配備)し直す必要が出てくる。
スケーラビリティ(拡張性)やチーム開発の効率の面で限界が出てくる。
2. モデル・ビュー・プレゼンター(MVP)
ユーザー入力
↓
┌────────┐ ┌────────────┐
│ View │───▶︎│ Presenter │
└────────┘◀───└────────────┘
▲ │
└───────────────┘
モデル更新 / 取得
┌───────┐
│ Model │
└───────┘
MVP(Model-View-Presenter)はMVCを発展させたアーキテクチャパターン。
主にGUIを持つアプリケーションの設計で使われる。
プレゼンターと呼ばれる部分がUIロジックの仲介役を担う。
MVPではビューからの入力イベントをプレゼンターが受け取り、必要に応じてモデルのデータを変更または取得する。
取得したデータをプレゼンターが加工してビューに渡す点が特徴。
※MVCはユーザーからの入力をコントローラが受け取りモデルを更新しビューに反映。
ビューの表示ロジックを可能な限りプレゼンター側に移すことで、ビューを「受け身(Passive)」な存在にする。
画面ロジックとデータ処理が分離されるのでテストもしやすくなる。
実装のイメージを簡単に示すと、ユーザー操作 -> View(イベント発行)-> Presenter(ロジック処理)-> Model(データ変更)という一連の流れになる。
さらにModelから得た結果をPresenterが受け取ってViewに表示を指示する。
メリット:
柔軟でテストしやすい設計
プレゼンターが徹底してビューとモデルを分離する。
なのでUIの見た目とビジネスロジックを独立に開発・変更ができる。
MVCに比べ関心ごとの分離がより徹底されているため、長期的な保守や機能拡張が容易になる。
特にビューとロジックが疎結合になる。そのため自動テストでプレゼンターの挙動を確認しやすいという利点がある。
視覚要素とデータ処理の独立:
アプリケーションの画面部分(HTMLテンプレートやJavaScriptなど)に複雑な処理を書かずに済む。
プレゼンターが画面表示用のデータを用意するのでビュー側はそれを表示するだけのシンプルなコードになる。
結果としてビューの再利用性が高まり、デザイナーとプログラマの分業も用意になる。
デメリット:
実装の手間と複雑さの増大:
画面ごとにプレゼンターを用意するケースが多く、コードの量(クラスやファイル数)がMVCより増えがちになる。
シンプルな画面であってもプレゼンター経由でモデルを操作する構成になるので小規模なプロジェクトではかえって過剰設計_オーバーエンジニアリングになる可能性がある。
双方向データバインディング非対応:
ビューとモデルの間にプレゼンターが入るので、ビューがモデルの変更を自動的に監視してリアルタイム更新するようなデータバインディング機能は基本的にサポートされない。
そのため、プレゼンターの実装次第ではMVC以上にコードが煩雑になる。
3. クリーンアーキテクチャ(Clean Architecture)
+-------------------------+
| Frameworks / Drivers |
| (外部技術) |
+------------▲------------+
│
+---------------------------------------+
| Interface Adapters |
| (Controller / Presenter / Gateway) |
+--------------------▲------------------+
│
+------------------------------+
| Use-Cases |
| (ユースケース層) |
+--------------▲---------------+
│
+------------------+
| Entities |
| (ドメイン) |
+------------------+
円で表現ができなかった。Entitiesを囲むようにUse-Cases。
それを囲むようにInterface Adaptersと、囲まれていく。
ソフトウェアのビジネスルール(業務ロジック)をフレームワークやUI、データベースといった外部要素から独立させ、高い保守性を保つことを目指した設計手法。
提唱者のロバート・C・マーチン(通称アンクルボブ)の著書『Clean Architecture』で広まった。
その中で提示された同心円状のレイヤー構造はヘキサゴナルアーキテクチャ(Hexagonal, 六角形)やオニオンアーキテクチャ(Onion, 玉ねぎ)といった従来のパターンの思想も取り込んでいる。
中心にエンティティ(Entities)やユースケース(Use Cases)など純粋なビジネスロジック層を置き、外側にインターフェースアダプタ層(コントローラやプレゼンター、データアクセスの実装など)、さらにその外にフレームワークやデータベースといったインフラ層が位置する。
依存方向は常に内側(ビジネスルール側)に向かうよう規定されており、UIやデータベースが変わっても中心のドメインロジックへの影響を最小限に留められる。
メリット:
高い保守性・拡張性:
層ごとに役割がはっきり分かれている。
核心であるビジネスロジックが外部要因に影響されない。
これにより要件変更や技術のアップデートにも柔軟に対応できる。
例えばWebフレームワーク(Laravelなど)から別のフレームワークに変える場合でもビジネスロジック層が独立していれば比較的移行が容易になる。
テストのしやすさ:
ビジネスロジックがインフラ層と分離されているため、自作のドメインオブジェクトやユースケースを単体テストしやすい。
外側のインフラをモック(代替)に差し替えて純粋なロジック部分のみ検証できるので自動テストによる品質向上に向いている。
モジュール性とチーム開発効率
層ごとに関心ごとが分離されているためチーム内で担当を分けやすくなる。
たとえばUI担当者とドメインロジック担当者が並行して作業できる。
互いのコードに干渉しにくい。
異なる層間はインターフェースでやり取りするため実装を差し替えても影響範囲が小さい。
デメリット:
実装が複雑になりがち:
層を厳格に分けるので依存関係も制御するために大量のクラスやインターフェース、ファイルが必要になる。
小規模プロジェクトでこれを採用すると「フォルダとファイルばかり増えて全体像が見えない…」という事態になりかねない。
実際、慣れていない開発者にとっては学習コストも高く、正しく実装するまで時間を要する。
初期開発のスピード低下:
基本的なCRUD処理程度の簡単なアプリであればクリーンアーキテクチャを最初から導入するとむしろ開発が遅くなる場合がある。
各層間のデータ受け渡しのためのボイラープレートコード(繰り返し記述する定型コード)が増えることが原因。
規模や期間が小さいプロジェクトでは、シンプルなMVCやレイヤードアーキテクチャで十分。
クリーンアーキテクチャはオーバーキル(やりすぎ)と言われることもあるらしい。
4. マイクロサービスアーキテクチャ
┌─────────────┐
│ Client │
└─────────────┘
│ REST / gRPC
┌─────────────────────┐
│ API Gateway │
└─────────────────────┘
┌─────────┐ ┌─────────┐ ┌─────────┐
│Service A│ │Service B│ │Service C│ …(独立デプロイ)
└─────────┘ └─────────┘ └─────────┘
│ DB_A │ DB_B │ DB_C
アプリケーションを複数の独立したサービス(小さなアプリ)の集合として構築するアプローチ。
従来の単一のモノリシックなアプリケーション(典型的にはMVC+単一データベースの構成など)や、あるいは以前のSOA(サービス指向アーキテクチャ)の流れを発展させたもの。
それぞれのサービスが独立した機能を持ち、可能な限り他のサービスに依存しない。
サービス間は軽量なAPI(RESTなどのWeb API)やメッセージングキュー(非同期通信)を介して連携。
例えばECサイトを開発する場合はユーザ管理、商品カタログ、在庫、注文処理、支払い処理といった機能を別々のサービスとして開発・デプロイする。
それぞれが独自にスケール(負荷に応じて増強)したり、異なる技術スタックを採用することも可能。
メリット:
サービスごとの独立開発・デプロイ:
各サービスは他とゆるく連携するだけなので、チームごとに異なるサービスを並行開発できる。
ある機能のデプロイが他部分に影響しにくいのでリリースの頻度を上げやすくなる。
スケーラビリティと信頼性向上:
必要な部分だけ水平方向にスケールアウト(サーバ増強)可能。
例えば商品カタログの閲覧が集中するなら商品サービスだけ複数インスタンス起動すればよく、全体を複製する必要がなくなる。
また一つのサービスが障害を起こしても、他のサービスは独立して動作するためシステム全体への影響を抑えられる(フォールトアイソレーション)。
技術選択の自由度:
サービス間が疎結合なので、サービスごとに最適な言語・フレームワーク・データストレージを選択できる。
極端な例ではあるサービスはPHP+MySQL、別のサービスはNode.js+MongoDB、といった異なる技術を組み合わせることも可能。
デメリット:
システム全体の複雑性増大:
アプリケーションが分散システムになるため、サービス間通信やデータの一貫性確保など考慮すべき事項が格段に増える。
APIゲートウェイの用意やサービス発見(ディスカバリ)、分散トレーシング等、インフラ面での追加投資も必要になる。
単純な機能追加でも複数サービスにまたがる変更になる場合があり、開発者に分散システムの知識が求められる。
サービス境界の設計が難しい:
どの単位で機能をサービスに分割するか(サービスの粒度)を決めるのは経験を要する難しい部分。
粒度が細かすぎるとサービス間呼び出しが頻発してネットワーク負荷が高くなったり、デプロイ管理も煩雑になる(かつてのSOAが直面した問題)。
逆に粗すぎると結局従来のモノリシックと変わらなくなり、マイクロサービス化のメリットが出ない。
適切な分割と統合のバランスを見極める必要がある。
デプロイ・運用のコスト:
サービスの数が増えるほど、コンテナ管理やCI/CDパイプライン、監視やログ集約など運用面の負荷が高まる。
それぞれのサービスに対してバージョン管理や依存ライブラリの更新も個別に行う必要があり、小規模チームには荷が重くなる。
5. マイクロカーネルアーキテクチャ(プラグインアーキテクチャ)
┌────────────────┐
│ Core System │ ← 必要最小限の機能
└────────────────┘
▲ ▲ ▲
│ │ │
┌────────┴┐ ┌───┴────┐ ┌────────┴┐
│ Plugin 1│ │Plugin 2│ │ Plugin 3│ …(あとから追加/削除)
└─────────┘ └────────┘ └─────────┘
システムの中核(コア)部分と、そこに動的に追加できるプラグイン群に分けて構築するアーキテクチャ。
主にパッケージソフトウェア(製品)や、プラグインによる拡張性が求められるシステムで採用される。
コアシステムには最低限の基本機能のみを持たせ、周辺のプラグインコンポーネントが個別の機能を提供する。
プラグインはコアに対して明確に定義された契約(インターフェースやAPI)に従って実装され、コアとメッセージをする。
重要な点はプラグイン同士が直接通信しないようにすることで、全体としての結合度を下げ保守しやすくしていること。
この設計により、新しいプラグインを追加・削除することで機能拡張や変更を柔軟に行えるようになる。
PHPの世界では、WordPressなどのCMSがコア機能とプラグイン拡張という構造を持っており、マイクロカーネルアーキテクチャの良い例と言える。
メリット:
拡張性とモジュール化:
コアを変更せずにプラグインの追加・入れ替えで新機能を提供できるため、アプリケーションの拡張が容易。
たとえばCMSに新しいSEO機能を追加したい場合はプラグインとして実装すれば既存システムに影響を与えずに機能強化が可能。
ユーザーごとに必要なプラグインだけを導入するといったカスタマイズも可能で、製品のモジュール性が高まる。
段階的な開発が可能:
全機能を最初から作り込まなくても、あとからプラグインで機能を継ぎ足していける。
まず最小限のコアをリリースし、ユーザーフィードバックに応じてプラグインを追加するなどの漸進的な開発手法に向いている。
コアとプラグインの契約がしっかり定義されていれば、プラグイン部分を外部の開発者に委託することもでき、エコシステムの構築にもつながる。
デメリット:
設計と契約定義の難易度:
コアとプラグインの通信インターフェース(契約)を最初に正しく設計する必要がある。
契約が不十分だと、後から「この機能はプラグインでは実現できない」と判明したり、逆にセキュリティ上許可すべきでない操作までプラグインに許してしまう恐れがある。
契約変更はシステム全体に影響するため、慎重な設計が求められる。
プラグイン間連携の困難さ:
アーキテクチャのルール上、プラグイン同士が直接やりとりしないため、もし複数プラグインの機能をまたぐ処理が必要になると実現が難しくなる。
どうしてもという場合はコア側で各プラグインのデータを統合する処理を書くか、特殊なプラグインを作る必要がある。
そうすると設計が崩れる。
プラグインはあくまで独立した追加機能に留め、相互依存しないようにする運用が前提となる。
単一システム前提:
基本的にコアとプラグイン群は同じアプリケーションプロセス内(単一マシン上)で動作する想定。
そのためマイクロサービスのように分散システム化する手法とは相性が悪い。
一つの巨大なサービス(モノリス)の内部構造をプラグイン方式でモジュール化するイメージに近く、システム全体をスケールアウトしたりする場合には別のアーキテクチャと組み合わせる必要がある。
⸻
まとめ
MVC以外の代表的なアーキテクチャパターンを5つ紹介した。
簡単に振り返ると:
三層アーキテクチャ:
シンプルで基本的な構造。
まず小規模アプリはこれで十分。
大規模化してきたら次の手を考える。
MVP:
プレゼンテーションロジックを分離してテストしやすくするパターン。
フォーム入力や画面遷移の多いUIに向いている。
WebよりもデスクトップアプリやAndroid開発でよく使われている。
クリーンアーキテクチャ
大規模開発や長期運用で威力を発揮するドメイン重視の設計手法。
ドメイン駆動設計(DDD)とも親和性が高い。
ただし小規模だとやりすぎになる。
マイクロサービスアーキテクチャ:
システムをサービス単位に分割する現代的手法。
スケーラビリティや開発スピード向上に効果的だが、インフラ含めた高い設計・運用能力が要求される。
段階的にモノリシックから移行するケースも多い。
マイクロカーネルアーキテクチャ:
コア+プラグインで拡張性を確保するパターン。
プラグイン機能をユーザーやサードパーティに公開したい場合に有用。
WordPressのようにプラグインエコシステムを作るプラットフォームに適している。
実際の現場では、これらのパターンを組み合わせて使うことも一般的らしい。
例えば「マイクロサービス化された各サービス内部はクリーンアーキテクチャで構築する」「基本は三層だが特定部分にプラグイン方式を採用する」などだ。
それぞれのアーキテクチャにはメリット・デメリットがある。
大事なのはアプリケーションの要件やチームのスキルに照らし合わせて最適な方法を選ぶことが重要ですね。