Full-Stack JavaScript meets DDD.

これは 2020-01-10 に開催された、DDD meetup#3 でのLTの内容を記事化したものです。
Vuex+Express環境でどんなアーキテクチャを採用したか、して良かったこと／悪かったことを発表しました（LT資料は[こちら](https://speakerdeck.com/uskithub/full-stack-javascript-meets-ddd)）。

# 問題提起
フロントエンドでDDDを実践しようと考えて、結局採用を見送った経験のある方は以外に多いのではないでしょうか。ドメイン知識はバックエンドに集中させてフロントはできるだけライトウェイトに…。と、がんばっても、どうしても気になるものの一つがバリデーション。些末なことだけどバリデーションはれっきとしたドメイン知識。これだけ半端にフロントにいるの、気持ち悪いですよね？

折角ドメイン知識をその他と分離するなら、フロントとバックでもそれらを共通化したい！できるんです。そう、Full-Stack JavaScriptでの開発なら。

# 結論

こんなアーキテクチャを採用しました。フロントとバックで、共通化したドメインモデルを使います。

<img width="960" alt="採用したアーキテクチャ" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26825/3f7fcb65-2a78-1f4b-ebde-a316da295590.png">

# 前提認識

私の中のDDDを完結にまとめます。

### DDDの目的
それは「**システムモデルをメンタルモデルと近づけること**」に集約されると思います。そして、DDDという設計手法を採用するのはなぜ？という問いの答えは、システムモデルをメンタルモデルへ近づけることで、メンテナンス性の高いシステムにできるから、だと思っています。

システムは作って期待通りに動けば終わりかというと、そうではありません。システムはサービスあるいは業務を実現するためのツールです。システムで実現したいサービスあるいは業務は日々変わっていきます。その変更を実現するためにシステムはメンテナンスし易く在るべきです[^1]。

### DDDした際のゴール
1. ドメイン知識がメンタルモデルに近い形でモデリングされている
2. ドメイン知識とその他のコードが分離された **何らかのアーキテクチャ** が採用されている

メンタルモデル（人の頭の中でのサービスあるいは業務の理解）とシステムモデルが近ければ近いほど、システムの理解が容易になります。それは内側から見た設計という意味でも、外側から見た操作性という意味でもです[^2]。

メンタルモデルとシステムモデルを近づけるために、ドメイン知識 **以外** のコードをドメイン知識から切り離すことがDDDの基本的戦略です。

### ３レイヤーのガイドライン

軽量DDDと言って、ほにゃららアーキテクチャでシステムを作ってみよう！というアプローチも否定はしませんが、サービスあるいは業務要件にあったインフラ環境上で、最適なアーキテクチャを導くことが Domain-Driven **Design** でしょう。そして、その際のガイドラインが３レイヤーです。

<img width="960" alt="３レイヤーガのイドライン" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26825/e97d4941-a478-69cd-1f94-9d5fecf56e82.png">

個人的には、ドメイン層とユースケースを扱うアプリケーション層には名前を付けて良いと思いますが、それ以外はその他層で充分と思います。

ここで忘れてはいけないたった一つのルールが、外の層は内の層を知っていていいが、その逆はダメ、ということ。そして最も内側にあるドメインモデルがRDBやAPIから取得したデータを **外側を知らないまま** 扱えるようにするトリックとして、インタフェースを使った依存性の逆転も押さえるべき必須事項です。

<img width="960" alt="依存性逆転" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26825/2dd05334-fd81-6b11-96f4-3ff41d0e691b.png">

# アーキテクチャ解説

## Front-End

複雑なので、パート毎に見てみます。フロントだけ見ると、
<img width="960" alt="フロントに着目" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26825/b1a67efe-929a-884d-6d39-b92ce293b7f5.png">

Vuexが採用するフロントエンドアーキテクチャ（下図）の派生系です[^3]。iOS開発で生み出された [VIPER](https://cheesecakelabs.com/blog/ios-project-architecture-using-viper/) を参考にしています。ユースケースを実現する `interactor` がその内部で `domain model` を扱う際には、専用インタフェースをインプリメントするAPIクライアントのインスタンスを渡すことで依存性を逆転させつつ使用します。
<img width="960" alt="Vuexが採用するフロントエンドアーキテクチャ" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26825/00f1d3d7-809c-f451-eabb-33de31a55c7a.png">

## Back-End

バックエンドだけを見ると普通にオニオンアーキテクチャ。
<img width="960" alt="バックに着目" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26825/d4b430a0-dbca-7e65-825c-a9bbfa9ea792.png">

## 全体で見ると
<img width="960" alt="ドカンアーキテクチャ" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26825/7ff18eff-8df3-e4ca-ec66-7923c529bf22.png">
フロントの `interactor` からバックの `interactor` までをドカンをぶち通した感じに見ることができます。名付けて **Full-Stack JavaScript ドカンアーキテクチャ** の誕生です。

# 評価

## 良かったこと

- Front-Endでドメイン知識が必要になってもへっちゃら
- Front-EndとBack-End合計でのアーキテクチャルールを削減できた
- 定数ファイルの共通化

Front-Endにドメイン知識が取り残される気持ち悪さや見通しの悪さがなくなりました。インタフェースを実装するAPIクライアント／DBクライアントを生贄に `domain model` を生成して`interactor` で使う、という形が共通なので実装がスムーズになりました。そしてバリデーションや定数などを共通化できるのが地味に嬉しいです。

## 悪かったこと
- プロジェクト内に同じ名前のソースファイルが増えて混乱した
- Front-EndのES Modules（ex. import）と、Back-End（Node.JS）のCommonJS（ex. require）に苦しめられた

命名に関してはフロントとバックの双方に `ほにゃららInteractor.js` がいるように命名規則を作ってしまったので、どっちがどっちだったっけ？と混乱することがありました。命名は改善の余地ありです。

そしてフロントとバックでファイルを共通化すると聞いて、ES Modules と CommonJS の違いはどうしたのさ？と疑問に思われていた方もいらっしゃると思います[^4]。結論としては、今々はNode.js側に esmImport を入れて対応しました。それでも不自由なことがあるので、この辺が統合されたバージョンが早くスタンダードになると嬉しいですね。


[^1]: 正直、（プロトタイプは別として、）動くだけのものをできる限り安く作ろうという発注者は「安物買いの銭失い」でしかないと思う。
[^2]: 参考までにと拙著「[ドメイン駆動設計とOOUXで迎える2019年](https://qiita.com/uskiita/items/d4aec5d6aea09cc50d77)」をおもむろに紹介してみたり。
[^3]: Vuexは、想定する方法（ユースケース）のみが状態を変更できることを保証する、状態管理パターンを実現するライブラリ。ちなみにVueはMVCでいうViewの描画フレームワーク。HTMLテンプレートにデータをはめ込みDOMに描画する。
[^4]: Node.jsでは別ファイルの読み込みにCommonJSというモジュール管理システムを採用しているが、ES6以降ブラウザ側での企画として採用されたES Modulesとで記述に差異があり、ファイル共通化の妨げになっている。