実況中継シリーズ Vue.jsで実現するMVVMパターン Fluxアーキテクチャとの距離 - Re.Ra.Ku アドベントカレンダー day 13
前説
丸山です。Re.Ra.Ku. アドベントカレンダー13日目の記事です。前日はiOSアプリのUIをコードで書いてみる話でした。明日はおそらくScalaの話になると思います。
さて、以前も話題にしましたが、builderscon2016が先日開催されました。チケットは3hでSOLD OUT。プラチナチケットと化した参加権ですが、発表する側ならば実質無料で参加し放題!これはいっそ申し訳ないレベルでは!?
というわけで、せっかく発表したのでその内容をなるべく多くの手段で共有したい。そう思い、今回も実況中継シリーズを弊社テックブログで行います。実況中継シリーズというのは、プレゼンをブログで再現するアレです。なお、実際のプレゼンは動画になってYoutubeにアップロードされております。builderscon公式サイトのセッション詳細ページからもご覧いただけますので、よろしければそちらも合わせてご覧ください。なお、先日行われたNDS#50でも再演を行いました。
Vue.jsで実現するMVVMパターン Fluxアーキテクチャとの距離
導入
本日はこういうタイトルで発表させていただきます。よろしくおねがいします。
簡単に自己紹介をするとこういうものです。文学部卒の文系プログラマなんで、文系プログラマdisには敏感です。Scala好きです。
Scalaスキーとか言っておきながら今日話す内容はタイトルの通りJavaScriptなんですが
Vue.jsの詳しい使い方とか、MVVMとMVPとの違いとか、あるいはモダンなJavaScriptってこう書くんだぜみたいな話はしませんので、そのへんに興味がある場合はちょっと求められているものを提供できないかもしれません。
Aパート Vue.jsで実現するMVVMパターン
今回はAパートとBパートに分かれています。Aパートとしては、MVVMというアーキテクチャパターンでPDS(PDSについては後述します)を実現するといいことがあるよ、という内容について触れ、Bパートでは最近「流行り」のFluxアーキテクチャってのはそんな中でどう位置付けられうるのか、という話をします。ではまずはAパートです。
概念的な話から入ってもいいのですが、わたし自身どちらかというと帰納的にものごとを理解するタイプなので、具体的な例題を交えてMVVMとPDSについて見ていきましょう。今回は、画像pickerを例題にします。これがどういうものがというと、はてなフォトライフみたいな場所があって、そこにユーザーがいろんな画像をアップロードしているとします。画像pickerは、そのサーバーに対して「最近投稿された画像をください」とリクエストして、画面に描画します。で、その画像をクリックすると、クリックされた画像は「選択状態」になり、「選択された画像」という部分に表示されます。この「選択された画像」をクリックすると、その画像は「非選択状態」となり、「選択された画像」一覧から消えます。
ちょっと文章で書かれてもわかりにくいと思うので、動画で見ると、こんな感じです(プレゼンではこれ動画になってます)。
画像picker without MVVM パターン
こういうのをこれから作っていくわけですが、PDSがどんな問題を解決するのかを見るためには、まずは「それがないときに何が困るのか」を見ていく必要があります。そのため、まずはjQueryでべたっと書いて行ってみましょう。
さて、jQueryでがんばる場合、HTMLはこんな感じになるんじゃないかなと思います。class="selected-images"
っていうからっぽのdiv
タグと、class="recently_posted_images"
っていうからっぽのdiv
タグがあってこの中身をjQueryでゴリゴリっと書き換えていく感じになりますよね。
で、JavaScriptのほうはこう。あとで細かく読んでいくんでちゃんと読まなくていいです。クラスがいっこあることだけ見ておいてください。
じゃあこのクラスがなにをやっているかっていうと、まずコンストラクタでjQueryのエレメント、っていうんですかね、それを受け取って内部に保持しておいて、
showRecentlyPostedImagesっていうメソッドを叩くと、念のためrecentlyPostedImages
をからっぽにして、ajaxでサーバーから画像一覧持ってきて、それをぐるぐるぐるっと回してimgタグ用意して、ハンドラ設定して、recentlyPostedImages
にぽこぽこぽこっとappendして行っています。
で、さっきimgタグの設定したハンドラが叩かれるとselectImage
っていうメソッドがinvokeされます。ここでは、クリックされた画像のsrc
要素見て、おなじsrc
持ってる画像がすでに選択されてたらなにもしないでreturn。そうじゃないなら新しくimg
要素つくって、やっぱりハンドラ設定して、で、selectedImages
にぽこっとappendするみたいなことしてますね。
まあこれでもちゃんと動きます。動くんですけど、すでに結構嫌な予感してますよね。具体的にどういう嫌な予感がするかっていうと、
まず、見ての通りDOMをコードで作ってますよね。
これ、もし「最近選択された画像のところに新しいcss当てたいからclass設定したいな〜」なんてときに、HTML上にはそのDOMが書かれてないので、「えっとこのimgを作ってるコードはここだから」みたいな感じで、ただcss当てたいだけなのにJSの森に足を踏み入れないといけないわけです。うーむという感じしますね。
問題ってそれだけじゃなくて、たとえばハンドラがとっちらかってるっていうのも問題です。選択された画像のクリックハンドラはここで作ってるし
最近投稿された画像のほうのクリックハンドラはここで作ってますよね。散らかってます。
さて、そうすると、たとえば「画像クリックしたらなんか画面がぶっ壊れた!!バグってる!」みたくなったとき、まずは「ところでこのハンドラどこで設定されてるの……」って感じで、またしてもJSの森に足を踏み入れることになるわけです。うーむ。地獄っぽい。
問題はまだまだあって、今って状態がDOMにしかないんですよね、これ、どういうことかというと、「選択された画像はどれか」っていう情報が、DOM上にしかない。そのため、「この画像は選択されてるか」を確かめるためにDOMをさらう必要があるわけです。問題はそれだけじゃなくて、
たとえば「選択された画像一覧をサーバーに投げて、サーバー側でそれを永続化しておいてほしい」みたいな要件が出たときにも困りますよね。選択された画像一覧を得るために、いちいちまたDOMをさらわないといけないわけです。不毛すぎる。
まあそんな感じで、何も考えずにDOMをいじっていくと、こういう感じで破綻が近づいてくるわけです。
ウゥゥゥっていう感じですね。
PDSとは
で、諸悪の根源はなにかってことを考えるんですけど、どうやらこれは「UIを実現するためのコード」と「アプリケーションの挙動」が密結合しているのが問題っぽいぞ、と思い立つわけです。
これ別にわたしが思いついたわけではなくて、そもそもMartin Fowler先生がPresentation Domain Separationっていう考え方を言ってるんですよ(プレゼンテーションとドメインの分離)。
PDSについては以前わたしもやぱちーで発表をしているので、それを参照してもらってもいいかもしれません。
で、今回の話に必要なところでいうと、要するにPDSっていうのは「UIを実現するプラットフォーム依存のコードと、それ意外の部分を分けよう」っていう考え方です。
ちょっと重要なポイントとして、「ドメイン」っていう言葉があるので、「あーこれってDDDでやったところだ!」なんて思われる方もいるかと思いますが、DDDにおける「ドメイン」という言葉とPDSにおける「ドメイン」という言葉は別のものを指しています。ので、DDDでいうところの「ドメイン」のことはPDSについて語っている間は忘れてください。PDSでいうところの「ドメイン」というのは、「UI(プレゼンテーション)以外すべて」を指します。
画像picker with MVVM
さて、PDSという考え方があることはわかりましたが、じゃあそれはどうやったら実現できるのでしょう。実はMVVMに限らず、MV*はすべてPDSを実現するためのアーキテクチャパターンです。今回は、Vue.jsを利用してMVVMパターンを実現してみましょう。
さて、Vue.jsを利用したアプリケーションの場合、ViewはスライドのようなHTMLテンプレートとして表現されます。v-for
とか{{i.url}}
とか見慣れないアトリビュートなどが出てきていますが、Vue.jsはこういうアトリビュートを利用してHTMLテンプレートを書くスタイルなわけですね。
さて、jQueryでがんばっていたときと比べて、HTMLはどのように変化したでしょうか。まず、jQueryで頑張っていたときにはHTML上に出現していなかったimg
タグが、このテンプレートには出現しています。これはどういうことかというと、DOM構造が余すことなくHTML上で表現できているということです。これなら、img
タグにcssを当てたいから新しいclassを設定したいなんてときにも、テンプレートをいじるだけで済みます。あと、@click
という見慣れないものがあると思いますが、これはVue.jsが提供するイベントハンドラです。こうして、イベントハンドラもHTML上で表現することで、「イベントハンドラがJS上にとっ散らかる」という問題も解決できました。
改めてポイントをまとめておくと、こういう感じです。
なかなか良いですね!
さて、Viewについては見てきたので、今度はモデルを見てみましょう。
モデルってどういうものかというと、その名の通り「アプリケーションをモデリングしたもの」です。モデリングっていうのは雑にいうと「具体的なあれこれから具体性を引っぺがして抽象化すること」ですよね。今回も、具体的なHTMLとかフレームワークとか関係なく、ピュアなJavaScriptでアプリケーションの挙動を書いてみましょう。
今回のアプリケーションならば、「最近投稿された画像」と「選択された画像」というのが動的な要素なので、コンストラクタでそれらをプロパティをして宣言しておきます。
また、今回のアプリケーションの動きとしては、「サーバーから最近投稿された画像を読み込んでくる」「画像を選択する」「画像を非選択にする」という挙動があるので、それらをメソッドとして表現していますね。
さて、これでモデルを記述できました。「DOM」だとか「imgタグ」だとか「css」だとかそういうViewのことばがModelに出てきていないところがポイントです。また、選択された画像についてもModel内に状態を持つことができています。これなら、「じゃあそれをサーバーに送ってよ」と言われたときにらくちんですね。
よい感じです。
さて、これでViewとModelができました。図にするとこんな感じです。プレゼンテーションとドメインが分かれています。
ところで、ViewとModelが分かれているのはいいのですが、今はViewとModelがまったく無関係に存在しているだけで、これではアプリケーションが動きません。そこで、ViewとModelの間でどうにかしてコミュニケーションする必要があります。ここをやってくれるのがMV*の*部分です。当然、MVVMで言えばVMがここをやってくれるわけです。
図にするとこう。よく見る図ですね。ここでポイントは、VMが「Presentation」の側に置かれていることです。VMを「Domain」の側においてしまうと、「UIを実現する以外のコード」がどんどんViewModelに書かれるようになってしまい、ViewModelがどんどん太ります。いわゆる「FatController問題」とか「SmartUI問題」にぶち当たることになるので、あくまでVMはPDSで見た場合「Presentation」側の責務を負っているということを意識しておいてください。
さて、それでは、Vue.jsにおけるVMはどのような責務を負うことでVとMの間を仲立ちしてくれるのでしょうか。Vue.jsのVMは、3つの責務を持っていますが、そのうちのひとつが「Viewのための状態ストア」です。
Vue.jsにおいて、VueというクラスのインスタンスがVMの役割を持ちますが、それのdataというプロパティがViewのための状態ストアとなります。今回ならば、selectedImages
とrecentlyPostedImages
を定義していますね。
ここで定義されたものは、Viewからはv-for
とか{{someVar}}
とかそういうVue.jsが提供するシンタックスを利用して参照することができます。
で、ここがVue.jsのおもしろいところなんですけど、VueModelのdataを書き換えると、databindという仕組みを通じて、ViewであるHTMLが書きかわります。例えばここでは5秒後にVMのrecentlyPostedImages
を書き換えていますが、これを実行すると、
こうなります(プレゼンではここは動画でした)。リロードした瞬間は「最近投稿した画像」が空で、5秒待つと、VMの値が書き換えられて、はい、Viewが再描画されて画像が出てきましたね。
こういう感じで、「databinding」という機構を利用して、ViewModelとViewの間をつなぐことができました。
さて、ViewModelの2つ目の責務は、「Viewからのイベントを待ち受けてModelをdispatchする」です。
Viewに記述された@click
というのがイベントハンドラであることは説明しましたが、たとえばここでselect
が呼ばれると、実際にはVMのmethods
に定義したselect
が発火します。
じゃあVMのselect
メソッドは何をしているかというと、自身が保持するModelのselect
メソッドをdispatchしているだけです。
Modelのselect
メソッドでは何が起こるかというと、Model自身のselectedImages
を書き換えて、Modelの状態に変化を起こしています。
ここまでを図にするとこうですね。ViewModelがViewのイベントに応じてModelをぶっ叩いて、Modelの状態が変わります。
さて、これだけでは、Modelが書きかわるだけで、UIは書き変わりません。そこで、Vue.jsにおけるViewModelの三つ目の責務を見てみましょう。「Modelの変化を監視し、自身の状態ストアを変更する」です。Modelが変化したときに、その変化をキャッチして自身のデータを書き換えれば、databindingでViewも書き変わりますよね。
で、Modelの変化をキャッチするんだから、まあここはObserverパターンの適用でしょう。
このへんはいろいろ工夫の余地(Rx使う?とかいろいろ考えられるでしょう)がありますが、今回はそこは論点ではないので愚直にObserverパターンを適用しましょう。
通知くんを作って
Modelが通知くんを保持して
Modelのプロパティが書き換わったことを通知するためにsetterでこの通知くんをfireします。
ViewModel側は、あらかじめこのModelの通知くんを監視しておいて、変化があればそれを契機に自身のデータを書き換えます。
図にするとこういう感じですね。
さて、このみっつの責務をもったViewModelの完成系はこんな感じになります。
Viewのための状態ストアがdata
にあって
methods
でViewからのイベントをModelをdispatchして
Modelの変化を監視して自身を書き換えるように設定しておくわけです。
これらを図に表すとこうなりますね。
まず、Viewからイベントが起こり、それをVMがキャッチします。
VMはイベントに応じてModelのメソッドをdispatchします。
また、VMはModelを監視しているので、Modelに変化が起こったら自身を書き換えます。
databindingによって、Viewが再描画されます。
矢印は依存の方向を表していますが、この時Domain側がPresentation側に依存していないことに気をつけて下さい。
DomainがPresentationに依存して、UIの言葉をしゃべりはじめると、せっかくMVVMを利用してPresentationとDomainを分けたのに、Domain側にどんどんDOMとかが漏れてきて、だいなしになってしまいます。MVVMパターンを利用して、DomainがPresentationに依存しないようにする、という形で、PDSを実現させているわけですからね。
さて、MVVMによってPresentationとDomainの分離に成功しました。
PDS導入前は、UIとそれ以外がごちゃごちゃに書かれることによっていろいろと問題が出ていましたが、
PDSを導入することで、各レイヤーの責務が明確になり、拡張に対して開かれた感じに設計することができました。
とっても良い感じですね!
ところで、ModelはピュアJSなのでテストしやすいのはわかりましたが、Presentation側のテストってどうやるの?って話についてはしませんでした。ブラウザに依存しちゃうようなPresentation側のテストについては、id:shiba_yu36 さんが良い記事を書いているのでそちらを参照してください。
さて、ここまででAパートは終了です。
Bパート:Fluxアーキテクチャへの接近
さて、AパートではMVVMパターンを利用することでPDSの実現ができる、ということについて見てきました。Bパートでは、Vue.jsによるMVVMパターンがFluxアーキテクチャに接近してゆく様を見ることで、FluxアーキテクチャとMVVMパターンの距離を見ていきたいと思います。
さて、まずは「Fluxアーキテクチャってそもそもなによ」って話から見ていきたいと思います。なんかFacebookが言い始めた概念で、MV*の陥る問題への解決として提言されているようです。
こういう記事があるので、中身を見てみましょう。
すると、「MVCって、MとVの間に双方向データフローがあるせいでえらく複雑になるよね〜」
「Flux使って単方向データフローにするとめっちゃいいぜ!!!」
ってことがかいてあります。
Fluxの基本的なアイデアは、「MとVの間の双方向データフローなくして単方向データフローにしようぜ」ってことだと言えそうです。
じゃあ単方向データフローってなに?って話なんですけど、よく見る図はこういうやつですよね。
ViewからActionが発火して
Dispatcherがどんなロジック呼ぶか決定して
そのロジックがStoreを書き換えて
結果がViewにレンダリングされる。
こういうフローをぐるぐるぐるぐるぐる回すことでアプリケーションを実現しよう、ってアイデアですね。
ところで、われわれのMVVMと比べてみるとどうなるでしょうか。
まず、Viewからアクションが発生して
ふむふむふむ。Viewでイベントが発生して
Dispatcherがどんなロジックを呼ぶか決定して
ふむふむふむ。VMがModelのどんなメソッドを呼ぶか決定して
そのロジックでStoreが書き換わって
ふむふむふむ、Modelが書き換わって
その内容でViewが書きかわる
その内容でViewが書きかわる
おなじでは!?!?!?!?!!?となるわけです。
実際、Bパート冒頭で貼った記事にはこういう反応もあるんですね。「ああ、君たちはMVCを再発明したんだね!そしてそれに違う名前をつけたんだ!!」みたいなことが書かれてしまっています。
実際、注意深く設計されたMV*は、データフローは単方向になります。
じゃあFluxって単なる流行なの?意味ないの?というと、
そんなことはなくて、ここから先は独自研究って言われちゃいそうですが、Fluxにもちゃんと価値はあって、そのうちのひとつが単方向データフローの強制です。
MVVMと言えば尾上さんのこの有名な記事がありますが、ここには
それを踏まえて考えれば、ViewModelに公開するModelのインタフェースは以下の二つしかありません。
- Modelのステートの公開とその変更通知
- Modelの操作のための戻り値のないメソッド
ということが書かれていますね。
実際、わたしたちのアプリケーションでもVMはModelの戻り値のないメソッドを叩いているだけでした。
逆に言うと、もとの記事にある通り、戻り値のあるメソッドを叩いてその結果を利用してしまえば、VMとMの間に無駄な依存関係ができてきてしまい、ここで単方向のデータフローもぶっ壊れます。
[
もう一度言うと、「注意深く設計された」MV*は単方向データフローになるんですね。しかし現実的な問題として、
われわれは注意深くないのです。大切なことなので光らせておきます(プレゼンではこのスライドの文字がポケモンフラッシュしました)。
まあそういうわけなので、仕組みで縛って単方向のデータフローを強制するというのはなかなかに価値のあることなのではないかと思います。
もう一点、Fluxアーキテクチャを実現するフレームワークとかはだいたい単一のStoreを持つようなものが多いのですが、この単一のStoreというのもFluxが提供する価値として数えていいとわたしは考えています。
この価値について考えるためには、素朴なVue.jsの難しさについて、もう少し深く見ていく必要があるので、もう少しVue.jsについて深く見ていきましょう。
Vue.jsでは、VMをサブコンポーネントに分けて、Presentation層を分割統治することができます。Presentation層を分割統治するなら、Domain層も分割統治したいですよね。やってみましょう。
親のコンポーネントのViewでは、こんな感じで子コンポーネントを配置します。VMでは、どのコンポーネントがどのVMをロードするか書いてあげます。
で、子VMのほうでは、それぞれ選択された画像のみに関心を持つModelや最近投稿された画像のみに関心を持つModelを保持するようにしてみました。
図にするとこういう感じです。
これでうまく動けばいいのですが、ちょっと問題があります。
というのは、今回は「選択された画像」というのは「最近投稿された画像」をクリックすることによって初めて生まれるものなので、selectedImages
というコンポーネントがrecentlyPostedImages
のイベントに依存しているのです。
困ったときには公式を当たってみましょう。
あるコンポーネントで起こったイベントを他のコンポーネントで受け取りたい場合はEventBusを利用するといいよってかいてあります。
しかし、これ、ちょっと嫌な予感がしますね。単純な例ならいいのだけれど、複雑になってくると、このEventBusが渋滞してスパゲッティ化していくことが容易に想像できます。
問題はそれだけではありません。「VMとMの配線問題」と私は呼んでいるのですが、そもそもVMとMはきれいに1:1になるのでしょうか?画面上の右上と左下に、同じ情報を参照する要素があり、片方が編集されたらもう片方も同期して変更されたい、というようなことは、ざらにあります(未読通知とかそういうのを想像してみてください)。この場合、複数のVMが同じModelを参照すればいいでしょう。
しかし、これも複雑になってくると、VMとMの間に複雑な依存関係が生まれてきてしまうことが想定されます。
ウゥゥゥゥっという感じですね。
とは言え、本当はこれは問題ではなくて、今回で言えば、本来凝集性が高いはずのModelをVMに引きづられて分けてしまったりしていて、「そもそもDomain側の設計がタコ」というのが問題だったりするわけです。Model側、つまりDomain側が適切に設計されてさえいれば、こんな複雑なことには本来ならないはずなのです。
とは言え、私たちは注意深くないのです。重要なことなのでまた光らせておきます。
そこで、困った時は公式のドキュメントにあたります。そうすると、「もっと複雑なときはstate-management-patternを見に行ってくれよな」とかいてあって、リンクが貼ってあります。そこを見に行きましょう。
そこを見に行くと、
- まず単一のStoreを用意します
- そのStoreに状態をもたせます
- そのStoreの状態をmutateするメソッドはStoreに生やします
- そのStoreのstateを複数のVMで共有します
- 各VMは単一のStoreに対してメッセージを送ることでStoreに生えたメソッドでStoreを更新します
というようなことが書かれています。
これってどっかで見たような話ですね。
実際、Vue.jsの公式サイトでは「こうしてStoreパターンを通じてFluxアーキテクチャにたどり着きました」みたいなことが書かれているわけです。
さて、こうして見てみると、Fluxアーキテクチャというのは、見方を変えると、MVVMパターンに対して、単一の大きなModelを持たせ、単方向のデータフローを強制したもの、とみなすことができるでしょう。
図にするとこうです。
えっ!?!?!?!?!?!?
って感じですよね。嗅覚の鋭いひとはすでに戦々恐々としてると思うんですけど、
「巨大なStoreってまじ!?それが便利って言ってるのって、グローバル変数はどこからでもアクセスできて便利って言ってるのと何が違うの!?」って感じですよね。怖い。
とは言え、実はそんなに怖くないんですこのStore。なぜなら、まず、StoreをmutationできるのはStoreだけです。そのStoreがどのような状態を取りうるのかは、Storeを見ればわかります。また、Storeだけで全てをやる必要はありません。たとえばフォームのバリデーションをしたいなら、そのフォームをモデリングしたFormモデルのようなものをStoreが保持し、そいつに仕事をさせればいいわけです。また、状態の数があまりに多くて見通しが悪ければ、Storeの内部をmoduleという単位に分けて分割統治すればいいのです。実際、Vue.jsのfluxライクな拡張であるVuexのサンプルコードでは、Storeをmoduleという単位に分けています。
図にするとこうですね。Presentation側からは一枚岩に見えるStoreですが、Domain側に注目すればそれは適切に分割統治されています。
見てきたように、単一のStoreは怖くないんですが、ここを適切に分割統治するためには、適切な設計が必要になってくるわけです。なおかつ、Domain側をどのような視点で分割すべきか、という問いには万能の答えはなく、そのアプリケーションがどのような特性を持っているのかを技術的、ビジネス的双方の視点から紐解いて導くしかないものです。まあ銀の弾丸はないので、がんばってやっていきましょう。
そんな感じで、Bパートのまとめに入っていきますが、Fluxの価値ポイントとしては、
- PresentationとDomainの分割構造は一致しない
- 巨大なStoreという窓口を設けることで、Presentatonからは単一の窓口に見える
- それによって、Presentationの分割構造に引きづられずにDomainを設計できる
ということが言えるのではないでしょうか。なんというか、責務の分割、という普通のことを言っていますが、
ある種の問題の解決としてFluxアーキテクチャに接近していくことが見て取れたと思います。
ここでBパートは終了なので、まとめとしてCパートに入っていこうと思います。
- Veu.jsを利用することで、MVVMパターンを導入し、PDSが実現できることを見てきました。
- ただ、素朴なVue.jsで綺麗な設計をするのはなかなかに難しい、ということも見てきました
- そこで、Storeパターンを利用してFluxアーキテクチャに接近することで、問題の解決への補助線を引けることを見てきました。
- とはいえDomain側の設計はしっかりやらないといけないので、がんばってやっていきましょう。
なお弊社はメンバー募集中です。
Q&A
以上で発表内容は終わりですが、ここから先はbuildersconとNDSで出てきた質問に返答していきます。
- Q.Viewのコンポーネントの再利用したいって場合ってどうするのがいいの?
- A.良い質問です!Viewのコンポーネントの再利用は幻想だと思っています。「似たような見た目だけど違う役割」みたいなものを再利用するのはやめたがほうがいいと思います。
- Q.PDSはわかったけど、localStorageとかってプラットフォーム依存だけどUIのコードじゃないよね、どう考えればいいの?
- A.良い質問です!PDSという考え方で言えば、Domain側です。しかし、特定のプラットフォームに依存するという意味でいうと、少しP側と性格が似ていて、直接依存するとポータビリティやテスタビリティが悪くなりそうです。ここはちょっと分離したいですよね。そのときに用いるべきなのが「infrastructure層」という考え方です。そのような特定のプラットフォームやミドルウェアに依存するコードはInfrastructureという層に押し込んでしまって、Infrastructureを使う側はInterfaceを通じてそれを利用することで、Infrastructureへの依存を断ち切る(いわゆるDIPですね、そのための方法としてDIなどが使えるでしょう)ということができます。こういうことをやっていくと、「ヘキサゴナルアーキテクチャ」だとか「クリーンアーキテクチャ」だとか呼ばれるものに近づいていきます。