その他
Drupalのアーキテクチャについて
WordPressのようにオープンソースでありながら、カスタマイズ機能に特化したCMS「Drupal」のアーキテクチャについて解説します。
はじめに
LYZONエンジニアの齊藤です。
今回はDrupalのアーキテクチャ(基本構造・設計思想)についての記事をまとめましたので、ここで紹介させていただきます。
Symfonyコンポーネントの上で動くモダンPHPの作法
DrupalをCMSですが、PHPの標準的なフレームワークであるSymfonyのコンポーネントをベースに再構築されています。
その基盤を支える3つの要素を紹介します。
HTTPKernelによる標準的な処理フロー
Drupalのリクエスト処理は、SymfonyのHttpKernelコンポーネントに準拠しています。
ユーザーからのリクエストを受け取ると、ルーティングシステムが適切なコントローラーを特定し、最終的にレスポンスを返すという、モダンなPHPアプリケーションとして極めて標準的かつクリーンなパイプラインで動いています。
この「URLを解析してコントローラーを呼び出し、レスポンスを生成する」という流れは、Sitecoreで採用されているASP.NET MVCの動作とよく似ているため
Sitecoreエンジニアであれば、「ルーティング設定によって特定のコントローラーのアクションが実行され、ビュー(.cshtml)が返される」というお馴染みのMVCパターンをイメージしていただければ、Drupalのアーキテクチャもスムーズに理解できるはずです。
Dependency Injection (DI) とサービス
Drupalの内部設計には、依存性の注入(Dependency Injection)とサービスコンテナの概念が深く根付いています。
データベース操作、メール送信、パスワードハッシュといった共通機能はすべて「サービス」として定義されており、サービスコンテナによって管理されています。これにより、以下のようなメリットが生まれます。
- 密結合の回避: 各機能が独立しているため、ある機能を修正しても他に予期せぬ影響が出にくい設計になっています。
- テストコードの書きやすさ: 依存関係をモック(擬似オブジェクト)に差し替えることが容易なため、ユニットテストの導入がスムーズです。
- 機能の差し替え: 既存のサービスを自作のクラスでオーバーライド(上書き)することで、Drupal本体(コア)のコードを一行も汚さずにシステムの挙動を根底から変更できます。
Sitecoreでは標準の動作を書き換える際にパッチファイル(.config)を作成してSitecoreに配置して標準の設定値を書き換えたり、パイプラインに処理を差し込んでプログラム自体の挙動を変更したりする場面がありますが、Drupalでも近しいことが可能です。
オートローダーとPSR規格への準拠
現在のDrupalはPHPの標準規格であるPSR(PHP Standard Recommendation)に準拠しています。
Composerによるパッケージ管理を前提とし、PSR-4によるオートローディング(クラスファイルの自動読み込み)が採用されているため、外部のPHPライブラリを導入する際も、一般的なPHPプロジェクトと同じ感覚でシームレスに連携できます。
Sitecoreでは追加のパッケージはそこまで多くはありませんが、Drupalは基本的に機能を追加する場合はcomposerコマンドを利用してモジュール(機能)を追加する事が多いです。
データ構造の核「Entity API」と「Field API」
Drupalにおける「データ」の扱いについて深掘りします。Drupalでの開発において、最も頻繁に触れるのが「エンティティ」と「フィールド」です。
これらは単なるデータの器ではなく、強力に抽象化されたシステムとして構築されています。
「すべてはエンティティである」という抽象化
Drupalでは、サイトを構成する主要なデータ要素のほぼすべてが「エンティティ(Entity)」として定義されています。
- ノード (Node): 記事やページなどのメインコンテンツ
- タクソノミー (Taxonomy Term): 階層を持つことができる、カテゴリやタグ(Sitecoreの設定アイテムに近い使い方ができる)
- ユーザー (User): アカウント情報
- メディア (Media): 画像や動画ファイル
これらが共通の「Entity API」というインターフェースを介して扱えることが、開発上の大きなメリットを生みます。例えば、「最新のコンテンツを5件取得する」という処理を書く際、対象が記事であってもユーザーであっても、同じメソッド(エンティティクエリなど)を使用して、統一された方法でデータを操作することが可能です。
正規化されたデータ保持とField API
Drupalは、フィールド単位でデータベースのテーブルが分離されるという特徴的な構造を持っています。
例えば「記事」というエンティティに「公開日」と「画像」というフィールドを追加すると、データベース上ではそれ専用のテーブルが自動生成されます。一見するとテーブル数が増えて複雑に見えますが、これには明確な意図があります。
- 厳密なメタデータ管理: 各フィールドは型(数値、テキスト、参照など)を持ち、バリデーションやデータの整合性がシステムレベルで担保されます。
- 再利用性: 作成したフィールドは、他のエンティティタイプでも再利用が可能です。
- スケーラビリティ: 大規模なデータセットに対しても、正規化された構造のおかげで、キャッシュの最適化やクエリの効率化が図りやすくなっています。
コンテンツタイプという考え方
エンティティをさらに柔軟にしているのが「コンテンツタイプ」という概念です。Drupal上のコンテンツタイプは、Sitecoreでの「ページテンプレート」によく似ています。 コンテンツタイプという大きな枠組みの中に、異なるフィールド構成を持つように定義することができます。
例えばニュース詳細というコンテンツタイプに「お知らせタイトル」のフィールドや「お知らせ本文」フィールドを持たせ、
製品詳細というコンテンツタイプに「仕様表」フィールドや「価格」フィールドを持たせることができます。
これらのコンテンツタイプを作成した上で、実際のページとして「ノード(コンテンツ)」を作成し、URLを設定することでページを表示させることが可能です。
Sitecoreエンジニアの方であれば、「テンプレート」を定義し、それに基づいて「アイテム」を作成する概念に非常に近い、と考えると理解がスムーズかもしれません。
フックとイベントによる「宣言的」なカスタマイズ
Drupalの最大の特徴は、コアのソースコードを一行も変更することなく、システムのあらゆる挙動を上書き・拡張できる点にあります。
これを実現するのが「Hook」「Event Subscriber」「Plugin」という3つのメカニズムです。
Sitecoreの「Pipeline(パイプライン)」にプロセッサを登録する感覚に近いのが「Event Subscriber」であり、特定のイベント(アイテムが保存された時などに動くイベント等)をフックする感覚に近いのがDrupalでの「Hook」になります。
Hookシステム:伝統的かつ強力な割り込み処理
Drupalを象徴する仕組みがHook(フック)です。特定の処理(コンテンツの保存、フォームの表示、ページのレンダリングなど)が走る際に、システムが「誰かこの処理に介入したいモジュールはあるか?」と各モジュールに問いかけます。
- 宣言的な拡張: モジュール名_hook名 という命名規則に従った関数を定義するだけで、自動的に処理が呼び出されます。
- 柔軟な介入: フォームの入力値をバリデーションしたり、表示されるHTMLを直前で書き換えたりといった操作が、非常にシンプルに記述できます。
Event Subscriber:Symfony由来のモダンな設計
Drupal 8以降、Symfonyのコンポーネントが導入されたことで、Event Subscriber(イベントサブスクライバー)による拡張が主流になりつつあります。
- オブジェクト指向な設計: Hookが関数ベースなのに対し、Event Subscriberはクラスベースで記述します。依存性の注入(DI)をフル活用できるため、より堅牢でテストしやすいコードを書くことが可能です。
- 使い分けの指針: 現在のDrupalでは、古いシステムに由来する処理は「Hook」、ルーティングやリクエスト/レスポンスといったモダンな基盤に近い処理は「Event」で行うという使い分けが一般的です。
プラグイン・システム:機能を「差し替え可能」にする
「特定のインターフェース(規約)に従っていれば、中身を自由に入れ替えられる」という仕組みがプラグイン・システムです。
- 具体的な例:
- 独自の「ブロック」を作成する
- 新しい「画像エフェクト」を追加する
- 外部システムとの「認証ロジック」を実装する
- アノテーションによる定義:
- クラスのコメント欄に特殊な記述(アノテーション)を加えるだけで、Drupalがそのクラスを特定の機能として自動認識します。これにより、エンジニアは「どこに登録するか」を悩むことなく、ロジックの実装に集中できます。
コンテンツと設定を分離する「Configuration Management」(構成の同期)
Drupalでの開発体験を劇的に向上させているのが、「構成管理(Configuration Management)」という仕組みです。かつてのCMS開発でよくあった「ローカルで作った複雑な設定を、本番環境で手作業で再現する」という不安定な作業は、Drupalでは過去のものです。
Content vs Configuration:データの役割を明確に分ける
Drupalの設計思想の根幹には、「コンテンツ(動的なデータ)」と「構成(システムの定義)」を明確に切り離すという考え方があります。
- コンテンツ: 記事本文、ユーザーが投稿したコメント、アップロードされた画像など(DBに保存され、環境間で同期しないもの)。
- 構成(Configuration): コンテンツタイプの定義、Viewsのクエリ設定、有効化したモジュールのリスト、サイト名などの基本設定(開発者が定義し、環境間で同期すべきもの)。
この分離により、本番環境のコンテンツを壊すことなく、開発環境で作った「新しい機能(構成)」だけを安全にデプロイすることが可能になります。
YAMLベースの同期とDrushによる効率化
Drupalの構成情報はすべてYAML形式のテキストファイルとして書き出すことができます。
ここで活躍するのが、Drupal専用のCLIツールである「Drush(ドラッシュ)」です。コアモジュールであるdrushモジュールをインストールすると利用することが可能になります。
- 構成情報のエクスポート: drush config-export (または drush cex) コマンドを叩くと、DB上の最新設定が大量のYAMLファイルとしてディレクトリに書き出されます。
- 構成情報のインポート: 別の環境(本番など)で drush config-import (または drush cim) を実行すれば、それらのファイルの内容がDBへ一括反映されます。
DrupalのCMS画面上で「ポチポチ」とクリックして設定した内容が、コマンド一つで寸分違わず他環境へコピーされるため、構成を丸ごと反映させることに非常に長けています。
(反面、個別にこの部分だけ反映したい!となると、個別にyamlファイルを払い出してインポートする必要が出てきてしまいますが…。後述のGit管理をすることで、多少は差分反映をすることも可能です)
Git管理の徹底:構成を「コード」として扱う
すべての設定がYAMLファイル(テキスト)になるということは、Gitによるバージョン管理が可能になることを意味します。
- 変更履歴の可視化: 「誰がいつ、どのフィールドを追加したのか」が、GitのDiff(差分)として明確に残ります。
- チーム開発の円滑化: 複数人のエンジニアが同時に開発していても、Git上でコンフリクトを検知・解消できるため、チームでの大規模開発も安全に進められます。
- ロールバックの容易さ: もし設定反映で問題が起きても、Gitで前のバージョンに戻して再インポートするだけで、元の状態に確実に戻すことができます。
おわりに
以上、ちょっと長くなってしまいましたがエンジニア観点での「Drupalのアーキテクチャについて」を記載させていただきました。
引き続き、今後もDrupalについての技術情報ブログを発信していきますので、どうぞよろしくお願い致します。
※エントリーの内容・画像等は、公開時点での情報に基づきます。
※Drupalのバージョンによって実装されている機能が異なります。