Attempting Clean Architecture with Vue.js and Vuex.
Event Link: https://kfug.connpass.com/event/90962/ GitHub Link: https://github.com/andoshin11/clean-architecture-example-vue
Clean Architecture with VueVue.js / Nuxt.js Meetup Osaka #0by andoshin11Attempting
View Slide
Who am I ?● Andy (@andoshin11)● Vue.js Japan User Group● Frontend Developer @FOLIO● JS, Ruby, Go, Perl, Python, PHP, Rust(?)
Vue Fesやります!!
本日のゴール● Clean Architectureの概要を理解する● 「VueにClean Architectureを導入すべきか」を判断できるようになる
Clean Architectureの基本・オニオン型のレイヤードアーキテクチャ・各レイヤーの名前は重要ではない・依存性の矢印が単方向であることが大切・内側のレイヤーは外の世界を知らない
Clean Architectureの特徴● ビジネスロジックがFWやUI、DB、外部API等に依存しない(Testable)● ビジネスロジックはEntitiesとUsecasesに集約される● 外界(UI等)からのデータの読み書きはInterface Adapter(Presenter, Controller)を通して行われる
アプリケーション実装パターンのおさらい● MVC● MVVM● DDD● Flux
MVC(割愛)・Model - View - Controller・Railsが採用しているやつ・ViewがModelをObserve・Fat Model, Fat Controller問題・「データの振舞い」を定義する余地がない
MVVM(割愛)・Vue Componentがやってるやつ・Modelは生データおよびDomain Model・ファサードとしてのVM・ViewとVMにおいてデータバインディングが成立しているのでPresenterやイベントを受け取るControllerが不要
DDD(レイヤードアーキテクチャ)・業務上の関心事(ドメイン)を中心に据えた設計手法・Domain Layerでデータの振舞を定義・責務が分離されているため、フレームワークやライブラリに依存しづらい・レイヤー間の依存性は単方向でない
Flux・Facebook社が提唱・Redux, Vuex等・CQRS・Event Sourcing・単方向データフロー
Flux はClean Architectureに近い?
DDDの観点から見るFlux(Vuex)● UIコンポーネント - Presentation層にあたる。イベントの発火も。● Action - Application層。API通信だけでなくデータ処理に必要な手続き全般を担うためDomain層も兼ねることが多い。● Mutation - StoreのInput Port。ロジックは持たない。● Store - オンメモリでデータを永続化。外部への柔軟なOutput Portを持つ。
Vuexを利用したFluxパターン(課題)● Actionsにアプリケーションロジックが集約しがち。● ドメインモデル(振舞いを持ったデータモデル)ってどこに書くんだっけ?● Storeの構造がViewを意識したものになりがち● Gettersを全てのViewに用意するのは厳しい● Mutation(≒ Store)にロジックを書く人間が後を断たない(書けてしまうので)● 本当にTestable?
スケールしなさそう・・・
今回の設計方針・より厳密なレイヤー分割を行う・業務上の関心と技術上の関心を分離 → Domain Modelの活用(DDD Like)・シンプルなイベント伝達 → 単方向データフロー(Flux Like)・依存性も単方向にしてよりテスタブルに → Clean Architecture・フレームワークや外部のI/Fに依存しない → Gatewayの活用
スケールさせるぞ!!
ContainerControllerUse CaseRepositoryStoreEntityPresenterUI ComponentsQueryCommand
アプローチ● Controllerからイベントを発火● Usecaseにビジネスロジックを記述。DI(依存性の注入)はControllerで● データモデルの振舞いを定義するEntity層を用意● Repository内でStoreにアクセス● UIはPresenterで抽出したデータセットの写像である
実装編https://github.com/andoshin11/clean-architecture-example-vue
登場人物今回はNuxtつかってません!! (土下座)
Store● DomainごとにModule化● Actionsは持たない● Observableな箱としての用途● 無理やり型付けしてます
Entity● Interfaceの提供● Domainロジックの定義● Data Store Layerへの読み書きを許容(through Vuex API)
Repository● アプリケーション固有のDomainロジックを定義● Data Store Layerへアクセスを持つ(through Vuex API)
Container● UIの表示単位● Atomic DesignでいうPages?● ContainerごとにUse CaseとPresenterを持つ● Containerを1つのドメインとみなしたDucksなStore実装も可能
Use Case● アプリケーションに必要な手続きを定義● DIされてはじめて動く
Presenter● 表示要件に必要なデータをStoreから抽出・整形● 純粋関数として定義し、Computed プロパティにマッピング
Container View● Presenterの値をUI Componentに注入● Use Caseに依存性を注入する(DomainInjection)● 現状Controllerも含まれてしまっている
Testing● UIコンポーネント - Presenterの写像なので再現が簡単● Repositories - Data Store LayerへのRead & Write APIをテスト● Use Cases - Given ConditionとしてRepositoryとServiceのモックをDIするだけ● Entities- Use Casesと同様
まとめ:● いたずらにレイヤーを増やせば良いというわけではない● 依存の方向性を意識することで変更に強いアプリケーションに近づく● Vuexと周辺のテストツールを信用しきるならデフォルトのAPI叩く方が楽(今はGatewayを使っていない)● Clean Architectureの導入コストは高いので見極めが大事
これってVueじゃなくてもできるのでは?
We Are Hiring!!
参考:● https://qiita.com/kondei/items/41c28674c1bfd4156186● https://speakerdeck.com/takasek/10fen-tezhen-rifan-rusohutoueaakitekutiyafalseli-shi-2017● http://embryo.hatenadiary.com/entry/2016/12/16/011446● https://github.com/azu/large-scale-javascript● https://qiita.com/[email protected]/items/705360b357c2a00c9532