トップ 最新 追記

2019-04-03 [長年日記]

_ routingのconstraintsを一気に適用する

Rails Routing from the Outside In — Ruby on Rails Guides

こういうこと。

You can also specify constraints in a block form:

namespace :admin do
  constraints subdomain: 'admin' do
    resources :photos
  end
end

これを使えば先日の

Rails 4.2.11.1, 5.0.7.2, 5.1.6.2, 5.2.2.1, and 6.0.0.beta3 have been released! | Riding Rails

であった問題は以下のように一網打尽に解決できる。

constrains lambda {|req| req.format == 'html'} do
  ..
end

Accept ヘッダ経由で format に不適切な文字列が入っていたら routing にマッチしないので 404 が返って終了となる。lambda で書く場合の req.format は Symbol ではなく String であることに注意。(Symbol は Symbol で別に取得方法があるはず。)

何かの事情で Rails のアップグレードがすぐにはできない場合には上の対処方法でもなんとかなる。

Tags: Ruby Rails

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 の中身に手を加えることはできなかった。他に方法あるんだろうか。

参考

RailsのCSRF保護を詳しく調べてみた(翻訳)

Tags: Ruby Rails

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-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 実行時間や副作用を閉じ込めるためのセットアップなど。RSpec側の設定でbefore(:all)でskip_callback = trueするという方法はあるけど。


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-21 [長年日記]

_ WebpackerとWebpackManifest gemはwebpack-assets-manifest npmのentrypoints形式を前提にしている

まとめ

webpack-assets-manifest - npm

これを正しく使え。

頼むからそういう大事なことは大きく何度でも書いといてくれ。

確認したバージョン
  • webpack npm 4.28.4
  • webpack-assets-manifest npm 3.1.1
  • rails gem 5.2.3
  • webpack-manifest gem 0.2.4

で意図通りに動作することが確認できました。

manifestの形式が違う

どういうことかというと、よく見る manifest.json の

{
   "name": "path",
   "name": "path",
   ..
}

name と path を紐づけただけの形式ではなく、entrypoints の中にツリー構造で種類別に書き出してくれるものがあるのだ。

{
  "entrypoints": {
    "name": {
      "js": [
        "path",
        "path"
      ],
      "js.map": [
        ..
      ],
      "css": [
        ..
      ],
      ..
    }
  },
  "name": "path",
  "name": "path",
  ..
}

この "js" の中にはもともとのアプリケーションの entry point だけでなく、splitChunk で書き出されたファイルも含まれているので、WebpackManifest gem で言う javascript_bundles_with_chunks_tag, Webpacker で言う javascript_packs_with_chunks_tag は依存している chunk も自動的に <script> で読み込めるようになるという寸法らしい。

もうちょっと設定が必要

具体的には plugin を利用するところで

new WebpackAssetsManifest({
  entrypoints: true,
  publicPath:  true
})

としておくと entrypoints を有効にしつつ、かつ publicPath の設定を引き継ぐことができるので、splitChunk の成果を自在に利用することができる。

逆に言うと他の似たような manifest 系の npm ではなく、間違いなくリンク先の npm を利用して間違いなく上の設定を追加しておかないとダメ。

完全に過渡期の、すぐに使えなくなるノウハウのにおいがするけど、Webpack さんはメジャーバージョン 4 になってまでなんでこんな感じなんすかね…。いい加減、Web は Webpack だけで完結しないんだから他のツールとのインターフェイスをどうするかが重要という基本的なことに気づいてほしい。書き方いじったりする前に manifest の API 固めてくれ。

あと WebpackManifest gem も Webpacker じゃないんだから Webpack 側の設定に強い前提を黙って入れたらダメだと思う。せっかく脱 Webpacker しても Webpacker の隠された設定みたいなものに依存してたら謎が深まるばかり、せめて前提はしっかり明記してほしいです。

参考


2019-04-25 [長年日記]

_ RSpec 3からPendingだけでなくSkipというものが増えていた

自分の知識が RSpec 2 時代で止まっている部分があることが再確認できた。

Pending and skipped examples - RSpec Core - RSpec - Relish

  • skip と mark された example は実行されない
  • pending と mark された example は実行される
    • failure でも spec 全体では fail しない
    • 逆に pass するともう pending すべきではないと教えるために全体では fail になる

ということらしい。

つまり、「時々落ちる」けど「基本的には動くことが分かっているもの」*1 は pending にしても spec 全体ではやはり「時々落ちる」ことになってしまう。pending ではなく skip にしておくと安定して通る。実行しないので。

一時的に skip にしてあとで直すということをするとよいらしい。

※ 個人的には it を xit にする、describe を xdescribe にする、という prefix 方式は commit に残すのは分かりにくいのでやめた方がよいと思う。手元でだけ一時的に書き換えるのはアリだと思うけど。

Tags: RSpec Ruby

*1 例えば random な要素を入れてしまっているために落ちることが分かっている


2019-04-27 [長年日記]

_ Feature Toggleについて調べてみた

前から何回か興味を持ってるんだけど、ちょっとまとめて調べながら思ったことをザクザク吐き出しておくよ。

今のところ考えるちょうどいい使い方

いろんな使い方ができると思うんだけど、リリースサイクルの都合ではなく

  • どんな機能が現在のプロダクトで切り替え対象となっているのかの可視化 ( admin panel )
  • サーキットブレイカー

の意味で使うのを前提にしつつ、

  • カナリアリリース
  • A/Bテスト

にも導入していく、くらいの感じだとよさそう。

大げさな感じがするかもしれないけど、ちゃんと考えて入れないとややこしくなるだけだなと感じているので。

雑な言い方すると feature toggle って global な状態分けの if ブロックなわけで、実際、特別な記法なんかなくなって環境変数と if だけで実現可能なわけですよ。でもそれって実装側からもプロダクトオーナー側からも実態が見えにくくなるだけでいいことないので、なぜそれが有用なのか?を説明できる状態、機能を実現してなきゃダメだよな、と思うわけです。

基本的には避けられるものなら避けるべき

全体的に feature toggle でリリース管理していくのは危険な感じがする。クライアントサイドやアプリでは、時限リリースなど、マーケットが挟まることで uncontrollable になってしまう部分には使わざるを得ない場合があると思うけど。

普通は 追加するより消す方が怖い ので、消されないで放置されるフラグが増え続けることは容易に想像できる。リリースサイクルのコントロールよりも duration や user segmentation などのメリットを重視してやった方がよさそう。

また、サーキットブレイカーのような、消したらダメな機能もある。そういう違いが admin panel 上で可視化できるとよさそう。

※ 小さいプロダクトの立ち上げ期で次々 feature toggle で出し入れしなきゃいけない状況はあり得るっちゃああり得るけど、それは技術的に解決するものではなく WHY に立ち向かっていない状況のように見える。実装は同時並行で行なって短時間で検証をくり返す、言葉で言うとかっこいいけど、聞いただけで事故る可能性が高い(同時並行のくだりのあたり)し、本当にスモールな時って、そもそも多人数で同時並行開発できないので、feature toggle とか考えようがなくね?という気もする。まぁ、もしかしたら自分の知らない魔法の世界があるのかもしれない。

SaaS

https://launchdarkly.com/pricing/

75 USD/mo

ちょっと高いかなー。でも機能がすごい。これ全部実装するのは確かに大変。user の segmentation も webhook もみんなある。ユーザーベースの拡大がビジネスチャンスの拡大に繋がるプロダクトにはよさそう。

Admin Panelとスピードと履歴が重要

Admin Panel はかなり重要な要素になると思うんだけど、真面目に作るとそこそこ重たいので、複数のサービスをまたいで使えるとよさそう。というかイマドキはまたげないときつそう。サービスをまたいで feature toggle を実現するには CDN でスピードを稼ぎやすい仕組みが重要な気がする。例えばサーバが JP にあったり US にあったりするはず。*1

そして admin panel 上の操作は履歴を残すことが重要。なんなら同時に通知を飛ばすなどがあってもよいかもしれない。(PubSub でいいか?)

feature の追加、削除はどこで?

  • admin panel で追加、削除すると実態(実装)と乖離する可能性がある
  • 各サービス(アプリ)側で追加、削除の order が出せるのが理想的?
    • 追加、削除の履歴はコードの VCS 側に残る形か

調べてみた

http://featureflags.io/ より。先の LaunchDarkly 提供。

気になっているのは、

  • backend storage はどうするのか?
  • admin panel はどうするのか?
  • frontend ではどうしているのか?

frontend で feature toggle はー、いるんだろうか? Node.js でバックエンドならまだしも。JSアプリケーションを初期化する cell をバックエンドで rendering するかしないかで切り替える方が素直な気がするなぁ。

flip gem

参考になる。

https://github.com/pda/flip

Admin Panel までコミで、ちょうどいい感じがする。

feature gem

DSL 部分の実装がよさげ。

https://github.com/mgsnova/feature

  • separeted repository
    • in-memory or redis or static YAML or ActiveRecord
  • cache されるし force refresh! もできる

Repository が分離しているのは非常によい。refresh! もできるので、例えば Central Repository を分離しておいて Central Repository に Admin Panel を設置、Central Repository から必要な情報を取得して Repository に保存しつつ、Central Repository が更新されたら PubSub で Repository を更新する、といったことが可能なはず。

Unleash npm

こりゃすごい。

https://unleash.github.io/

  • Admin Panel ( Server ) と Client SDK を持つ
    • Java / Node.js / Go / Ruby / Python / .NET
  • Heroku-ready
  • storage は PostgreSQL 固定?
  • userId とカナリアリリースっぽいものに対応

これすごいな。 LaunchDarkly の OSS 版ぽい感じ。ただ、都度アクセスが必要になりそうなので、cache がないとサーバの位置によってはちょっとつらそう。

bandiera gem

監視体制が参考になる。

https://github.com/springernature/bandiera

  • サーバサイドは Ruby
  • Client SDK は Ruby / PHP / Node.js / Scala

これ自体が中央のサーバになるので監視ものがいろいろ最初から考慮されている。

chanko

https://github.com/cookpad/chanko

Rails アプリの中で toggle する機能に関するコードの置き場所を安全に分離する DSL. user segmentation 対応。

確かにねぇ、View 以外も分けたいとなった時にコードの置き場所に困るのは事実。

rollout gem

https://github.com/FetLife/rollout

API サーバも Dashboard も作ってあって真面目にやってる感じがする反面、DSL や Redis 固定の部分はイマイチかなぁ。

flagship gem

https://github.com/yuya-takeyama/flagship

  • dependency free
  • flagset を DSL で定義できる
    • この中でもさらに条件分岐できる
    • ちょっと難し過ぎる気がする
  • no storage

admin panel を別立てにできなさそうに見える。

少なくとも DSL での定義を通過する必要があるので、deploy / restart は必要な気がする。環境変数を参照する部分は反映し直しやすいけど、それでも restart は必要? でもそれだと目的を達成できないような気がするので何かを見落としているかも。

feature-toggles gem

https://github.com/LouisSayers/feature-toggles

YAML storage 固定

まぁ YAML dump する script があれば admin panel を別立てにすることもできるけど、YAML storage ってことは deploy し直さないといけないので、そこはイマイチ。feature gem でいいかな。

参考

*1 主に Heroku の利用が視野に入っているため。


2019-04-28 [長年日記]

_ RDBMSと変更履歴のメモ

問題意識

いわゆるマスタ系(リソースエンティティ)の履歴をどこまで考える必要があるのかなーということをぼんやり考えるために一度ざっくり整理してみよう。くらい。

最近は Event Sourcing とか単純な CRUD でないデータを扱うものに関心が強い*1ので、なんかこの辺からやり直しが要るんじゃね、てなことを考えている。

まとめ

  • 単純に履歴が追えればよいだけなら paper_trail gem でよい
  • 過去データにも JOIN したいとなると変更履歴を全部持つ table が必要
    • begin と end を指定できるカラムを追加するのがよくあるパターン
    • 単に update を create として別テーブルに記録するパターンを考えてみたけど「事前」にデータを用意できない
    • いずれにせよ監査目的(誰がどんな変更したか)のログと実際のデータの変更ログが混ざる
  • 監査ログと変更ログを分離して扱うにはバイテンポラルデータモデルですよ

参考

「グラス片手に」は割と評判がよいと思っていたんだけど、業務として気にしなければいけない大切なことは書かれているけど、コラムで触れている内容こそ気になっていた人間からするとやや物足りない感じになっている*2。あと理論系の参考書籍なんかも挙げてもらえると嬉しかったんだけど、その辺は自分で頑張って補完する必要がありそう。

バイテンポラリという言葉がどうにも最初うまく頭に入ってこなかったんだけど、自分で考えてみてもう一度読み直したらよく分かってきた。

paper_trail gem

https://github.com/paper-trail-gem/paper_trail

paper_trail はまるごと versions table に放り込む方式。リソース(マスタ)はデータ量がそこまで大きくないという前提に立つと、うっかりリソースと密になるとそのものが削除になった時に困るので、全部まとめて versions 方式も理に叶ってる気がする。

ただ、これだけだと単純に CRUD の履歴が追える程度になりそう。

CMS的なものの編集履歴が見れる、監査ログが採れる、程度ならこれだけでもよいのかもしれない。

currentの情報を重視して過去に対してJOIN可能なだけのtableを素朴に作ってみる

本当に過去だけを考えてみる。例えば「受注」のように

  • その時点の商品
  • その時点の定価
  • その時点の販売担当者、販売部門
  • その時点の税金

などを記録し、過去のバージョンに対しても JOIN して集計を求めることができるようにすることを考える場合、

  • ほぼ同じ構造の table を二つ用意

する方法がありそう。*3役割は

  • resource current ( read only )
  • resource history ( create only )
    • こっちにはいつから有効になったかの日付情報が入る

データを作成する時の選択肢としては current を参照するが、実際に記録する際には同じ code の最新の id を持つものに JOIN する。

ただしこの方法では

  • 監査のためのログ
  • 「商品」の変更ログ

の両方が混ざる。

というのを考えてみたけど、これは後述のスナップショットデータモデルでもトランザクション時間データモデルでもない別なナニカのようだ。

データモデルを整理した資料より

まさに2種類の時間と4種類の要件という話が述べられている。扱っているデータモデルは以下の4種類。(なんだけど、自分が素朴に考えてみたやつはどれでもなかった。)

  • スナップショットデータモデル
    • いわゆる論理削除とか
  • トランザクション時間データモデル
    • 最新のデータも、任意の時点のデータも取得しやすい
  • 有効時間データモデル
    • トランザクション時間 - 変更履歴
    • 事実として残したいデータだけが残る
    • update が許可されているので監査したい場合は別にログを残す必要がある
    • そこだけ paper_trail にする方法はアリかも
  • バイテンポラルデータモデル
    • システム上の時間とデータとしての時間の両方を記録する
    • 監査対応と事実としてのデータの両方を区別できるようにするため

ただちょっと思ったのは、データモデリング的にはバイテンポラルデータモデルが素直で正しいのかもしれないけど、レコードレベルで権限管理できないと、例えばすでに退社済みの人のデータに誰でもアクセスできてしまう、といったことが起きる。データモデリングではなく業務の設計としてどうなのかという問題が残るような気がする。この場合、スナップショットモデルとトランザクション時間データモデルの中間のような自分の考えたアイディアは有効に機能しそうな気がしなくもない。

activerecorde-bitemporal

kufu/activerecord-bitemporal: BiTemporal Data Model for ActiveRecord

誰かと思ったら SmartHR さんですよ。これは SmartHR さん分かってらっしゃる感じがしますねぇ。

関係ないけど

読む時は Kindle 端末やスマホの Kindle アプリで読んでおいて、こうして書き留める際に PC の Kindle アプリで引っ張ってくる、ってのはなかなかいいんだけど、これは Kindle 本でしか実現できなくて、ドキュメントに対しては同じソリューションがないんだよなー、つらい。

Tags: DBMS

*1 Event Sourcingの話はどこかにメモを起こすかもしれないし起こさないかもしれない

*2 履歴ログが残る構造とか暗号化の話とか

*3 あとで気づいたけど二つに分ける必要はない。view でもなんでもよい。


2019-04-29 [長年日記]

_ DB設計の3段階って必要なんだろうか

なんかぼんやり思ったので自分用にメモ。特に強い結論があるわけではない。

よくある

  • 概念モデル
  • 論理モデル
  • 物理モデル

ってやつ。

物理モデルについては ActiveRecord + Rails-ERD などで作り方を逆転させることも可能。これに慣れると抽象的なモデリングってほんとに必要なのかなという気もするけど、それはたぶんプロダクトの特徴を熟知しているから可能なのであって、何をしようとしているのかさえ不明瞭な状態ではいきなりは無理。

概念モデル、論理モデルはコミュニケーションのためのものと割り切ればよいのかもしれない。

概念モデルはやりたいことに名前を付けて、必要そうなシステムを洗い出すためのもの。

論理モデルはその際に必要になるデータの制約に注目するためのもの。何と何が紐づいているので、とか、どういう変更がどのタイミングで入るのか、とか、絶対に欠落したらダメとか。

いずれもコミュニケーションのためなら母語で書かれてよい。ただ、論理モデルで考えてる制約の話は物理モデルから出てきている部分があって、やはりもやもやは残るんだけど、頭の中である程度の物理モデルが作れる人が論理モデルで会話できるようになる、ってイメージかなぁ。物理モデルの経験がある程度以上ないと論理モデルが挟まるのは単に面倒が増えるだけというか、逆に難しい感じがする。

物理設計はパフォーマンスまで考えるって話があるけど、正規化してる段階でパフォーマンス考えてますよね、とか、なんかもやもやを感じたもので。はい。

いや、はい。だから特に結論はないってば。

Tags: DBMS

_ drawa.ioの使いどころを少し考える

draw.io &#8211; Online Diagramming

特徴

  • 日本語も使える
  • 開発者向きのドローツールとしてある程度利用可能なレベル(単に丸や四角や三角と文字が描けるレベルではない)
  • Google Drive, Dropbox, GitHub (!) など様々なストレージを利用できる
  • なんならローカルにも保存できる

カジュアルに使い始めるのに本当によさそう。

G Drive / Dropbox連携

当たり前だけど他のデータと同様、片付け場所に困る気がする。使い始めるのはよいけど、のちのち困るパターン。特にチームで共有するような場合。

逆に複数のチームに出入りするフリーランスのような立場だととりあえずこういう形ででも共有できると嬉しい気がする。

GitHub連携

branchには保存できないっぽいので、ドキュメント用のrepositoryがあった方がよいような感じがする。

GitHub ベースで議論する際に「設計図は G Drive」へ、と分断があると割とつらいんだけど、画像で export するとこの問題は回避できそう。保存形式は draw.io 独自の XML のようなものでもできるし、画像としての export もできる。ただまぁ、図だけ共有できてもダメなので、開発プロセスすべて GitHub に寄せてくれ、を実現することはたぶんできない。

とは言え、GitHub を中心に活動するエンジニアが自分たちのために図を残すには十分使えると思う。

参考

懸念

draw.io は今のところ confluence / JIRA connect 以外は無料で使えてしまう。サービスの永続性がどれくらい本気なのかよく分からない。とっとと課金させてほしい。

逆に無料ツールとして導入してしまうといずれ有料化した時にどうするのかを判断する必要がある。