📑

Laravelをモジュラモノリスで開発する

2022/02/14に公開

中小規模のLaravelは、初期はモジュラモノリスで運用し、サービスが複雑/大規模になって行く過程で、モジュールをマイクロサービス化していく運用が初期開発が抑えられ、かつクリーンなコードも実現できて良いのではないのでしょうか?

モジュラモノリスとは

モノリスアプリケーション内で、ドメインモデル等を単位としてモジュールに分解し、モノリスのように1つのデプロイパイプラインだけを持ちつつも、マイクロサービスのようにシステムのモジュール化・独立性を両立したモノリスアプリケーション。

モジュラモノリスとマイクロサービス

モジュラモノリスの長所

モジュラモノリスの長所として、以下のようなものが挙げられるかと思います。

  • モノリスであるため、開発がマイクロサービスと比較し容易である
  • コンテキストの境界がモジュール毎に明白になる
  • モジュール毎にマイクロサービス化を進める事が可能である

モジュラモノリスの短所

モジュラモノリスの短所として、以下のようなものが挙げられるかと思います。

  • DB周りのモジュール性がない可能性がある
  • マイクロサービスと比較し、境界を適切に維持し続けるコストがかかる
    • 静的解析ツールにより軽減可
  • モジュール毎に異なった動作環境で動作させることができない

Laravelのモジュール化

Laravelのモジュール化するパッケージはいくつかありますが、Laravel-Modulesが一番人気だと思います。

Laravel-Modulesは、モジュール毎にLaravelの機能を作成/管理することができるパッケージです。Blogモジュールを作成した場合は、デフォルトで下記のようなディレクトリが作成され、Laraevlの中に小さなLaravelを作成することができます。ControllersだけでなくRoutesResourcesもモジュール内で管理することができます。

app/
bootstrap/
vendor/
Modules/
  └── Blog/
      ├── Assets/
      ├── Config/
      ├── Console/
      ├── Database/
      |   ├── Migrations/
      |   └── Seeders/
      ├── Entities/
      ├── Http/
      |   ├── Controllers/
      |   ├── Middleware/
      |   └── Requests/
      ├── Providers/
      |   ├── BlogServiceProvider.php
      |   └── RouteServiceProvider.php
      ├── Resources/
      |   ├── assets/
      |   ├── lang/
      |   └── views/
      ├── Routes/
      ├── Repositories/
      ├── Tests/
      ├── composer.json
      ├── module.json
      ├── package.json
      └── webpack.mix.js

作成したモジュールは、Laravel Module Installerを使用することで別リポジトリで管理する事もできます。

Laravel-Modulesのインストール

Laravel-Modulesのインストール

$ composer require nwidart/laravel-modules

コンフィグファイルの生成

$ php artisan vendor:publish --provider="Nwidart\Modules\LaravelModulesServiceProvider"

オートローディング設定

composer.json
{
  "autoload": {
    "psr-4": {
      "App\\": "app/",
      "Modules\\": "Modules/",
      "Database\\Factories\\": "database/factories/",
      "Database\\Seeders\\": "database/seeders/"
  }

}
$ composer dump-autoload

モジュールの作成

モジュールはartisanコマンドによって作成することができます。

$ php artisan module:make <module-name>

カスタムネームスペース

モジュールのlangファイルやviewファイルには、ネームスペースを用いてアクセスすることができます。

Lang::get('blog::group.name');
view('blog::partials.sidebar')

モジュールの境界を強制する

本来、モジュールは独立したモジュールであることが期待されますが、モジュラモノリスは結局のところモノリスのため、モジュールのクラスから他のモジュールのクラスを呼び出したりしてしまい、意図せずモジュールの独立性が失われる可能性がある問題があります。

この問題は、静的解析ツールを用いることで解決することができます。

PHPStanの場合、spaze/phpstan-disallowed-callsをインストールすることで、依存関係をコントロールすることができるようになります。

e.g. Modules\Blogを他のネームスペースから呼び出し不可にする設定

phpstan.neon
parameters:
    disallowedNamespaces:
        -
            namespace: 'Modules\Blog\*'
            message: "Don't call form other namespaces"
            allowIn:
                - Modules/Blog/*

PHPStanをまだ導入されていない場合は、Larastanをlevel 0から導入することろから始めましょう。

おわりに

近年、静的解析ツールが普及してきて、コーディングルールを自動チェックできるのは非常に便利になりましたね。 現在のPHPは残念我ならマイクロサービスにはあまり向いていません。モノリスであるがマイクロサービスの特徴もあるモジュラモノリスは、非常にバランスの取れた選択肢の一つかと思います。

参考サイト

Discussion