BEAR.SundayとDDD

# アプリケーション設計

マニュアルのチュートリアルの記事などではリソースのリクエストメソッドで直接SQLを操作していますが、実際にはアプリケーションをどのように設計、実装するかはアプリケーション開発者の仕事です。

この記事は[コンテキスト境界、ディレクトリ構成について考える](https://qiita.com/kumamidori/items/0462b0bee4ff6281fa1d)のフォローアップ記事で、BEAR.SundayでDDDを導入するときにどのような構成になるかを考えます。

# PHP DDD Cargo Sample

[PHP DDD Cargo Sample](https://github.com/codeliner/php-ddd-cargo-sample)とは[エリック・エヴァンスのドメイン駆動設計](http://amzn.to/2kTNtj8)で紹介されているパターンを実際に使用して作られたPHPのサンプルアプリケーションです。

このサンプルはPSR-7とPSR-15を使ったzend-expressive + Doctrine ORMで構成されていますが、これをBEAR.Sundayで利用する事を想定します。

# DDD Compoentsフォルダ

まず、アプリケーションのトップディレクトリ`cargo`や`graph-traversal`というDDDコンポーネントのフォルダを設置します。それぞれのコンポーネントは`src/`、`tests/`ディレクトリを持っていてそれぞれプロジェクトとして独立しています。PHPの名前空間も独自に保持します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/3940/feff515a-7dcc-179f-35f1-59cb9e18aa60.png)

autoloadのためにcomposer.jsonを編集します。

```composer.json
"autoload": {
        "psr-4": {
            "MyVendor\\MyProject\\": "src/",
            "Codeliner\\CargoBackend\\"  : "ddd/cargo/src",
            "Codeliner\\GraphTraversalBackend\\"  : "ddd/graph-traversal/src"
        }
    }
```

後述するように独立したパッケージにしてもいいでしょう。

# DDD src

![image.png](https://qiita-image-store.s3.amazonaws.com/0/3940/095e0a88-f710-9af2-ceeb-3ecda127b901.png)

DDDコンポーンネントのsrcフォルダは上記のようにディレクトリが分けられています。詳細はPHP DDD Cargo Sampleをご覧ください。

DDDコンポーンネントは原則、**フレームワークアグノスティック**、つまりBEAR.Sundayの特定の機能に依存しないように作成します。**フレームワークの機能に依存してDDDを構築するのではなく、DDDコンポーネントに依存**してBEAR.Sundayフレームワークを利用します。

# UIレイヤー

![image.png](https://qiita-image-store.s3.amazonaws.com/0/3940/b994fee4-a0da-69ff-6ebb-8ea172bbc0ab.png)

サンプルのようにUIを全てJSで行えばBEAR.SundayはもちろんPHPからの依存をなくすことも出来ます。詳しくは[Javascript UI](http://bearsunday.github.io/manuals/1.0/ja/js-ui.html)をご覧ください。

# HTTP Action

Cargoのルートを取得する`HTTP Action`のコードは以下のようなものです。

```php
final class GetCargo implements MiddlewareInterface
{
    /**
     * @var BookingService
     */
    private $bookingService;

    public function __construct(BookingService $bookingService)
    {
        $this->bookingService = $bookingService;
    }

    public function process(ServerRequestInterface $request, DelegateInterface $delegate): ResponseInterface
    {
        if (null === $trackingId = $request->getAttribute('trackingId')) {
            return new EmptyResponse(404);
        }

        try {
            $cargoRoutingDto = $this->bookingService->loadCargoForRouting($trackingId);

            return new JsonResponse($cargoRoutingDto->getArrayCopy());
        } catch (CargoNotFoundException $e) {
            return new EmptyResponse(404);
        }
    }
}
```

このアクションをBEAR.Sundayのリソースにインジェクトして利用することもできますが、Resourceのメソッドで同様の記述をする事も検討してみましょう。

上記のPSR-7/15対応のHTTP ActionはBEAR.Sundayでは以下のようになります。

```php
final class Cargo extends ResourceObject
{
    /**
     * @var BookingService
     */
    private $bookingService;

    public function __construct(BookingService $bookingService)
    {
        $this->bookingService = $bookingService;
    }

    public function onGet(string $trackingId = null): ResourceObject
    {
        if (! $trackingId) {
            $this->code = 404;
            
            return $this;
        }
        try {
            $cargoRoutingDto = $this->bookingService->loadCargoForRouting($trackingId);
            $this->body = $cargoRoutingDto->getArrayCopy();
            
            return $this;
        } catch (CargoNotFoundException $e) {
            $this->code = 404;

            return $this;
        }
    }
}
```

また第３の選択肢としてBEAR.Sundayを[PSR7のmiddlewareでそのまま動作させることもできます。](http://bearsunday.github.io/manuals/1.0/ja/psr7.html)

BEAR.Sundayネイティブの記述はAPIのドキュメントの自動生成や優れたリソースキャッシュを使うか、他のフレームワークとの協調を重視してPSR対応にするかはトレードオフで、アプリケーション開発者の選択です。 

# 終わりに

この記事で紹介したのはアプリケーションから可能な限りフレームワーク依存を取り除き、フレームワークのバージョンや密結合したコンポーネントからロックされないDDDのアプリケーションの可能性です。「BEAR.SundayでDDD」 ..ではありません。

`ddd`フォルダ内のアプリケーションは独立したpackageとしてprivateなpackagistレポジトリに登録して複数のアプリケーション（例えば管理画面）から利用可能でしょう。分割されたアプリケーションはユースケースに応じて組み合わせるようなこともできるし、それぞれバージョンを持たして内部の互換性を担保しつつ疎にすることが出来ます。

["分散型の大きな泥だんご"](http://www.codingthearchitecture.com/2014/07/06/distributed_big_balls_of_mud.html)になることなく、アプリケーションを分割可能にし、モノリスの良さを持ちつつ、マイクロサービスが持つメリットをかなりの程度を享受できるような構成が可能ではないでしょうか。

そういう大きな希望で今年のカレンダーを締めくくりたいと思います。作成してくれたkuma_nanaさん、執筆してくれたkaliboraさん、kawanamiyuuさん、記事を読んでくれた皆さん、BEAR.Sundayをいつも使ってくれている国内外の開発者の皆さん、今年もありがとうございました。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/3940/75d1ae99-17d8-8a99-fcb5-35a7bc271c97.png)

> 大好きなクマのぬいぐるみの「日曜日」は、なんにもしゃべらないし、だきついてもこない。ぼくのことをどう思ってるんだろう…
>
> アクセル・ハッケ - [クマの名前は日曜日](http://amzn.to/2zsLJBv)