モジュールは高速で副作用がないこと

Ray.Diのモジュールは、設定に外部XMLファイルを使用せずに通常のPHPコードで記述します。 PHPは使い慣れ、お使いのIDEで動作し、リファクタリングに耐えることができます。

しかし、PHPが自由に使える利点の代償としてモジュール内で多くのことをやりすぎてしまいがちです。 例えば、Ray.Diモジュールの中でデータベースの接続やHTTPサーバーの起動をすることです。 しかし、それはやめましょう。このような処理をモジュールの中で実行するには以下の問題があります。

  • モジュールは起動するが、シャットダウンしません。 モジュール内でデータベース接続を開いた場合、それを閉じるためのフックがありません。
  • モジュールはテストをする必要があります。 モジュールの実行過程でデータベースを開くと、そのモジュールの単体テストを書くのが難しくなります。
  • モジュールはオーバーライドが可能です。 Ray.Diモジュールは オーバーライド をサポートしており、本番サービスを軽量サービスやテストサービスで代用することができます。モジュール実行の一部として本番サービスが作成される場合、このようなオーバーライドは効果的ではありません。

モジュール自体で作業を行うのではなく、適切な抽象度で作業を行えるようなインターフェースを定義しましょう。 例えば、次のようなインターフェースを定義します。

interface ServiceInterface
{
    /**
     * Starts the service. This method blocks until the service has completely started.
     */
    public function start(): void;
    
    /**
     * Stops the service. This method blocks until the service has completely shut down.
     */
    public function stop(): void;
}

Injector を作成した後、サービスを開始してアプリケーションのブートストラップを完了します。 また、アプリケーションを停止したときにリソースをきれいに解放するためにシャットダウンフックを追加します。

class Main
{
    public function __invoke()
        $injector = new Injector([
            new DatabaseModule(),
            new WebserverModule(),
            // ..
        ]);
        $databaseConnectionPool = $injector->getInstance(ServiceInterface::class, DatabaseService::class);
        $databaseConnectionPool->start();
        $this->addShutdownHook($databaseConnectionPool);

        $webserver = $injector->getInstance(ServiceInterface::class, WebserverService::class);
        $webserver->start();
        $this->addShutdownHook($webserver);
    );
}