1日目の記事になります。オンラインデモカーソルキーで移動、スペースキーでポーズ、エンターキーでドロップ。power-assertなぜ3Dテトリス?業務で扱っている機能やアプリケーションではReduxを使っていてあまり困ったことがなく、1年以上のメンテナンスに耐えることができました。一方でReduxつらい、わからんという声は今でも一定数あります。そこで普段とは違ったタイプのアプリケーションを題材にしたら、これまで気付かなかった問題に出会えるかもしれないという期待をもってゲームを作ってみることにしました。ちなみに、最初に取り組んだゲームは3Dテトリスではなくライフゲームでした。少々脱線してしまいますが、Reduxのミドルウェア選定に悩む人には役に立つかもしれないので紹介しておきます。Reduxで副作用を扱うミドルウェアを比較する目的で作ったライフゲーム実装です。TodoMVCを書くのは嫌だし、ショッピングカートを実装しても面白くないし、悩んだ末のライフゲームになります。現在実装済みのミドルウェアは以下になります。3種類redux-observableredux-ship今のところ副作用といってもタイマーくらいしか扱っていなくて、少なくとも通信系を追加しないと比較にならないので頑張ります。得られた知見さて、話を戻してOberisを作る過程で悩んだ部分について書いていこうと思います。2種類のActionRedux、というかFluxではViewにStoreの状態を直接変更させる代わりにActionを介してStore自身に状態変更を任せます。このようなデータフローを採用することで、ViewがStore内部の知識を持たないようになり、Viewの再利用性が向上します。デザインパターンで言うところのコマンドパターンですね。これまで作ったアプリケーションではActionを使った分離で困ったことはあまりなかったのですが、ゲームを作ってみるとすぐに問題が出てきました。キー入力の取り扱いです。キー入力をActionとしてdispatchした場合、状況によってそのActionの意味が変わってしまいます。スタート画面でスペースキーを押したらゲームの開始ですが、ゲーム中にスペースキーを押したら一時中断になり、ゲームオーバー時に押したら新規ゲームになります。この問題解決にはいくつかアプローチがあると思います。キー入力をActionとしてdispatchし、Reducerで状況に応じて処理を分けるキー入力を受け取るハンドラーで状況に応じた適切なActionを投げ分けるキー入力を低レベルActionとし、それに応じた高レベルActionとして分離する1つ目はキー入力のハンドラでは何も工夫せずにほぼそのままActionとしてdispatchして、Actionを処理するReducer(とかSaga)で状況を判断して処理を分けます。素直なアプローチですが、コマンドパターン的にはある程度ドメインにおける意味を持ったものをActionとして扱いたい気持ちがあって、生のイベントをそのままdispatchするのはいかがなものか、という懸念があります。2つ目はキー入力のハンドラ側で状況を判断して適切なActionをdispatchします。ある程度の意味を持ったActionが流れることになるので1つ目のアプローチよりは筋が良いように感じます。生のイベントを扱うハンドラに状況判断のためのロジックが詰め込まれていくので密結合しすぎな気がします。3つ目は1つ目のアプローチと同様にキー入力のハンドラでは素直にActionをdispatchしますが、Reducerでそれらを直接処理しません。代わりに設けた中間層(Saga)で低レベルActionを受け取り、適切な高レベルActionをdispatchします。Reducerでは基本的に高レベルActionだけを処理するようにします。レイヤーを分けることで生のイベントを扱うレイヤーは他のアプリケーションで再利用できますし、ドメインの知識がそのレイヤーに漏れ出すこともなさそうです。いう記事で提案されており、読んだときは「へー、そんな方法もあるんだー」程度の認識でしたが、実際の問題に直面してみるとその利点が実感できました。Reduxではこういった問題に直面しなかったとしても、Actionをどう定義するかはとても大事なトピックです。大半の人は後者を選ぶと思います。実際のアプリケーションを開発しているとドメインとして意味のあるActionではなく、物理的なユーザー入力に対応したActionだったり、Storeに格納してある状態のデータ構造に依存したActionを定義してしまいがちです。できるだけそういったものに依存しない高レベルのActionを定義するようにしましょう。分割問題が出てきました。StateのネストはReducerが複雑化してバグの温床になるので避けたいところです。いえ、分割を工夫してどうにかなる感じでもなくとても困りました。その結果、もともとReducerにあったロジックのかなりの部分がSagaに移動してしまい、Reducerがスカスカになってしまったわけです。この状況が悪いのかどうか、というのは私にもわかりません。そもそもReducerは状態変更の門番ではありますが、当然のことながら状態変更だけがアプリケーションロジックではありません。アプリケーションによってどういうタイプのロジックがどのような割合で占めるのかは変わってくるため、Reducerで処理する以外のロジックが多いからそれが悪いとも言えないわけです。いうことでこれに関しては有用な結論が出ませんでした。いうのも、Reducerが過疎っても何か困ったことが起きているわけでもないですし、将来メンテナンス性が損なわれるような状況が生まれる気もしません。問題があるとしたら「それはもはやReduxなのか?」というツッコミに答えられないことでしょうか。さいごに1日目からなんだか気の抜けた記事になってしまいましたが、当初の目論見どおり、いつもと違ったアプリケーションを作ることで、いつもと違った問題に直面することができました。これによってあまり実感の湧かなかったトピックについて考える良い機会となり、視野が広がった気がします。問題は解決することでまた別の問題を生み出しているはずなので、また進展があったら記事にしようと思います。明日はまだ埋まっていません。upLoginユーザーは見つかりませんでした。