トップ 追記

2019-04-14 [長年日記]

_ JavaScriptでcompose可能なCommandとして諦めてPromiseを使うことにしようとした話

手頃なInteractorが見つからずほとほと困り果てた

Clean Architecture を読んで以来、依存の方向はともかく*1Interactor が気になって気になって仕方なかったので、ずっと調べてたんだけど、どーにも JavaScript で手頃なものがなくて困っていた。

そのものズバリとしては

vadimdemedes/interactor: Organize logic into interactors

なんだけど、残念ながら Node.js 上では動くけどブラウザに持ってきたら動かなかった。

ここから放浪の旅が始まる。

大きく Interactor や Command Pattern で調べると数年前にリリースしたきりみたいなのが多い。

angeloashmore/cleanroom: Compose your business logic into commands that validate input.

こういうやつ。どれも自分用に作ってみました感が強くて、乗っかるには躊躇する。

business logic方面

business logic で調べるとどうなるかというと、

辺りなんだけど、割と多機能かつちょっと DSL 感が出てくる。もっとシンプルなのでいいんだけど。

あと

trooba/trooba: Fast isomorphic lightweight framework to build pipelines for request/response, stream/response, request/response and stream/stream use-cases

こんなのも見つけて、へー面白いとは思ったものの、なかなかオーバーキルだなって感じ。

functional programming方面

command を compose できるくらいでいいんだよなーとこじらせてたら functional programming でいんじゃね説が出て来て ramdajs とか lodash/fp とか目移りし始めた。特にこれは面白かった。

Future は Promise よりも Lazy に扱えて、Fluture は functional な flavor も乗ったもの。とても面白いんだけど、明らかに語彙が増えすぎて目的から外れるのでやめた。

Promiseでいんじゃね説

まーとにかく右往左往したが、Promise で Promise の結果をまとめるのでいいんじゃねーの? というところに戻って来た。例えば以下のようにすると

function p1() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      reject('reject p1')
    }, 50)
  })
}

function p2() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      reject('reject p2')
    }, 20)
  })
}

p1()
.then(p2)
.catch((err) => console.error(err))

普通に p1 で止まる様子が console で確認できる。極めてフツー。ただし、これだと command 失敗した際に実際どうするかを catch の中だけでうまいことやるのは難しい。

もちろん工夫はあって、

function exec() {
  const eventId = ulid()

  pubsub.publish(`start ${eventId}`)
  p1(eventId)
  .then(() => p2(eventId))
  .catch((err) => pubsub.publish(`fail ${err}`)
}

みたいなことをすると eventId とともに記録を外に出せるので、rollback とかあれこれ対処はしやすくなる。こんな感じの base class があると楽かもしんない。

あとは

辺りを入れるともうちょっと使い勝手もよくなるかも。

参考

Tags: JavaScript

*1 たぶん普通は逆で、みんなめっちゃ依存の方向、レイヤーの切り方の記事が多い


2019-04-10 [長年日記]

_ Model周りの複雑さがやや増してきたWebアプリのリファクタリングメモ

ほぼ Rails の話だけど、考え方は Rails や ActiveRecord に限らず適用できると思ってる。

現在の status としては適用済みのものもそうでないものも混ざっており、絶対全部オススメ!とかそういうキャッチーなものではない。

■ その1 - Interactor

□ 追加gem

collectiveidea/interactor: Interactor provides a common interface for performing complex user interactions.

□ 効果
  • いわゆる Service Object であり、記法が決まっている
  • 複数の Interactor を organize して一つの大きな Interactor を作ることができる*1
  • rollback ( ≒ transaction ) を実現できる*2
□ 適用範囲
  1. 複雑な処理のカプセル化(CでもMでもないいわゆるService Object)
    • 複数の処理が依存しあっていて、かつその結果を同期的にコントロールしたい、あるいはすぐにフィードバックを返したい
  2. Callback を分解して明示的な処理の流れに書き起こす
    • まず 1 があって C や M の外にやりたいことが明示されることが大事
□ メモ

伝統的なサーバサイドWeb開発アンチパターンとその対策 - C, V編 - - あーありがち(2019-01-20) で Fat Controller への対処としてとにかく Model を作れと書いたが、例えば複数の外部 API を叩くようなことがあるような場合、シンプルな PORO で実現しようとするとエラーハンドリングが面倒くさくなってしまう。

cf. Fowler EAA の Notification

そこで Interactor を入れることで処理全体の成否や rollback などの記述しやすさを期待できる。また Interactor という名前がいかにも外部 API やユーザーのアクションとの間に立ちそうで、少なくとも単なる Service Object という捉え方よりよいと考えている。

また Callback の分解については、例えば処理A, 処理B, 処理C があって、画面1からは処理AとCを、画面2からは処理AとBを行う、

画面の種類 画面1 画面2 画面3
処理の流れ 処理A 処理A 処理B
処理C 処理B

みたいなものがあった時に Fat Controller を避けるために 処理 A の callback で処理を流していく、みたいなことをやると callback に条件分岐が入って変に複雑になってしまう。

これを素直に Interactor で書き起こすと分岐が減って読みやすくなるし callback が減ると暗黙の依存が減る。結果、メンテナンス性を高く維持しやすくなるはず。

□ 参考

似た gem として

もあるが、やや Flow Control の DSL 色が強すぎると感じた。

■ その2 - CQS

□ 追加gem

なし

□ 考え方
  • Command と Query を分ける
  • Command の分離は上の Inetractor を使って明示的に外にくくり出す
  • Query は Thin Read Layer の class に分離する
□ 適用範囲
  • Model
□ メモ

Fat Model は Fat Controller よりはるかにマシではあるが、Model には様々な役割があり、その役割によって異なる複雑さが一つの Model の中に収まってしまうことで、どこを変更したらどこに影響が出るかが分かりにくくなっていく。

一口に Model の複雑さと言っても read と write ( 副作用 ) で異なることが多いと考えている。

Read の複雑さ
  • よく似た微妙に異なる Query の組み立てが画面や操作ごとに分かれやすいこと
    • 結果、Query の組み立て時に条件分岐が入りまくる
  • 関連する Model の呼び出しもどんどん増え、他の Model への依存がどんどん増える

これに対して、C でも M でもない場所で目的ごとに明示的に分離された Query の組み立てを行うことで複雑怪奇な named scope を減らすことができる。

class Model < AR
  scope :items, -> {|args1, args2|
    rel = self.where(.....

    if ( args1 )
      rel = rel.merge(AnotherModel.where(...
    end
  }
end

class ItemsWhenConditionSpecified
  def call(param)
    Model.where(....).merge(AnotherModel.where(...
  end
end

こうする。

Write の複雑さ

上にもあるが

  • 関連する他の Model や外部 API の芋づる呼び出しが多くなりやすい
  • 結果 Callback など暗黙的な複雑な操作が埋め込まれやすい

これは Interactor の導入で対応できるはず。

□ 参考

上のリンク先は CQS ではなく CQRS なので「時間やイベントの概念が入ってきて現在の snapshot を取得しにくいので Read Model を導入しましょう」なんだけど、ベースとなっている CQS でも Read 用の Model のようなもの、Thin Data Layer を導入するのはアリだと思う。少なくともこれで Fat Controller も Fat Model も避けることができる。

■ その3 - PubSub

□ 追加gem

krisleech/wisper: A micro library providing Ruby objects with Publish-Subscribe capabilities

□ 効果

依存のうち、同期的に結果が分かる必要のないもの、即時に rollback する必要のないものを非同期メッセージングで分離することで結合度を下げる。要は decoupling の一つ。

□ 適用範囲

Callback

例えば Model の Callback は使いすぎると暗黙の依存が増えて目的の Model のテストが重くなる*3という問題がある。これを PubSub に持っていくことで、テストをメッセージの Publish のテストと Subscriber の実際の動作のテストに分離することができ、Model をテストしやすくなるし、callback を condition で複雑にせずに済む。

その他 C, M が長くなってしまう処理のうち非同期に分けられるもの。

□ デメリット

処理の流れが書いた順番通りにならない。イベントドリブンの考え方に不慣れだと混乱が増す。

Interactor の利用が適しているところに Wisper を持ち出さないように注意したい。結合度は下がるけど複雑さが上がる可能性がある。同期、非同期をきちんと見分けることが大事。result をきっちり活用したいなら Interactor.

ただし、Backend を ActiveJob にすると GlobalID が付与されてトレーサビリティが生まれる。イベントソーシング的に扱える部分を差し込むために Interactor と Wisper を併用するという選択肢もあると思う。

□ 参考
Tags: Rails

*1 この gem については個々の Interactor を書く際に気にしなければいけないのは context だけ

*2 この gem に hook があるだけなので中の処理をどうするかはもちろん自分で考える必要アリ

*3 実行時間や副作用を閉じ込めるためのセットアップなど


2019-04-06 [長年日記]

_ Reactive Programmingって何?と聞かれたらどう答えたらよいだろうか

問題意識

Reactive Programming って何? Functional Reactive Programming って何? Reactive System と謳っている Vue.js を使って書くことと何が違うの?

って聞かれたどう答える?

React とかって Reactive Programming って言うんでしょ? Reactive って何? Functional Reactive Programming と何が違うの? みたいな展開をとりあえずフロントエンド GUI 方面で考える。

バックエンドのことはとりあえず置いておくし、自分自身は関数型言語と呼ばれるもので何かを作った経験があるわけでもない。が、言葉として存在感が増しているので一度整理しておきたい。

Reactive Programming とはこれだ、という結論を出すことではなくどう答えるか、がポイント。

三行で

  • Reactive Programming というものは存在しないが Fuctional Reactive Programming は存在する
  • Observable という言葉は文脈によって結構意味が違うので注意
  • すべてを Event で考える
  • Promise で先送りしろ

四行だった。

FRPに到る道

今のところ jQuery を卒業して ReactiveX ( Functional Reactive Programming 向けライブラリ ) でコードを書こうと思った場合、いくつかの段階があるなぁということを感じている。

だいたい以下のような感じ。

  1. Reactive に考える
    • 代入変更を自分でモリモリと記述しない
    • jQuery は自分で変更する世界*1、React や Vue は自分で変更しない世界*2
  2. 何が起きているのか?と疑問を感じる
    • 素朴には Observer Pattern というものがある
      • 代入や書き換えではなく method call で実現する
    • Spreadsheet の例はいったんここまででよいと思う
    • Proxy Pattern
  3. Event を中心に考える
    • Queue, Pub/Sub
    • Future Pattern / Promise
  4. Future / Promise のおかげで違いを吸収できることに気づく
  5. 違いが吸収できているならすべてタイミングも長さも不定の Array のような何かで表現可能
    • -> Functional Programming

くらいかなぁ?*3

実際には Timer など非同期のものは全部まとめて扱うことができる(Zone.js っぽい)んだけど、従来のイベントを拾って自分で書き換える、という考え方からの移行を考える分には上のような感じ。

Observer Pattern はイマドキの FRP ライブラリや Reactive なフレームワークを使って書く分には気にする必要がない、少なくとも自分で書く必要はないんだけど、イマドキの「書き方」だけ分かっても応用が効かないと思うのと、少なくとも現時点では ReactiveX の説明には Observer Pattern という言葉は出てくるので知っておいて損はない。実際、Observer Pattern + Proxy Pattern というのは Vue の set を追っていくとなんとなく掴める。React の setState から始まる旅は長すぎるが、Vue 2.5 くらいまでの Vue.set はまだだいぶ分かりやすい。Future も Proxy の一種と捉えることができるので、Observer と Proxy と Queue, Pub/Sub 辺りをなんとなくふーんへーと眺めつつ Promise を書いているうちにちょっとずつつかめてくる気がする。

結構必要な知識が多いので、jQuery 使ってアニメーションできますレベルのコーダーがこれからは Reactive だぜ!って盛り上がるとキツイと思う。どっちかというと Container / Presentational Component の分離を意識して Presentation の方に注力した方が成果は出やすいはず。

Container からロジック、FRP をモリモリやっていける人を見つけて組むことができれば面白いものを作れるようになると思う。少なくともなんでもかんでもイベントとして定義して emit できるようになっていれば、それを Container 側で Composite することが可能になる。自分でモリモリ書き換える系の実装を続けているとそれは難しくなってしまう。

言い方を変えると イベントの定義がロジック側とプレゼンテーション側のインターフェイスになる のだ。これが分かっていないとつらいコードから抜けられない。まずはここからだと思う。

cf.

Observableとは何か

これは実装によって意味が大きく異なるので文脈の確認が超大事。

  • 例えば MobX で Observable と言ったら変更を監視したい値のこと
    • 古典的な Observer Pattern で言う Subject に近い、はず
  • ReactiveX で Observable と言ったら Event など Stream を流れてくる「何か」
    • これを pipe で処理してから subscribe する

Reactive Programmingとは何か?

分かんない。

いろいろ調べたけど、Functional Reactive Programming を Reactive Programming と Functional Programming に分解して考えるのは無理な気がしている。つまり、Reactive Programming というものは存在しなくて、すべて FRP の話をしている。譲って「Reactive に考えましょう」までが Reactive Programming で、考え方止まり。

個人的には Reactive Programming と呼ぶのは名前付けに失敗している気がする。Event Stream Programming でよいと思うのだが、Stream というのは名前空間的に非常に厳しい戦いになってしまう*4ので、もう FRP でいいよという気持ち。考え方はともかく、少なくとも書いてる分には Reactive という言葉にはあんまり意味は感じないし、言葉の意味としては Stream も Observable も一意には決まらないので、個々の言葉から意味を掘り下げていくのは無理。分解するのではなく合成したまま Functional Reactive Programming として丸呑みせざるを得ない感じ。

オブジェクト指向は間違いなくオブジェクトと対話してるし、関数型は関数を書いているじゃんすか。なのに Reactive を書いているわけじゃない、ってのがすごくしっくりこないのですよ。とりあえず名前としてね。Functional の部分は多少感じるけど。

結論、Reactive Programming は存在しないが Functional Reactive Programming は存在する。中途半端に省略して Reactive Programming と呼ぶと混乱が増す。

ではFRPとは何か

Promise, async/await が非同期を同期的であるかのように「呼べる」ところまでで留まっているのに対し、その集合を集合として扱えるように Event Stream として概念を整理したのが Functional Reactive Programming なのではないかと今のところ自分は思っている。

そしてそこで果たす Promise の役割は大きいのでやはりしっかり Promise はつかんでおきましょうという気持ちになっている。Promise は起きたイベントをすぐに処理しなくてもよい状態を実現してくれる。おまじないとしてでなく、ちゃんと自由に使えると強い。

※ 蛇足だけど FRP は別に新しいパラダイムではないと思う。オブジェクト指向と関数型のいいとこどりをしようとする考え方の一つでしかなく、イディオムと考えるくらいでよいと思う。

FRPの嬉しみとは何か

個人的には FRP の簡単な例として RxDart のコナミコマンドの例が最高だと思っている。

ReactiveX/rxdart: The Reactive Extensions for Dart

イベントを一つずつしか扱えない状態でこれを実現しようと思うとしっかりと状態を保持して、都度都度、一致しているかどうかをチェックすることになってしまうが、FRP なら「10回の keyup をまとめて取得できるように整える」ところから始まり、10回の keyup の内容をそのままコナミコマンドのシーケンスと比較して一致してるかどうか見ればよいだけ、になってしまう。

RxJS で同じものを作ってみたが、IterableEquality みたいな便利な API を見つけることができなかったので take(10) と combineAll() で処理しようとしたら Promise にしろって怒られてしまった。そこで「あーなるほど Promise か!」って初めて後回しにできるメリット強く感じることができましたとさ。これは面白い!

今後

個人的には、昨今はインフラが非同期分散を前提にしてたりするのでバックエンドについても FRP の考え方を取り入れた方が無理のないコードに落とし込みやすくなるんじゃないかなぁということを考えている。もしかしたら自分としてはバックエンドの方が恩恵は大きいかもしれない。(とは言え同期的な考え方と retry で十分な部分まで reactive にしようなんてことは思わないけど。)

参考

Tags: JavaScript

*1 class名書き換えたりね

*2 変化した値が自動的にセットされる、適用される

*3 Cycle.jsの場合はもう少し違っていて、外部の世界とやりとりしてside effectを生むDriverとpure functionに分離するというターンが入る。reactiveというよりは、よりfunctionalな感じがする。

*4 Stream API というものもあるので。こっちの方がより直感的な「Data の Stream」なので FRP の文脈で Data と言っちゃうのは本当によくないと思う。


2019-04-04 [長年日記]

_ RSpecでわざとCSRF対策に引っかかったフリをする

前提として

protect_from_forgery with: :exception

で例外を上げるようにしているものとする。

このうえで Controller Spec をこのように使って該当する例外を上げるようにするとイケる。

descibe FooController, type: :controller do
  controller {
    before_action {
      raise ApplicationController::InvalidAuthenticityToken
    }
  }

  it {
    post :create, params
  }
end

こんな感じ。controller instance に対して任意の before_action を追加できる RSpec つええ。

ちなみに RSpec 3.5 で試したところ、新しい Controller Spec の代わりにオススメされている Request Spec では上のような書き方で Controller の中身に手を加えることはできなかった。他に方法あるんだろうか。

Tags: Ruby Rails