ブラウザアプリケーション以外のプラットフォームでのReduxはtoo muchか?

@Nkznとかが最近よく言ってるReactNativeではReduxじゃなくていいんじゃない?って話。Electronでも同じような議論が可能だと思うので、さらに一般化して「ブラウザアプリケーション以外のプラットフォームでJSで動くGUIアプリケーションでReduxは必要なのか?」という話をします。

結論から先に言うと、わたしも「ブラウザで動くわけじゃないなら、Reduxである強みってそんなになくなるよね」という立場です。

まず、前提として、Fluxは「状態管理パターン」で、Reduxは数あるそのパターンの実装のひとつである。ということを確認しておきましょう。

その上でReduxの特殊性は

  • Stateがピュアなオブジェクトで表されている
  • Stateの更新はイベントを契機に常に同期的に行われる
  • 以上2点により
    • いつでも状態のスナップショットをシリアライズ/デシリアライズ可能な形で取得できる
    • イベントを積み重ねれば常にそのスナップショットを再現できる

という利点を持ちますね。その結果として非同期なオペレーションと同期的なオペレーションでコードが全然異なってきてしまうというデメリットも同時に引き受けているわけだけれど、今回はそこについては詳細には触れません。

ところで、状態のスナップショットがシリアライズ/デシリアライズ可能であるという利点は「サーバー側で最初に状態作って、HTMLレンダリングして、シリアライズされた状態もクライアント側に渡したい」みたいなときにめっちゃ効いてくるので、SSRやるならReduxに乗っとくってのはかなりいい戦略になると思います。

しかし逆の見方をすると、Stateがシリアライズ/デシリアライズ可能であるという点がうまく効いてこないような場合にはReduxである必要は本当にあるのだろうか? という疑問が湧いてくるわけです。そして、SSRが必要ない、ブラウザアプリケーション以外のプラットフォームでは、あまりこのStateがシリアライズ可能であるという特性は活きてこないんじゃないか?というのが、わたしの考えです。

となると、今度は「Reduxならみんな書けるでしょ(ほんとか?メンテナブルなReduxアプリケーションをほんとにみんなが書けるのか?破滅してる現場結構あるっぽいぞ?)」を取るのか、「別のやりかたをイチから検討しましょう」のコスト支払ってRedux以外のやり方探すのかっていう話になってくるかなと思います。

というわけで、掲題の「ブラウザアプリケーション以外のプラットフォームでのReduxはtoo muchか?」については、わたしの立場としては「Reduxはtoo muchかどうかはともかくブラウザアプリケーションを取り巻く環境に対して too adaptive である。とは言えFluxパターンの実装として枯れてるしよく使われてるし、そういう"長いものにMackerel"的な良さはあるんじゃないですかね。」というあたりに落ち着くのでありました。

わたし個人的には「SSRみたいな特殊な事情がないなら、古き良きレイヤードアーキテクチャのようなやり方のほうが見通しがいい気がするねえ」くらいの立ち位置ですが、それぞれメリット・デメリットあると思うので、このあたりを出発点にチーム内で議論できるといいですねと思います。

Living in Peaceの「チャンスメーカー奨学金」の支援者になった

どこで知ったんだったか忘れたけれど、Living in PeaceというNPOがある。

www.kodomo.living-in-peace.org

詳しくはサイトを見てもらうのがいちばんいいと思うんだけど、ひとことで言うと、子どもの貧困対策をやってるところだ。そのうち寄付で支援できるのは「チャンスメーカー」ってやつと「チャンスメーカー奨学金」ってやつで、どちらも子どもの貧困対策であることは同じなんだけど、それぞれ性格がちょっと違うみたい。「チャンスメーカー」のほうは「児童福祉施設にいる間のための支援」、奨学金のほうは「施設出たあとの教育機会の格差を支援」というような性格になっているようだ。

ところで、話は変わって、わたしは、自分の今いる環境や仕事、それに使っている能力を得るために、ちゃんとそれなりのコストを自分でかけてきたという気持ちはある。大学に入ったあと、高校の友人が「しんぺいくんが受験勉強始めたあとめちゃめちゃ努力してるの見て"すごいな"って思ったよ」って言ってくれたりしたのは「あー、見てくれるひとは見てくれるんだな」って思ったし、プログラマになってからも様々なことを自覚的に、自律的に学習してきたつもりだ。その学習がなければ、いまの環境や仕事、能力は自分にはなかったと思う。

けど、一歩引いてみれば、「どうして自分がそのコストを学習にかけることができたのか」ということが見えてくる。わたしの親はわたしの教育にとてもお金と時間と手間をかけてくれて、そのおかげで、わたしは22歳で大学を卒業するまで、他の雑事にあまり気を取られることなく集中して勉強することができた。そのときの経験から「あ、学習すればわかる、できるようになるんだな」という実感も身につけることができた。

前回の記事で書いた「家庭の所得格差がそのまま学習機会の格差になってる」ってのはそういうことも含めた上でのことで、さまざまなめぐり合わせの上でたくさんの「学習の機会」を、偶然に、運良くもらった自分には、学習の機会の格差を少しでも再配分する義務があると思っている(この思いは、プログラマコミュニティから学んだ様々なことを別の形でプログラマコミュニティに還元しようとしている自分の気持ちともつながっている)。

ざっくり言うとそういう気持ちから、「チャンスメーカー奨学金」の支援をすることにした。月々1,000円から支援できるそうです(少額で継続的にできるのも良いっすよね)。「俺が(私が)学習に集中できたのはそういう環境があったからだよな」みたいなことを思うひとは(そうじゃない環境だったひともいるでしょうし、そこから努力で学習の機会を勝ち取ってきたひとのことは尊敬してます。でも、「だから誰だってできる!!」ってのは生存バイアスですので、できたらそういうことを言わないでほしいな……)、検討してみてもいいんじゃないでしょうか。

ところで、こういう話をパブリックでするのは正直なところ損しかなくて、インターネットではなにを書いても怒られが発生するし、「偽善者乙」みたいに言われてとにかく嫌な気持ちになって最悪なんだけど、「こういうのあるらしいよ」って伝えたいから書いた。けどどうせ怒られるんだろうな、知ってる。

N予備校にめちゃめちゃ感動したって話

今日N予備校の体験授業やってみたんだけど、普通に内容めちゃめちゃよくて、あれが月額1000円安すぎる。予備校の価格破壊だって思った。

わたしは「医療」と「栄養がきちんと取れるレベルの食事」と「教育」については、格差の再生産が是正され、富の再配分が積極的になされるべきである、という立場を取っている。でも、現状を見てみると、正直言って家庭の所得格差がそのまま学習機会の格差になってると思う。それを考えると、N予備校めちゃめちゃ素晴らしいと思う。ほんとうにめちゃめちゃ素晴らしい。

ただ、強制力のあるサービスではないから、結局学習習慣のある子じゃないとメリット享受しにくいのはあると思ってて、そのあたりには家庭の「余力」ってめっちゃ効いてくるので、あれで全て解決にはならないと思うけど、それでもクッソ高い予備校以外の選択肢ができたのほんとすごい。

実はわたしは高3のとき東進衛生予備校(有名講師の授業がビデオで受けれるやつ)に通って「地方でもこんなレベルの授業受けられるのまじ革命的だな」って思ったけど、東進クッソ高いんですよね。あのお金を親が出してくれたの、親になったいま考えるとまじですごいと思う。とはいえ内容が良いならきちんとそれを提供しているひとたちには高い収入を得てほしいというのもあって、今のアクセスしやすい価格を維持したままN予備校の講師陣や仕組みを作っているひとたちが5000兆円得てくれるといいなってほんとに思う。

そのためにはもっとN予備校知られてほしい、まずは世の中のお父さんお母さん、体験授業受けてみませんか。わたしはまだ子供幼稚園に通ってるけど、「これがこのまま発展してくれるなら子供の教育費に関する悩みがひとつ解決するな」って思ったよ。いま真剣に自分が月額1000円払って授業受けたいなって思ってる。

追記

ちなみに体験授業受けたのプログラミングコースじゃなくて数学です

ToKyoto.jsでScala.jsについてLTしてきました。

自分の発表について

speakerdeck.com

もう3日前のことになりますが、ToKyoto.jsでLTしてきました。Scala.jsの話です。スライドにあるし、このブログでも何度か取り上げていますが Scala.js 普通に実用性あります。とはいえ「実用性がある」と「使うべき」の間には高い壁がそびえているわけですが……。

ちょっと補足的な話をしておくと、ReduxやVuexが提供する状態管理パターンのよさはいくつかあって、

  • Single source of truth が守られる
  • 参照系と更新系が強制的に分離させられる
  • 以上の二点により、プレゼンテーション上でのデータの分割構造とロジック上のデータの分割構造が分離される
    • ヘッダーで使う情報とメインコンテンツで使う情報が互いに関連するみたいなやつが、おなじデータソース見るの簡単とかそういうこと
  • 自分でデータの変更をobserveしなくていい
  • 状態がピュアなJS objectで表されている

というのがわたしの認識するメリットです。そのうち、最後のメリットに関しては「SSRするからInitialStateをサーバーサイドでバーっとjsonに吐き出したい」とかそういうときにとくに効いてくるもの(参考: You Might Not Need Redux – Dan Abramov – Medium )という感じがある。

逆に言うと、そういう制約がないのであれば、「ふつうのLayered Architecture」をやりながら、Single source of truthを守り、コマンドとクエリを分離してやれば、ReduxやVuexが提供してくれるメリットをある程度教授しつつも、他のGUIプラットフォームで得たGUIアプリケーション設計の知見を活かすことができると考えています(データ変更監視するのはがんばるしかない)。

実際、Scala.jsに移植されたNekogataDrumSequencerBackLoggerはRepositoryがSingle source of truthになっていて(というかモデル層以下で状態を持つものはすべてRepositoryに寄せて、他を全てImmutableで書いている)、これはそれなりにワークしているなあという実感があります。

逆に言うと、ReduxやVuex的な「状態のスナップショットがJSONで取れちゃう」みたいなことがしたいときにはもう少し工夫する必要がありそうですね。

で、こういう「Layered Architectureでがんばる」みたいなやつはJSだとちょっとむずかしくて、いや、むずかしいっていうか、むずかしくはないんだけど、Scalaみたいないろんな言語サポートが欲しくなるな〜みたいなのがあって、そこで「Scala.jsありかもよ」っていう文脈ですね。

ほかのひとの発表について

@amagitakayosi さんのやつがとくにおもしろかったです。とくにMIDI信号をがんばってGPUに送るときの工夫の話が「うおー、おもしれ〜〜〜」って感じで、NekogataDrumSequencerもMIDIコン対応したくなりました(GPUには送る必要ないけど)。

その他

仕事ではJS書きまくってるんですけど、コミュニティ的にはいままであまりJS界隈に顔を出したことがなくて、JSコミュニティはアウェイな感じだったんですけど、twitterやブログで一方的に知ってて「話してみたいな〜」って思ってたひとと話してみたら先方もわたしのこと知っててくれて、その上で有意義なお話をたくさんさせてもらえてめっちゃよかったです。次はScalaっていうアウェイなコミュニティに顔を出してみたいなと思うのでScalaMatsuri応募するぞ!

Repositoryの後ろにネットワーク越しのシステムがあることの是非からRedux的世界へ

twitterで言ってたことを少し修正してまとめ。MVVM + Layered Architectureの文脈です。

最近、Repositoyとして抽象化しておけばそのデータがメモリ上にあるかそれともDB上にあるかネットワーク越しにあるか意識しなくていい、というのは嘘だと思ってる。パフォーマンスの観点除いても。


というのは、リポジトリが「非同期クエリ」を受け付け始めると、ReadStackの複雑性が跳ね上がるというのがあるからなんだけど、そもそもRepositoryへのクエリは必ず非同期であるというインターフェースにすればこれは問題にならないのかな


リポジトリを基本非同期に設計してもいいんだけど、そうすると同期的な表示を求めてくるUIプラットフォームと相性悪くなるんだよな


たとえばMVVMで考えると、モデルのイベントを購読して、イベント来たらクエリ投げてモデル以下にある状態を読み出してきて、自分のデータストアに書き戻すとデータバインドでViewが更新される、ってことすると思うんだけど、クエリが非同期だと、「イベント来たら非同期な操作投げる」ってなって、複雑さを増すと思うんだよ


そうなると、WebAPIは外部システムとみなして、WebAPIからデータ持ってくる操作は「外部システムからローカルのシステムにデータを取り込むコマンド」と考えてやったほうが全体的にシンプルになりやすい気がする


で、それやっていくと基本的に「状態の更新は常に同期操作」になって、なんとRedux的な世界観が近づいてくるんだよな。で、じゃあ非同期をどこで吸収するですかって話になったときに、middlewareっていう仕組み必要とするかアプリケーションサービスで吸収するかって話がある


わたしがRedux触ってみて良いと思ったのはsingle source of truthと、読み出しと書き込みが強制的に分離させられることと読み込みが常に同期的な操作であるって部分で、嫌だったのがmiddleware、ってのはこういうことです


Vuexのほうが好きな理由も同じです。ちゃんと非同期吸収ポイントが納得できる場所にある


あと、ReduxとかVuexはState更新したよってイベント購読管理しなくていいのが楽。けどそのトレードオフでStateの作り方をフレームワークから制約受ける。この制約を「良い制約」と思えるかどうかってのはあると思う。


これ単方向データフローを守ったMVVM前提で考えるからこういう結論になるんだなって思い至ってる


MVPならそもそも非同期であろうがなんだろうがそのタイミングでP呼び出すだけだもんな


ここで、ninjinkunさんと話した「MVVMは難しいパターンですよね」を思い出すのであった


あと単純に、Repositoryの後ろにネットワーク隠蔽しちゃうとAPIのレスポンス複数のVMから参照したいときとか、キャッシュをちゃんと制御したいときにも困る(リポジトリで透過的にキャッシュ?それ一時期web系ではやった"アンチパターン"だよね)


このへんのこと考えて設計した結果がBackLoggerのコードです。

github.com

今思うと、これ、RepositoryがいわゆるStateのポジションにいる、という考え方をしていると見て取れる。少なくともBackLoggerではこの考え方はかなりよく回ったと思っている。

追記:さまざまな反応

VuexとかReduxの制約が生きるシチュエーションについて

@shinpei0213: vuexとかreduxだとstateの作りに制約受けるの嫌なんだけど、InitialStateを外から与えてアプリをbootstrapしたいというようなことが起こる場合は良い制約だと思う

Repositoryってそういう責務かなあという話

@shinpei0213: そうですね。なので、記事では最後に「RepositoryをStateと見なすような感じ」という感じになっています。もしかしたらStateというのを生やして、RepositoryからStateに読み込む、みたいにすると用語の混乱がなくていいかなあと思ってるところです


@shinpei0213 ただ、同期で読み書きできる永続化層ならそれはStateとしてあつかっても不利益ないな(BackLoggerのSettingRepositoryの実装がそうなってる)というのもあり、「名付けをどちらに寄せるか」という問題かなと。


@shinpei0213 「このプロジェクトにおけるRepositoryの責務はこれです」が一貫しており、メンバーに共有するという制約付きですが


@shinpei0213 というかこの話実はイミュータブルドメインモデルと状態のライフサイクルの話と繋がっていて、かなり射程が深いので是非NDSで議論しましょう

Vue.js + Scala.js + Electron でMac用backlogクライアントを作りました

backlogというのは一般名詞のほうのbacklogではなく、ヌーラボさんのプロダクトのほうのbacklogです。

www.backlog.jp

だいたい土日の2日間でできたのでよかった。

作ったやつどこにおいてあるか

アプリ

BackLogger-darwin-x64_v0.1.1.zip - Google ドライブ

ソースコード

github.com

スクリーンショット

f:id:nkgt_chkonk:20170911103251p:plain

タイトルの通り、Scala.jsとElectronで作られています。

なぜ Scala.js と Electron で作ったのか

  • Scala.js がどれくらい実用的に使えるのかを知りたかった
  • Electoron の得意なこと、苦手なことを触ってみて検証したかった

というまっとうな理由と、

  • 来年のScalaMatsuriに応募するためのネタがほしいな〜
  • 「で?なんか偉そうに設計の話してるけど、お前はそれで何作ったの?」って言われたときにちゃんと答えられるもの一個くらいあったほうがよさそう

という邪な理由があります。

なぜバックログクライアントなのか(GitHubIssuesじゃねーのかよ!)

わたしが仕事で使ってるからだよ!

どうせ作るなら自分が便利になるもの作りたい。

アプリの仕様設計

とにかく自分のユースケースに絞って作ってあります。

バックログは、とにかく高機能で、非プログラマなディレクターにもわかりやすい素晴らしいプロダクトだと思っています。

しかし、高機能でいろいろできすぎるがゆえに、

  • 自分にアサインされてる課題の中から、未完了のものを一覧で表示して、
  • 次に片付けるやつ見繕ってそれを「処理中」にして
  • 開発して
  • backlogに戻ってそれを「処理済み」にして
  • 場合によっては自分で「完了」する

という一連の流れがちょっと面倒です。

というか、今書いていて思ったのですが、これはバックログのせいというより、部屋が汚い人間はブラウザのタブを開きっぱなしにする傾向があるという例のやつのせいで、「一覧表示してたタブどれだっけ」「わータブばっかりだーしょうがない新しいタブ開いて課題一覧また開こう」「あ、検索条件また入れなきゃ」「あ、またタブ開いちゃった」「開かれたタブの数が5000兆になった」となるわたしが悪い気がしてきた。

まあそれはそうとして、そういう問題で課題のclose漏れ起こして同僚に迷惑かけたりしていたので、

  • 「パッ」と自分がアサインされている課題一覧にアクセスできる
  • 「パッ」と課題の状態を変更できる
  • ソレ以外のことがしたい場合はブラウザでやってもらう(そのために各画面へのリンクだけ提供しておく)

という思想で作られています。

技術上の課題

まだ設計上甘い部分があって

  • APIから降ってきた生jsonをモデルにマッピングするTranslatorくん作りたい
  • テスト書きやすくできてるとは思うんだけどテスト書いてない
  • リリースが手動

とかそのへんを余裕があればやりたい

仕様上の課題

ぱっと思いつくのは3つ。

とにかくデザインがひどい。スクリーンショット見てもらったらわかると思うんだけど、いかにも「プログラマが片手間でやりました!!!」って感じ。だれか助けてくれ……

Windows対応。自分がWin持ってないので、Winで使いたいというひとがいれば考えるかもしれない程度の感じです。ただ、今後仕事でElectron使う場合はwin対応必須だろうからどっかで試してみたいかも。

マルチスペース対応。自分が必要になったらやるかも。

しばらく自分でも使ってみて、「これあったほうがいいな〜」って機能は追加していくつもりです。もし使ってくれる奇特な方がいらっしゃったら「この機能欲しい」みたいなのを

@

か、普通にIssueにヒョっと書いていただけると対応するかもしれません。

Nekogata Drum SequencerをScala.jsに移植した

Vue.js + CleanArchitectureのサンプルとしてNekogata Drum Sequencerを書いたのは今年の3月のことでした。

解説記事(README)を改めて貼っておきます。

そんな折、最近はScala.jsの調査をしていまして、実験としてこのシーケンサーScala.jsに移植する試みを行いました。つまり、 Vue.js + CleanArchitectureをScala.jsでやってみる、という試みですね。

ソースコードGitHubに上がっています。

github.com

デモはGitHubPagesから。

Nekogata Drum Sequencer written in Scala.js

で、実際に書いてみてどうだったのかとか、どう考えてこういう形になっているのかなどをToKyoto.js ― Kyoto.js in Tokyo - connpassでLTするので、来る人はToKyoto.jsでお会いしましょう。ToKyoto.js終わったらまたスライドをアップロードします。