今日は 複雑なJavaScriptアプリケーションに立ち向かうためのアーキテクチャ (Shinpeim) – builderscon tokyo 2017 – YouTube を見たので、学んだことをメモしておきます。
※Aパートまでで今現在の自分が知りたかったことを学べたのでお腹いっぱいでした(もちろんBパートもおすすめです)
PDS(Presentation Domain Separation)
- プレゼンテーション(UI/入力出力)とドメイン(それ以外)で分ける
- |Presentation| <-> |Domain|
- プレゼンテーション層とドメイン層の会話はMVVM(MVP,MVWhatever)のVM(ViewModel), MVで行う
- |Presentation| View -> ViewModel -> |Domain| Model
- |Presentation| View <- ViewModel <- |Domain| Model
- ViewModelはModelを監視しておいて、Modelが変化したらViewに反映する
- オブザーバーパターン
- UIとModelの依存を断ち切る
- PDSができていればMVVMだろうがMVPだろうがなんでもよい
- PDSはPresentationのことには触れているがDomain(Model)の部分には触れていない
- MVWhatever自体もモデルの設計についてはなにも指針をくれていない
Layered Architecture
Domain(Model)の部分を
- Application層(アプリケーション層)
- プレゼンテーション層に対する窓口
- Domain層(ドメイン層)
- アプリケーションの論理的な挙動を定義する
- Infrastracture層(インフラストラクチャ層)
- 外部とのやりとりなどの「実装依存」なものを書く
の3つに分ける。
Application層
- プレゼンテーション層に対して「操作の窓口」を作る
- ドメイン層、インフラストラクチャ層を呼び出してユースケースを実現する
Domain層
- アプリケーションの構造をここに「モデリング」する
- ここを発見していくことはめちゃくちゃ難しい
- ステークホルダーとの会話
- ここを発見していくことはめちゃくちゃ難しい
- ビジネスルール(仕様)を表現
- そして、それ(ビジネスルール)がそのままコードに落とせるとGood
- 非プログラマー(ディレクター)でも見たらなんとなく読めるのが理想
Infrastracture層
- 外部とのやりとりやプラットフォーム依存の「実装詳細」が書かれる
- フレームワークの知識(機能)が現れる
- ドメイン層に持ち込むと、ドメイン層が実装依存(フレームワークに依存)になってしまう
- 技術的な関心をここに書く
- 技術的関心(Infrastracture層) or ドメイン層の関心かを見分けるために以下の視点をもつことは役立つかもしれない
- これはプログラマだけが関心を持つものか
- => Infrastracture層
- ディレクターが関心を持つものか
- => ドメイン層
- これはプログラマだけが関心を持つものか
テスタビリティ
現在までの依存の方向
Presentation ↓ (PresentationがApplicationを使う|知っている) Application ↓ (ApplicationがDomain,Infrastractureを使う|知っている) Domain ↓ (DomainがInfrastractureを使う|知っている) Infrastracture
これを、プラットフォーム/環境依存度
とテスタビリティ
という観点と合わせてみる。
プラットフォーム/環境依存度
層 | プラットフォーム/環境依存度 |
---|---|
Presentation | 高 |
Application | 中 |
Domain | 低 |
Infrastracture | 高 |
テスタビリティ(テストしやすさ)
層 | テストしやすさ |
---|---|
Presentation | 低(テストが難しい) |
Application | 中 |
Domain | 高(テストしやすい) |
Infrastracture | 低 |
こう見ると、
- 環境依存が高いものほどテストしづらい
- PresentationとInfrastracture
- 環境依存が少ないものほどテストしやすい
- Domain
というのがわかります。
だがしかし、DomainがInfrastractureに依存しているとテストで書きづらい部分が出てきます!!
しかも、Domain層は一番大事にしたい&&試行錯誤したいところなので、テストは書きやすいに越したことはない。というかそうであるべきです。
じゃあどうするかというと、依存の方向を変えます。
※依存の方向を変える目的は、テスタビリティを上げるのもありますが、外部サービスの変更やデータベースの切り替えなどのインフラストラクチャ層の変更により、ビジネスロジック全体に影響が発生する可能性を抑えるという目的もあります
依存の方向を変える
Presentation ↓ Application ↓ Domain ↑ // ここの向きを変える Infrastracture
このようにDomainが何にも依存しないようにします。
こうすることによってDomain層のテスタビリティを上げることができますし、他の層に変更があっても影響を受けずにすみます。
こういった依存の方向に関する考え方でCleanArchitecture(クリーンアーキテクチャ)
というものがあります。
他にも、
- ヘキサゴナルアーキテクチャ
- オニオンアーキテクチャ
というものもありますが、この3つ(クリーン、ヘキサゴナル、オニオン)は本質的には全く同じです。
※どれも内側にDomainModelを持って、それに向かって依存の矢印が伸びている
これらアーキテクチャの考えでいうと依存の方向は
Presentation|Infrastracture ↓ Application ↓ Domain
こうなります。
CleanArchitecture(CA)
抽象度が高く、テスタビリティの高いドメイン層を中心にして、内側に対して依存するようにしましょう。というコンセプト。
具体的なパターンは規定していない。(ヘキサゴナル、オニオンも)
CAを実現する実装パターン
- DIパターン※
- インフラストラクチャ層をDIしてインフラストラクチャ層への依存を断ち切る
- Application層やDomain層※はインフラストラクチャ層の抽象型(interface)と会話する
- 自分より外側の層と会話するにはinterfaceを用意してそれと会話する
- 依存関係逆転の原則(Dependency Inversion Principle, DIP)
- ※そもそも、Domain層にインフラストラクチャ層のものがDIされる場面ってあるのだろうか?
- Application層やDomain層※はインフラストラクチャ層の抽象型(interface)と会話する
- プレゼンテーション層をDIしてプレゼンテーション層への依存を断ち切る
- インフラストラクチャ層をDIしてインフラストラクチャ層への依存を断ち切る
- オブザーバーパターン
※…PHPerの文脈でいうとDIパターンが大事なのかなと思った
CQRS
- Domainのいろいろなところに状態が散らばりがち
- コマンド(状態を変更する操作)についてはドメイン層がマッチするけど、
- クエリ(状態の読み出し)についてはそうでもないことが多い
ここで、CQRS。
CとQを分ける
Command(状態の更新)
- 複雑なビジネスロジックを伴う場合が多いのでドメインモデルを利用して行う
- 状態を保存するRepository(DB, OnMemory,,)に保存する
- 副作用がある
Query(状態の読み出し)
- 状態の読み出しはRepository(DB, OnMemory,,)に保存されている状態を直接好き勝手に読み出してもよい
- 副作用がない
ここからBパート
実際にどうやって書くのか
Presentation
フレームワークが用意しているものを使う。
Application
- ApplicationService
プレゼンテーション層からの入力を受け取り、Domain層やInfrastracture層を操作する。
Domain
- DomainModel
要件によるのでみんなで考えるところ。
一人でじゃなくて、チームで、ステークホルダーも含めて。
Infrastracture
DB操作、APIアクセス、etc…。
上記以外のものは?
json -> DomainModel
- APIからプレーンなjsonが返ってきてドメイン層に書いたモデルにどこで変換する?
- 「腐敗防止層」
- 外部のデータを内部のドメイン層のオブジェクトに変換する
- DomainModelにstaticな
buildFromXX
みたいなメソッドを生やすのも手- 複数のデータソースから作成されるケースがある場合は上記のようなメソッドがたくさん生える可能性
- そうなったら、そのときに考える
DomainMoel -> Presentation
- DomainModelに表示に関するロジックやデータは持たせたくない
- maleを「男性」に変換するとか
- DomainModelは論理的な挙動のみを定義したい
- PresentationディレクトリにHelperを作ってそいつを使うのもアリ
- HelperにModelを渡す
- どんなものが定義されているのかわかりやすいかも
まとめ
- 悩んだら原則に立ち返る
- 実装パターンがあって問題があるのではなく、問題があってそれを解決するための考え方があって、その考え方を実現するための実装パターンがある
実装パターンがあって問題があるのではなく、問題があってそれを解決するための考え方があって、その考え方を実現するための実装パターンがある
大事なことだと思った&&感銘を受けたのでこれを2回書いて終わりにします。