Rxって正直使い方に気をつけないとアプリ全体のアーキテクチャを左右しちゃう時があって、さて今後どういう付き合い方をすればいいんだろうねって話をしがち
— そういう日もある (@shuma_yoshioka) September 27, 2019
実を言うと僕はもうアプリ全体のアーキテクチャとしてRxを行き渡らせるのが好きで、中途半端に入れるから負債になってるだけなんじゃね、という気持ちになることが多い
— そういう日もある (@shuma_yoshioka) September 27, 2019
本来モジュールごとにちゃんと独立性が担保されてなきゃいけないところを「Rx便利じゃーん」と中途半端に他のモジュールとのコミュニケート手段としても使っちゃったり、そのSubscriptionまわりの管理を適切に考えずに使っちゃうからダメなんじゃないかな
— そういう日もある (@shuma_yoshioka) September 27, 2019
Rxと一緒に生きる覚悟がないのなら、あんまり混在させずに特定モジュール内に閉じた使い方 (寿命管理を自前でちゃんとしてあげるのもそう) で終わらせないとそりゃ管理できなくなるよねっていう話
— そういう日もある (@shuma_yoshioka) September 27, 2019
覚悟っていうのは「このアプリのコードベースがRxとその周辺エコシステムのメンテナンスや品質が担保されている (または自分たちが担保できる) 期間で収まるか」という話で考えている。完全にエンジニアリングの理想を実現できているアーキテクチャというのは利用ツールを容易に置換可能なものなので。
— そういう日もある (@shuma_yoshioka) September 27, 2019
スッキリしたかったので頭の中身dumpしたhttps://t.co/tbjsCTCqqs
— そういう日もある (@shuma_yoshioka) September 27, 2019
どこまでいったってポエムなんだけど、じゃあ結局どういう風に考えようかっていう頭の中のことのdumpです。まとまってないけどごめんね。
モバイルアプリケーション開発におけるRxへの依存箇所でいうと、
という目的で記述された箇所とそこから続く処理全体へ続いている場合が多いと思う。あとはMVVMアーキテクチャ採用でデータバインディングやる都合上とかか。
Rxは非同期的なイベントの繋ぎ込みをめちゃくちゃ容易にできる反面、上手くハンドルしないとそれを行うためのコード全体に影響を与えてしまいうる。
こうしてプロジェクトの至るところで利用されたRxライブラリはツールの置換を困難にする場合がある。
その意味で影響範囲の広いライブラリを用いることは悪と言えるかもしれないのだけど、じゃあRxに相当するライブラリを現実問題として外せるんだっけ? というとかなり難しいケースがある。
現代のアプリケーションはたくさんの状態を持っていて、たくさんの非同期的なイベントを処理しなきゃいけない。さらにモバイルアプリケーションの事情になると、限られたリソースで最大限のパフォーマンスを出すべくライフサイクルなんて言い方をされる制約とも戦う必要がある。
要はモバイルアプリの世界は究極のステートフルで、人間には早すぎる場合すらある。
とするとRxに相当するものとは (新たな実用的パラダイムが提唱されない限り) 付き合い続けることになる。要はRxを使わざるを得ないコードベースにおいては、Rxはライブラリではなく既にパラダイムであり、言語の一部にすら相当しうるのである。そうやすやすとRxへの依存度を下げようなどと考えないほうがよい。
じゃあRx以外使いようがないんだっけ? というとそんなこともない。むしろここには再考の余地が大いにある。
Androidアプリケーション開発の文脈で言うと、同様の機能を提供するものは複数ある。
まずLiveDataはGoogleが提供しているAndroid向けのデータホルダライブラリだ。これはRxをモデルにしており、密にAndroidのライフサイクルと結合している。Androidアプリ開発に限れば最適な選択肢になりうる。
そしてKotlin CoroutineはKotlin言語機能のひとつとして提供される非同期処理ライブラリで、特定スレッドに依存しない部分や中断可能である部分などを利用し、Androidのライフサイクルに同期させることが容易である。
これらはRxに依存した既存コードが起こしている問題すべてを解決させることができるか? 答えはNOだ。LiveDataはRxよりもライトではあっても、同様にイベントの伝播を行うために複数モジュール間 〜 アプリケーション全体で依存しうるし、Kotlin Coroutineはさらにさらにライトではあっても結局複数モジュールごとに個別でlaunchを行うことになりうる。
しかし思い返してみれば、Rxは既にそのアプリにおいてはひとつの言語に相当する役割を担っているはずだ。だとすれば、言語選択と同じように考えればアドバンテージが見つかるのではないか。
アプリケーション開発における言語選択というのは面白くて、あくまでツールでしかないにもかかわらず「そのアプリの生き方」を決める。
言語機能として何が提供されるか? それが生産性の向上に繋がるか? というのはライブラリやツールの選定基準と何も変わらないが、このツールはコードの書き方を決定する。コードはビジネスそのものを作る。資産を作っているのである。
もしその言語の実行環境が未来永劫に失われたとしたら、ビジネス全体を別の言語へ移植するという大規模な作業をすることになる。
そのようなことが起こらないよう、十分に情報量があるか、メンテナンスできるだけのリソースが確保できるか、将来に渡っての品質が担保されているか、などを慎重に調査したはずだ。
言語もツールであり、言語に相当する役割を担ったライブラリがあるならば、それは同じ選定基準を持って選択すればよい。すなわち、一緒に生きられるかだ。今メンテナンスしているコードもいつかは寿命が尽きる。その寿命に耐えうるかで言語選定をしたのと同様に選べば良い。
さきほど挙げた3つはどれもOSSだ。すなわち自分自身がメンテナンスをする限り寿命は永遠だ。しかし現実問題としてそれは難しい。
Androidアプリであれば、Androidプラットフォームの寿命が尽きたと同時にそのアプリの寿命は尽きる。それほどまでにAndroid向けアプリはAndroidプラットフォームに依存したコードベースとなっている。だとすればLiveDataはAndroidプラットフォームのためだけにGoogleが開発しているという点で、アプリの寿命に耐えうるかもしれない。
そのAndroidアプリがKotlinで書かれているとしたら、Kotlin言語の寿命が尽きたと同時にそのアプリの既存コードの寿命は尽きる。だとすればKotlin Coroutineはコードの寿命に耐えうるかもしれない。
もちろん期待してはいけない。プラットフォームのAPIから独立したライブラリは容易にクローズできるし、言語機能の一部が非推奨になることもよくある話だ。
しかしもしあなたがRxに依存したコードと別れるべきだと捉えるのだとしたら、そのアプリはRxと一生を共にできなかったのかもしれないし、次のパートナー選びは慎重になるべきだ。一生付き合うことになるかもしれないのだから。
もちろん、リアクティブプログラミングに相当するパラダイムから離れるという選択肢も忘れてはいけない。たとえばFlux、Reduxというアーキテクチャはこれに取って代わるものかもしれない。それにRxの台頭以前はそれがなくともなんとかやってきたはずだ。
さらに言えば、リアクティブプログラミングとの付き合い方を見直すという方法もある。各モジュール内で発行し、サブスクリプションを管理し、他のモジュールへ当該パラダイムを漏れ出させないという方法だ。Kotlin Coroutineなら使い方次第ではこうなるかもしれない。
しかしそれは本当にやりたかったことだろうか。結局のところリアクティブプログラミングのパラダイムによって実現したかったイベント伝播の容易性は失われ、本来のboilerplateを書き続けることになるのではないだろうか。
もし既存のコードがこれまでRxによるイベント伝播にベッタリだとしたら、それらを剥がすコストを払えるだろうか?これは言語を変えるのに相当する労力が必要かもしれない。
もし最初に言語選択を誤ったのだとしたら、はたして他言語への移植を本当にすべきなのか、そのコストは効果に見合うものなのか、よく考えたほうがいい。