金曜日, 12月 05, 2014

IsomorphicとAPIファーストについて #apijp このエントリーを含むはてなブックマーク


Web API Advent Calendar 2014 の5日目の記事です。」

最近、Isomorphicという言葉をよく聞く。
Isomorphicとは、サーバーとクライアントのコードを同時に記述するパラダイムのことだ。
つまり、フロントエンド・バックエンドを曖昧にするようなアーキテクチャである。

私は、IsomorphicはWebのアーキテクチャーに合っていないと思っていて、やはりAPIファーストで設計し、フロントエンド・バックエンドをきちんと分けるべきだと考えている。

この点について詳しく述べたいと思う。

Isomorphicな開発環境が求められる背景


Isomorphicな開発環境が求められる理由としては、こちらのMeteorの記事が大変わかりやすい。(リアルタイムWebアプリケーションフレームワークMeteorについて)
  • 普通のWebでは状態が画面遷移でリセットされてしまうことが辛い
  • バリデーションをフロントエンド・バックエンドの両方で実装しなければならない
  • フロントエンドだけのスキルでは不十分。バックエンドの実装が特に面倒くさい。
  • フロントエンド・バックエンドを別々に勉強するのではなく、同時に作っていくイメージで開発したい
つまりは、

フロントエンドでも簡単に永続化されたオブジェクトにアクセスできるような仕組みのリアルタイムWeb開発フレームワークが欲しいということだと思う。Meteorであれば実現できるらしい。

例えば、meteorはフロントエンドでもバックエンドでもない、"Isomorphic" なフレームワークであり、WebSocket を利用しつつ pub/sub モデルで抽象化したような形で同期を取る。

Isomorphicなフレームワークの課題


2012年に登場して脚光を浴びたMeteorだが、それ以降は大きな広がりは見せていない。
Meteorは果たして失敗したのか。であるならその原因は何だったのか。

個人的に思いつくものを挙げてみる。

1. 信頼性の低いWeb環境を甘くみたこと

いつもいっていることだが、「分散システムでは信頼を失う」という真理に目を背けてはいけない。

リモートでは読込に時間がかかったり、更新に失敗したり、失敗したかのように見えて実は成功していたりということを考慮しないといけないはずなのに、ローカルと同様のシンプルな手続きで実行できるかのようにして本当に大丈夫なのか。

古くはJavaRMI、IIOP、RIAなど、誰もが挑戦して失敗を繰り返してきた歴史がある。これら死屍累々の歴史から学ぶべきことは多いように思うがなぜ生かされないのか。

2. 密結合になりがちなこと

ローカルにあるオブジェクトを更新するかのようにリモートのオブジェクトの同期を取ったり、逆にリモートのオブジェクトの更新があればローカルにも更新を伝達するような仕組みは、リアクティブかつ非同期に実行すればできそうに思える。(もちろん、正しく同期を取るにはデータのバージョニングまで行わないとでできないだろうが)

しかし、たとえ仕組みが出来たとしても、フロントエンドのオブジェクトが勝手に永続化されバックエンドとリアルタイムに同期していくというのは、シンプルであるがゆえに密結合へと誘導してしまう危険性もある。

Isomorphicな環境がフロントエンド・バックエンドの密結合をもたらしてしまった結果、パフォーマンスや拡張性などの様々な問題を生み出してしまうことになる。Meteorももれなく、そういった問題にぶつかったのではないかと想像する。

3.モノリシック(重厚長大)になりがちなこと

また、Isomorphicなアプリでは、モデルの操作含めフロントエンドにコードが集中していきバックエンドではほとんど何もしないということが多い。それはそれで一見良いようで、サーバで実行する方が効率的なものまで無理にクライアント側でやってしまうということが起りがちである。

実際にMeteorでは相当なモジュールをフロントエンドに作りこんでいるが、まともにプロダクションで利用できるレベルを維持していくのは大変だろうと推察する。

4.サーバサイドはどのみち意識して開発せざるを得ないこと

よくよく考えてみるとバックエンドを無視して作ることなんて実は不可能だということに気づく。

フロントエンド・バックエンドで同期するデータソースを実現し、また、バリデーションの定義を共通化したにしても、バックエンドでだけ実行すべき部分というのは必ず残るのだ。

例えば、IDなどのシーケンス番号の採番や、商品金額のマスターチェックなどは、整合性や不正が発生する可能性があるためフロントエンドだけでは実行できない。

サーバを意識しないのが無理なら、フロントエンド・バックエンドを区別無く隠蔽するのはむしろ開発を困難にしてしまうことにならないか。一見シンプルで作りやすそうで、エラー処理などによって複雑になってしまうのでは本末転倒である。

これについては、「node.jsで Isomorphic フレームワーク作ってみようとしたら超辛かった」を読むと困難さがよくわかる。

このように、Isomorphic化は苦労するわりには益が少ない。
開発生産性が劇的に向上するわけではないし、コードが奇麗にかけるとか自己満足の域を出ていないように思える。

Web系の人と組み込み系の人で意見がわかれるところかもしれないが、私はフロントエンド・バックエンドは明確にわけて記述する方がわかりやすいと思う。また、そもそも一人で開発すべきではないとも思っている。一人で作ろうとするから区別したくないと思うわけで、複数人であればそういう要求も出てこないのではなかろうか。

APIファーストとSPAはサーバサイドをスリム化させる


フロントエンド・バックエンドを分離してAPI主体で開発していく「APIファースト」と呼ばれる開発手法がある。

アプリ開発者はAPIのレスポンスだけを知っていればいいため、それぞれが独立して開発に集中でき、かつそれぞれをスケールさせることが容易になる。

最近、Micoservicesが注目されているが、API化はモノリシックなサーバサイドを分割してスリム化する効果が大きいことがわかっている。

弊社が請け負った実際のSPAアプリでの割合はクライアント6に対しサーバ1であった。
サーバサイドのスリム化を図りたい方はまずAPI化してSPA化するのがよいと思う。

API設計でCoC(設定より規約)が受け入れられるか


APIファーストのWebフレームワークでSynthというのがある。

これはフォルダをつくってや特定の方法に則った関数名をつけるだけで簡単にAPIが作れるもので、弊社のReflexWorksの考え方によく似ているのだが、「機能に影響するような新しい命名規則を覚えること」への批判もあって、今後どういう展開になっていくか興味をもって見ているところである。

実は、先日書いた記事でも似たようなことが指摘された。
サービスをそのままURLマッピングするのは筋がよくないという意見である。
URLにはそれが何をするものなのかや振る舞いが予想できるといった点が重要と考えています。
d/{リソースID1}/・・/{リソースIDn}
的な仕様の場合、どのように振る舞うのか想像できません。
静的コンテンツの場合はディレクトリにファイルを配置するのは自然なイメージだと思うが、サービスが絡んでくるとどのように振るまうかがわからないのでイメージするのが辛いということだった。これは一理あると思う。

ReflexWorksではサーバサイドでJavaScriptを実行できる。そして、ディレクトリの構造をそのままURLにマッピングできる。例えば、POST /s/booking をサーバで受けると、/booking.jsを読んでdoPost()関数を実行する。

これは、Restfulな新3層アーキテクチャで示すようなAPI表現 entity = blogic(entity) の考え方に基づいている。やろうと思えばIsomorphicにできると思うが敢えてやらない。

機能に影響するような新しい命名規則を覚えることの批判に対して、直感的な命名規則CoC(設定より規約)というスタンスが果たして受け入れられるのか、また、振る舞いをイメージしずらいという意見にも十分に応えられるのかが課題だと思っている。

負の遺産化を避ける


とはいえ、振る舞いをイメージしやすくするために適当で冗長なURIをつけるのもどうかと思う。

規約のないサービスAPIがいかに酷いものであるかは言わずもがなだ。

昔のSOAP時代によく見られた「お勝手API」はREST全盛の時代の今でも散見される。
REST制約が効いているとはいえ残念なURIになっているサービスはまだまだ多い。
そういうAPIにはお近づきにはなりたくないと思うのは私だけではないだろう。

BaaSがイマイチ流行らないのは残念なURIに付き合うコード書きたくないからだと思う。
技術的負債になるとわかっているから皆近づかないのではないか。
ロックイン、膨らむ学習コスト、不具合があったときのリスク、拡張性の欠如等々の技術的負債を考えてしまうのだ。

とにかくAPIを汎用的にすることで将来の開発に負の資産を残さないことが重要である。
ReflexWorksやSynthが主張するCoCで命名規則という発想は負の遺産化を避けることになるかもしれないと思うのだがどうだろう?

2つの現実解


ここまで、Isomorphicについては密結合・モノリシック肥大化の懸念がある一方で、APIファーストについては命名規則の批判や振る舞い表現に課題があることを示してきたが、結局どのように設計していけばいいのかについて最後に述べてみようと思う。

つまるところ、フロントエンド・バックエンドを同じコードで実行できなくても同様の効果をもたらすようなものを考えていけばよく、とりわけバリデーションの共通化とキャッシュ利用の2点は効果的だと考えている。

まず、バリデーションの一元化についてスキーマによる自動生成の仕組みを紹介したい。

これはReflexWorksでも採用している方法だが、共通のスキーマテンプレートからフロントエンド・バックエンドのバリデーションロジックを自動生成するというやり方である。

スキーマテンプレートについては、詳しくはこの記事を参照してもらいたいが、簡単にいうと項目名や型、正規表現のバリデーションルールなどを1項目につき1行で記述したものだ。

全く同じコードが生成されるわけではないが、正規表現などを使ったバリデーションはクライアントサイドとサーバで同じように動作させることができる。



詳しくは、12/10 のセミナーで説明するので、興味ある方はぜひご参加下さい。

「AngularJSとReflexWorksで始めるSPA開発 ~もうサーバー側で悩まなくて大丈夫~」

参加申し込み => compass

次に、キャッシュを利用した例であるが、firebaseでは以下のような機能がある。
publickeyより

 Firebaseを利用することで、すべてのクライアントとバックエンドのデータがリアルタイムに同期する、いわゆるリアクティブプログラミングが実現するわけです。
しかもあるデバイスがオフライン状態になっても、そのデバイスでのデータ同期が止まるだけでアプリケーションの動作に影響はありません。オンライン状態になった時点でバックエンドとのデータ同期が自動的に行われます。
このようにFirebaseでは、アプリケーション開発者がデータ同期の方法やネットワークのオンライン、オフラインの状態を気にすることなく、Webアプリケーションやモバイルアプリケーションの開発を可能にします。

実はReflexWorksのAndroidAPIも同様の機能を実装している。オフラインであってもリクエストを受け付けるし、裏で勝ってにバックエンドと同期を取ってくれる。

ただし、フロントエンドにあるデータはあくまでキャッシュであり、バックエンドにあるものとは時間差があることには注意を払う必要がある。また、バックエンドと同じ量のデータをフロントエンドに持つことも現実的ではないため基本的にサブセットになる。

要はこれで十分ではないかということがいいたい。

Isomorphicであることは、フロントエンド・バックエンドを意識しないコーディングができるというものであるが、それよりもオフラインでも使えるといったユーザエクスペリエンスの方が重要だと思う。

Isomorphicはほどほどに、ユーザエクスペリエンスを高めつつ、密結合にならないような現実解を見つけていければと思う。

水曜日, 11月 19, 2014

SPAとサーバサイドについて このエントリーを含むはてなブックマーク


SPA(Single Page Application)でサーバサイドをどうすべきかについて述べたいと思う。

MEANじゃダメな理由


今、MEANが注目されている。

MEAN(MongoDB, Express, AngularJS, Node.js)スタックが優れている理由 - Mozilla Open Web Day in Tokyoを終えて

MEAN(MongoDB, Express, AngularJS, Node.js)は、JavaScriptフルスタックのアーキテクチャで、データモデルとしてクライアントからDBまで一貫してJSONを使うという点でシンプルな設計が可能となっている。

業務ロジックにおけるO/Rマッパの割合は40%といわれるが、それが不要となって単純なJSONオブジェクトのやり取りだけで済んでしまうのは大変画期的だといえる。

しかし、MongoDBには、RDBMSでは一般的なトランザクション機能がサポートされておらず、スキーマレスという性質が品質面で悪い影響を与えている。Node.jsには、いわゆるコールバック地獄に陥りがちという問題がある。これらは業務アプリを開発するうえでは致命的な欠点にならなくもない。

ここにMongoDBのアプリ開発についてポイント突いた記事がある。

MongoDBでECサイトを実運用する3つのテクニック
1. トランザクション問題を回避する
2. スキーマをしっかりと設計する

これはMongoDBでもうまく設計すればECだって作れるよという内容のものだが、裏を返せばそれだけ大変な目に合いますよということだ。

ECサイトを構築運用してきた私の経験からいえば、これは、(乂´д`) No way! である。

ReflexWorksによる解決案


MEANは、要は、AngularJSによるSPA(Single Page Application)を中心にしたものであって、サーバサイドについてはJSONを返すREST APIが実装できれば何でもいいはずだ。

クライアント・サーバーの両方で実行できるisomorphic とまではいかないにしても、一貫してJSONやJavaScriptを扱え、かつO/Rマッパが不要であればそれで十分である。

ReflexWorksはMongoDBと同様にドキュメント指向であり、以下のような特長をもつ。
  • XMLやJSONなどの構造化データの読み書きができる。
  • ACIDトランザクションをサポートしており、シンプルなTransaction ScriptパターンでAPIが実行される。
  • AngularJSのMV*アーキテクチャーと相性がよく、ModelとViewに集中して開発ができる。
  • アプリケーションで定義するソフトスキーマを採用、項目の追加が容易である。
詳しくは、12/10 のセミナーで説明するので、興味ある方はぜひご参加下さい。

「AngularJSとReflexWorksで始めるSPA開発 ~もうサーバー側で悩まなくて大丈夫~」

参加申し込み => compass

SPAの優れた開発生産性


SPAでは、JavaScriptでJSONデータを処理し、ページを再読み込みせずに動的にページを更新する。

ユーザーが操作するたびにサーバーにリクエストを投げてHTMLを生成するよりも、クライアントだけで動的に画面を更新するほうが早くて効率的である。また、ユーザーの操作に応じてインタラクティブに動くリッチクライアントを実現できる。

パフォーマンスやSEO対策で問題があるといわれるが、業務アプリにおいては、ビューのパフォーマンスが問題になることは稀であり、SEO対策についても今年の年末にSPAのURLをGoogleがクロールするようになるという。(生まれ変わるAngularJSより)

むしろサーバサイドでのHTMLの事前レンダリングから解放されるメリットの方が大きく、特に開発生産性については相当なアドバンテージがあると考えられる。

Yeoman、Bower、Gruntなどのビルドツールやapi-mockなどを利用すればサーバサイドのことは意識しなくてよい。

毎回サーバにデプロイする必要がなく、ファイルの変更を監視してブラウザのリロードをかけたりできるので、開発者はストレスなく開発できる。

さらに、CircleCIやTravisCIによる自動テスト&デプロイも利用可能である。

サーバサイドでレンダリングはオワコン


たまにAngularJSとサーバサイドの両方にビューの機能を持たせる話を耳にするが、早く諦めてサーバサイドはAPIだけにすることをお勧めする。さらにスマホ向けAPIも共通にできればアーキテクチャーとしては大変スッキリする。



でもこれはLAMPを得意とする開発者にとっては苦痛を伴う環境の変化になるだろう。

ふとデジャブ感におそわれたので調べてみたら、8年前の2006年11月に書いた記事が見つかった。

楽をするために苦労を厭わない人たち - Seasarカンファレンス(秋) -

「しかしWeb2.0の主役になれなかったからといって、今更、LAMPの生産性に追いつこうと思っても、とうてい無理に思える。
(中略)Webアプリケーションをサクサク作るっていわれても、本当にそのようにできるのか。私はJavaで作るWebアプリという発想そのものが疑問符「?」である。LAMPの台頭で、それがもう否めない時期にきているのではないか。」

このときJava全盛でEoDとかいってたのを覚えているが、結局はRoRをはじめとするLAMPの生産性にはかなわなかった。しかし、8年経った今はこう言い換えることができる。

「LAMPがSPAの生産性に追いつこうと思っても到底無理に思える。私はLAMPで作るWebアプリという発想そのものが疑問符「?」である。SPAの台頭で、それがもう否めない時期にきているのではないか。」

8年前のLAMPが今のSPAといえなくもない。

最後に


ここまで一気に書いてから rebuild67a を聞いた。
どうやら、AngularJSが嫌いという人が増えているとのこと。
困ったな。


水曜日, 8月 27, 2014

RESTとJSON、スキーマ定義について思うところ このエントリーを含むはてなブックマーク


 mozaic.fm #7 REST#mozaicfm REST を聴いての感想、それから「Web+DB vol82のWebAPIデザインの鉄則」に触発されたので書こうと思う。

REST設計について


WebAPIを設計するうえでRESTが重要であることは周知のとおりである。

“Constraints are liberating”「制約は自由をもたらす」

@t_wadaさんがおっしゃっているように、RESTを前提にすれば、「アーキテクチャとしてもそうだし、アプリケーションフレームワークも「適切な制約」を設けることで設計のコストが下がる」という大きなメリットが生まれる。

しかし、相変わらずリソース設計やらインターフェース設計やらで悩んでおられる方も多いと聞く。 その一方で個人的には適切なフレームワークを使えばREST設計で悩まなくてもよいはず(※3)という思いもある。

インターフェース設計などの基本設計をフレームワーク側で用意してあげれば、普通に設計していくだけで自然とRESTfulになっていくような開発が可能になる。これはReflexWorksが目指しているところでもある。

そういう意味で、ちゃんとしたフレームワークさえあれば、今は開発者がRESTの基本をしっかり学んですべて設計する時代じゃないかもしれないと個人的には思っている。

URIの制約をどうするか


ではどのような制約を設ければ自然とRESTfulに設計できるのか。
まず私が一番大事だと思っているのはURIの制約である。

URIはリソースを指し示す重要な表現であるが、これは単にリソースの場所を示すものであって、表現形式や検索条件など他の情報を含めるべきではないと考えている。(ただし、クエリパラメータは別)

例えば、以下のURIはよくあるものだが、このなかにはバージョン番号や「API」で「datastore」であるということ、また階層によってコレクション、またはエントリであることを説明する情報がURIに含まれてしまっている。

/v1/api/datastore/{コレクションID}/{オブジェクトID}

私はこれをURIの「お仕着せ」と呼んでいる。

一方、単にリソースの場所を示すURIは以下のようになる。

/d/{リソースID1}/・・/{リソースIDn}

これにはバージョンやAPIなどを示す情報はなくコレクションやエントリの区別もない。
階層が/によって多段に表現されているが単にリソースがあることを示すだけである。
例えば、{リソースID1}/{リソースID2} は、リソースID1の配下にID2があることを示しているが、ちょうどファイルシステムのフォルダをイメージするとわかりやすいかもしれない。

/d/app/index.html とすれば、/appフォルダの下にindex.htmlが入っているというように直感的にわかる。これを、リソースにおいても/d/invoice/master/・・ などと階層で表現したいと考えている。

ちなみに、/v1/api/datastoreも階層表現されているように見えるがこの階層には意味はない。その証拠に、/v1_api_datastoreとしても同じことである。またURIの設計上だけで実際にエントリがあるわけでもない。
これはURLが単に固定されているだけの話であって制約というより不自由なだけである。

繰り返しになるが、URIにリソースのアドレス以外の特別の意味を持たせてはいけない。
そういう固定的でお仕着せ的な設計がAPIの齟齬を生む。そして最終的には管理できなくなりバージョニングが必要という話になってしまう。

URIが単にリソースを示すだけであれば、APIの仕様を見ながらコーディングする必要もなく、バージョニングも必要ない。(どうしても必要なら?v1とかクエリーパラメータで区別すべきというのが私の意見。<関連> You Need Not API verson2

ただ、先日ある開発者に、ReflexWorksにはGoogle Drive APIのような一覧ってないのですか?って聞かれたときには、世の中はそういうものなのかなあとは思った。(その開発者にとってみれば制約よりも仕様の方がわかりやすいということだった。(※1)

オブジェクトの中の項目をURIに含めるべきか


JSONオブジェクトの構造中の特定の位置を指し示すJSON Pointerというのがある。
例えば、以下のオブジェクトが/d/masterに入っていたとする。


{
  "customer" :[
    { "name":"foo" ,"id": "1"},
    { "name":"bar" ,"id": "2"},
    ・・・
  ],・・・
}

JSONPointerでアドレスを/customer/0/idとすれば、customer配列の0番目のidという意味になる。これを、URI(/d/master)にアドレスを直結して、/d/master/customer/0/id と表現するのはあまりうまくない。

なぜなら、先ほどのリソースの定義では、customerの下に0というリソースがあり、その下にidというリソースがあることになってしまうからだ。

HTMLの場合は同一文書内のフラグメントには#を使うが、リソースも同様であり、あくまでmasterの中なので、私なら/d/master#customer[0].id と表現する。この方がJSONっぽくて自然だと思う。

クエリパラメータによる表現の区別


では、リソースの階層表現においてコレクションやエントリをどのように区別すればいいだろうか。

私はクエリパラメータを使って区別すればいいと考えている。
実際にReflexWorksでは、?f(feed)でコレクション、?e(entry)でエントリを意味する。

また、リソースというのはデータの集合であり、その表現はJSONやXMLやその他どの様なフォーマットであっても同様に扱えるのが理想である。ReflexWorksでは、デフォルトはJSONだが、?xでxml、?mでMessagePackのフォーマットで表現できる。

もっといえば、HTMLコンテンツであっても画像であっても同じリソースと考えることができるわけで、GET /d/app/index.html でも、 GET /d/masterでも区別する必要はない。
フレームワーク実装の難易度は上がるかもしれないが、こうすることで全てのリソースに対してRESTで統一できる。


クエリパラメータを使っての条件絞り込みは直感的でよい実装だと思う。ReflexWorksでも、?の後ろに絞り込み条件を複数指定することができる。そのときのパラメータは以下のようにエントリの中の項目(subInfo.favorite.foodやpriceなど)となる。-eq-(=)や-lt-(<)は条件式である。
例) 
http://xxx.xxx/{URI}?f&subInfo.favorite.food-eq-egg&price-lt-5000&l=20
リソースを直列分割して扱う「?fields」というパラメータをよく見かけるが、個人的には好きにはなれない。これは、指定されたフィールドのみを対象としたリソースを得るために使われるものだが、リソースの最小単位=エントリの括りがなくなってしまい曖昧になってしまう。

上記の絞り込みの条件で得る結果はフィード(コレクション)もしくは、エントリであるべきだ。

リンクがない問題と語彙の導入


XML全盛の頃はATOM FeedのセマンティクスとRESTによってルールはうまく整理されていたように思う。(結局流行らなかったが)


ATOMの語彙は以下のようなシンプルなものであった。

title
id
link
 title
 rel
 href
content
・・

しかし、JSONによってXMLが駆逐されてしまった。それに伴い、SOAPもWSDLもATOMも無くなった。JSONがもたらした不毛の荒野にはXMLやATOMが培ってきた財産(スキーマやリンク)は跡形無く見当たらない。

リンクのないデータは扱いにくい。PrimaryKeyのないデータと同じようなものだ。
JSONにはメディアタイプとLINKヘッダという2つの解決策があるらしいが、エントリに入らないと扱いにくいし、特にコレクションをどう表現するかが課題だと思う。


JSON Schemaでは以下のようにidやlinksという項目が導入されている。

title
id
type
links
 title
 rel
 href
 mehod
 mediaType
 targetSchema

リンクについていえば、AtomよりJSON Schemaの方がむしろ語彙数が多くなっているのは皮肉である。Atomに比べてJSON Schemaはむしろ冗長になっている。
リソースは自由に変換されるべきということは述べたが、私はリソース定義にはmethodやmediaType、targetSchemaという項目は必要ないと思う。

結局、ATOMの語彙でよかったのではないか。ATOMのJSON表現で。

JSONでスキーマを書くべきか


JSONもちゃんと設計したいということで構造を含むスキーマ設計がやりたくなるのはわかる。それに、スキーマレスだから
設計しないでもOK(ヒャッハー)というわけにはいかない。特に業務システムであれば。

きちんと設計したうえで必要なときにいつでもスキーマを変更できるような柔軟なソフトスキーマが望まれていると思う。

ただAtomやXMLを削ってJSONを拡充するという発想は何か間違っている気がする。


今はJSON全盛とはいえ、JSON SchemaはWSDLと同じ匂いがする。
ATOMに比べても語彙数が増えている。JSONに語彙を追加するのはHTML5で語彙を追加するのとはわけが違うと思うのだが、それでもなお、似たようなものを導入するつもりなのだろうか。

たしかに、JSONはXMLよりシンプルで、基本的な値などをやりとりするだけであれば、XMLよりもJSONの方がずっと簡単だし、JavaScriptを使っているのであればJSON形式は自然な形式である。

しかしスキーマは別の話だ。何でスキーマをJSONで定義するの?という素朴な疑問は湧いてくる。


構造を定義するのにお世辞にも読みやすいとはいえないJSONを使う意味は本当にあるのだろうか。

XMLはHTMLに似て宣言的なのだしマークアップに適している。スキーマ定義も宣言的なのだからXML使う方がまだましではないか。

リソース定義のためのシンプルなスキーマ言語の必要性


そして、そもそも考える順序が逆ではないかということに気づく。

先に述べたように、JSONはリソースの一つの表現にすぎないわけだから、リソースはJSON前提で考える必要はなく、逆にJSONに縛られて考えてもいけない。

JSONを拡充する方向で上がっていってもだめで、つまりは、「JSONやXMLなどの表現にとらわれない汎用的な」リソース構造を定義するスキーマ言語があればよいという考えに至る。(※2)

例えば、以下のように、項目名や型、バリデーションルールなどを1項目につき1行で記述できれば便利である。ルールは、()に型、省略でString、{}に値の範囲、=の右辺に正規表現、!で必須項目などとする。



master
 customer
  name=^.{0,50}$
  id(int){0~999999}!
  ・・・


きっとEXCELの定義書でも1行1項目で管理しているだろうから、そこから書き起こしたテンプレートに簡単にルールを記述していける。

JSONは可読性においてXMLに及ばず、利便性においてEXCELに及ばない。
JSON Schemaで管理するのはいいが結局はEXCELを捨てきれないだろう。
プロジェクトの共有文書が相変わらずEXCELのままであれば、JSONSchemaで管理すると言った人が2重メンテするハメになるのは目に見えている。

ReflexWorksでも上記のようなシンプルなスキーマ定義を採用している。そして、スキーマからエンティティオブジェクトを自動生成している。


テンプレートに項目名を記述することで自由にスキーマを定義でき、運用中に項目の追加変更が可能である。ソフトスキーマであり更新すると直ぐにシステムに反映する。

また、型の指定、親子関係や繰り返しといった構造の定義、必須チェックや最大/最小チェック、バリデーション、Index、暗号化などを指定できる。

(詳しくは、仕様の「テンプレートによるスキーマ定義」を参照してください)


(追記)

(※1) このように感じる方は意外と周りに多い。特にサービス(ビジネスロジック)を含めて考えると可読性や振る舞いの予想という意味でURI固定の方がわかりやすいという。ここではあくまでサービスを含めない静的なリソースのことを主に想定している。(ReflexWorksの/dは静的なdataの意味で、サービスは/pのプロバイダというように使い分けている。)

 (※2) JSONとXMLは等価ではないし互いに可換でもないので、1つのスキーマで両対応は無理。サブセットしかサポートできないというのはおっしゃるとおり。https://twitter.com/makotokuwata/status/504954681085263872 ただ、構造や型、バリデーションなどを汎用的に定義することは可能だし、ReflexWorksで実際にやっていることでもある。


(※3) 悩まなくなったらものづくりの仕事つまんなくない?との意見をいただいた。

日曜日, 3月 16, 2014

You need NOT API version 2 このエントリーを含むはてなブックマーク


バージョン番号をURLに含めるべき「でない」理由について、 自分はどのように考えているか具体的に述べたいと思う。

見過ごせない根本的な問題



基本的には、 http://rebuild.fm/35/ で述べられていること、それから、

http://blog.kazuhooku.com/2014/03/web-api.html http://yohei.hatenablog.com/entry/2014/03/12/001707 

 に同意である。  そこで述べられているように、

 ・拡張が容易である
 ・拡張時に後方互換性を破壊しない 

をしっかり守り、互換性を壊すことがないようにできるだけ変更を少なくしたうえで、 互換性を壊す必要がある場合は関数名を変更する、といったような感じで概ね解決できると思う。 

ただ、何というか、根本的なところで琴線に触れるものがあって、見過ごせないなあと感じている。 率直にいうと、そもそもAPIのv2を作りたくなるような状況に陥ることこそが問題ではないかと思っている。 

JSONのような拡張可能なシリアライズフォーマットを用い、拡張可能な形で入出力パラメータを設計する。これは、いわゆるスキーマレスとかソフトスキーマといわれるもので、項目が変わったとしてもシステムに影響ないような仕組みである。つまり、項目が変更されても影響なければバージョン管理はそもそも必要ないというわけ。

これはJSONだけでなくXMLやMessagepackでも同じことがいえる。 

メディアタイプでデータ表現を変えるという発想はReflexWorksに近いというか、そのものなんだけれども、RESTのリソースの概念では表現を変えてもデータは同じものであって、v2なんていう発想はない。 (もちろん、データが更新されたらリビジョンは上がるがAPIのバージョンに相当するものはない)

それから、私はJSONの文化大革命って呼んでいるのだが、過去にHTMLやXMLで積み上げたセマンティクスの議論が台無しになっている雰囲気があって、今回のようなAPIでバージョニングすべきかが問題になるのは当然の帰結のような気がしてしかたがない。 

今さら、ATOM PUBとかいうつもりはないけれど、それにしても、勝手気ままにAPI作りがちではないか? 

最悪なのは、getXXX,updateXXX というように単にエンティティに操作をつけただけの関数をモッサリ作ってる。 だから、/v2/getXXX なんて欲しくなるわけ。データこそJSONかもしれないが、これこそSOAP時代の発想じゃないか。 

HTTPのGETやPOSTほど変更に強いAPIはないわけだし、RESTで単純にHTTPメソッドへマッピングして、GET|POST /XXX でいいじゃん、と思う次第。

CRUDに当てはまらないAPI


一方で、単純にCRUDに当てはまらないものはどうするんだ?RESTでCRUD4つに集約できるなんて幻想だ!なんていうのもわかる。 

基本的にリソースは静的なデータであって、GET /XXXとすると、/XXXのデータがそのまま返る。 しかし、/XXXの結果を加工したくて、サービスとしてサーバサイドで動的にビジネスロジックを動作させたいこともある。 

例えば、消費税計算関数calcがあり、デフォルトは5%であるとしよう。以下のように書くことで、calc(/XXX)を実行する。つまり、/XXXで取得した原価のフィードを関数calc()で消費税5%で計算して返す。 

 GET /XXX?f=calc   

さらに、8%で計算するv2も定義してURLパラメータで表現する。4月以降はv2をつけて実行すればよい。 

 GET /XXX?f=calc+v2 あるいは、GET /XXX?f=calc&v=2 など。 

つまり、URLパラメータでバージョニングすればよいという結論である。

結論からいえること


いつのまにかURLにバージョン番号をつけるべきという結論になってしまっているが、言いたかったのはリソースはあくまで静的なものであって、リソースをCRUDする限りにおいてバージョニングは必要ないということ。

また、リソースの項目の追加変更においても必要ない。唯一、必要になるのはCRUDに当てはまらない一部の動的なサービスAPIであって、それでもURLパラメータで管理すればよく、API全体をバージョニングする必要はないということ。

だから、/v2/~というように、URLにバージョン番号をつけるべきではないと思う。

日曜日, 2月 02, 2014

PC遠隔操作事件、状況証拠だけで有罪にすべきでない理由 このエントリーを含むはてなブックマーク


PC遠隔操作事件で初公判を2月に控えた今、ネットでは色々酷いと叩かれている。

逮捕当時は誤報だらけで最近沈黙しているマスコミ、4人の誤認逮捕をしでかした警察、予断と偏見を持たずに証拠を見れば有罪と言っている検察、そして、勾留して一年が経とうとしているのに未だに保釈どころか家族の接見すら許可しない裁判所。

この事件はK氏が真犯人であることを示す決定的な証拠が一つでもあれば有罪になるのだろう。しかし、決定的証拠はないといわれ、状況証拠のみで争われるという。そうなると冤罪が怖い。

状況証拠だけでは冤罪が生まれる


ITを悪用した遠隔操作事件では誰が“犯人”に仕立て上げられたとしても不思議はない。
肝心の「パソコン遠隔操作事件」の検証がどうなっているのかが、僕らの一番の関心事です。しかも警察や検察は、これまでに何件もの誤認逮捕事件を引き起こしているわけです。言い換えれば、誰が“犯人”に仕立て上げられたとしても不思議はなかった事件だと言うこともできるでしょう。検察は事件の真相を解明し、同様の事件の再発を防ぐことができるんでしょうか?  yahoo newsより
これがもし冤罪だったら恐ろしいことだ。
とにかく状況証拠だけでは有罪にならないようにしてもらいたい。


犯人を示す2つの証拠


しかし、そういう私はK氏が真犯人だと確信している。

それは、江ノ島のグレーという猫に触れたこと、またスマホ携帯の検索履歴という状況証拠があるから。PCの検索履歴、雲取山に行ったこと、悪の教典の本、まどか人形などの639点の状況証拠は弁護人のいうように証拠ゼロに等しいが、この2点、とりわけスマホ検索履歴は犯人性の推認力が高い有力な状況証拠ではないだろうか。

猫にK氏が触れた時間を含むわずか20数分の間に首輪がつけられていた。間違いなくこの時間に真犯人はいたわけで、K氏が犯人ではないとすると他の誰かということになる。しかし、この短時間に触れた人が大勢いたとは考えにくい。

また、K氏はスマホで「猫 首輪」など事件に関係するキーワードを1月2日に検索している。1月4日以降であればニュースを見たからと言い訳ができるがその日は犯人以外は誰も知らないはずである。またK氏は、神保 哲生でも検索している。K氏はそのような人物は知らないと供述していた。

PCと違いスマホでは検索履歴を残すような遠隔操作はできない。お正月ということもあり会社の同僚がK氏の目を盗んでやったということも考えにくい。

それでも無罪とすべき理由


このように、K氏は非常に怪しいのである。が、どんなに怪しくても私は真実より事実、つまり決定的な証拠がないということを重く見るべきだと思う。

もし状況証拠だけで有罪になるという判例を作ってしまったらネットは冤罪だらけになってしまうだろう。(ネットというものは本来そういうものである。)

ではK氏が本当に犯人だったらどうするのか。私はそれでも無罪とすべきと思う。求刑10年はいくらなんでも重すぎるし、もう彼は自分が犯した罪以上の罰を受けているではないか。

それより、実際に普通の市民が犯罪者と疑われ4人が誤認逮捕させられたことを重く見る必要があるだろう。

冤罪は人ごとではない。明日は我が身と考えるべきだ。

日曜日, 1月 19, 2014

次世代Webアーキテクチャーの話 (CROSS2014を聞いて) このエントリーを含むはてなブックマーク


 CROSS2014の次世代Webセッションを見て来た。
セッションの前半で議論されていたプロトコル編はしっかりとした方向性が示されていたが、後半のアーキテクチャー編は現状の混沌とした話が多くて、方向性というか新しいビジョンを示すまではいけなかった印象だった。

それは、サーバのアーキテクチャーが成熟していることも理由の一つなのかもしれない。
しかし、アーキテクチャーこそ方向性を示すのが重要だろうと思うので、個人的に考えていることをまとめることにした。 

Webスケールを実現する技術とリアルタイムWebの方向性


  リアルタイムWebというわけではないが、密結合なプロトコルはことごとくインターネットで玉砕されてきた歴史がある。 

古くはCORBA IIOP、DCOMの失敗。それからSOAPの失敗。 
(ちなみに、IIOPのIはInternetで、当初は大規模なインターネットスケールで分散させようとしたことがうかがえる) 

これらは何でうまくいかなかったのか。 

それは、簡単に言ってしまえば、単一のマシンから複数のマシン、そして、インターネットへと拡張していく際に失うもの、つまり整合性や信頼性の問題を考慮してなかったからだと思う。(詳しくは補足を参照)

 IIOPにしてもSOAPにしてもライブラリのAPIのような形で提供して、それをインターネットを跨いで同期的に実行することを想定していた。
 
これらは整合性や信頼性の問題を解決することなしに、単一マシンのアーキテクチャーをそのままインターネットの信頼できないマルチ・マシン上に拡張してしまったのだ。 
(かつてIBMでCORBAのプロジェクトに多少からんでいた私は、インターネット上では通用しないことを嫌というほど味わった。早く気づくことができていたら(特にIBMは)莫大な投資をせずに済んだのに。) 

ところで、リアルタイムWebの方向性はどうなんだろうか。

将来においても、既存のWebアーキテクチャの延長線上にあるものなのか。それとも、別の違う方向を進むのか。 また、何かリアルタイムWeb技術が急速に進んで、既存のWebアーキテクチャーがそれに置き換わるというような話になるのか。 

クライアントとサーバが密に結合した形になってしまうと前述したようなトラウマが蘇ってくる。 

プロトコルの高速化によりある程度の適用範囲は広がるとは思うが、それでも局所的にしか解決できないだろう。 逆の言い方をするとリアルタイムWebを追求していくとNWやプロトコルの高速化でしか対応する術はないように思える。 

一方、既存のWebアーキテクチャというのは、http(s)とかプロトコルの話だけでなく、RESTなどを含むアーキテクチャスタイルの話が中心になる。

これは、permalink(URI)で示すリソースを操作する概念であり、基本的にステートレスである。 あらためて説明するまでもないが、RESTの「リソース」概念は次のようなものである。

・インターネットのすべてのものがリソースである。
・すべてのリソースは、URIによってアドレス可能である。
・リソースに対する操作は、HTTPの基本的なオペレーション、Get, Put, Post, Delete によって定義される

普通のWebサーバは基本的にはスタティックなホームページの配信を行い、動的なWebにしてもサーバで都度生成したものを返しているだけだ。

Webスケールといった言葉があるように、これはスケーラビリティに優れており、また、ネットワーク耐性にも優れている。

RESTは要はそのWebのアーキテクチャースタイルを壊さないようにWebサービスに応用したものである。 

また、キャッシュも重要な技術要素の一つである。これは、「誰か他の人のリソースを利用し、あるいは、他の誰かが、我々のリソースを利用することを認める」ような世界である。

Webスケールを実現できているのは、ブラウザキャッシュ、PROXY、あるいはCDNといったキャッシュ技術があるからといっても過言ではない。

他方で、ワンセグTVがフルセグTVより数秒のディレイがあっても気にする人はいないように、そもそも本当にリアルタイムである必要があるかという話がある。 

別のいい方をすれば、非同期的な更新で、eventuallyであっても十分ではないかということ。 例えば、一般的なWebアプリのユースケースを考えてみると、そこまでオリジナルにこだわる必要はなく、コピーの若干古い情報だとしても実用上問題はないケースも多い。むしろ、完璧に同じタイミングじゃないとまずいケースの方が少ないのではないか? 

これは妥協しろと言っているのに等しく、低レイテンシーを求めるリアルタイムWebとは相容れない考え方かもしれない。 

なので、個人的には、既存WebとリアルタイムWebは別の方向性をとり、リアルタイムWebが既存Webに置き換えられていくこともないだろうと考えている。

全体的な方向性としては、リアルタイムWebはそれはそれで盛り上がりつつも、既存Webのアーキテクチャー上をベースとして今後も進んでいくのではないだろうか。 

データのキャッシュ化を実現するためには


 一つの解として、HTMLなどがWebサーバやブラウザでキャッシュされるのと同様に、データにおいてもキャッシュさせるという考え方がある。 

そもそもデータは動的であり揮発性の高いものであるが、生成する際にバージョニングして静的に扱えるようにすることで、HTMLなどの静的コンテンツと同様、キャッシュを効かせることができればスケーラビリティは高くなる。 

バージョニングとは、例えば、データはレコード単位にURI+リビジョン番号を振ることでユニークであることを保証し、それが一致している限り同じデータとみなすような仕組みである。更新されるとURIは変化しないがリビジョン番号はカウントアップされる。 

正のデータはサーバ上にあって常に誰かによって更新されている。他のシステムはそのコピーを持ち、正のデータが更新されるとEventuallyに更新されることを前提に考える。ここでは遅延を許容することが肝要である。 

また、同じデータが複数のクライアントやサーバに散在している可能性も考慮し、更新する際は何回更新されても同じリビジョンであれば同じ結果になるようにする(べき等性を考慮する) 

ただし、データの整合性についてはシビアに考えなければいけない。これには、更新の際に元データのリビジョン番号が異なれば他者による更新があったということでエラーとするような楽観的排他の仕組み(Snapshot Isolation)で対応できる。

これにより、整合性の問題をWebのアーキテクチャースタイルを壊さないで解決できるようになる。 

去年だったか、permalinkの重要性は薄れて短縮リンクで十分という議論もあったが、整合性まで考えるとやはりpermalinkは重要であるように思う。短縮リンクはpermalinkのaliasという位置づけであろう。

サーバ負荷軽減のためには通信しないこと

 
 注意しなければならないのは、既に同じリビジョンのデータを持っているにもかかわらず、何度もサーバにリクエストを投げてしまうようなことである。これではキャッシュしている意味がない。 

CROSSセッションの議論でも、シングルページアプリケーションのデメリットとして、サーバへの通信回数が増えて負荷が大きくなるという指摘がなされていた。

「long pollなどNativeと同じで不必要な通信が多く発生するリスクがありしばしば作り方が問題になる。 初期化のタイミングがなくなる反面、サーバへの通信回数が増える。 最悪、ステータスコードを見ないでリトライするようなクライアントコードが書かれるリスクも考慮しないといけない」

シングルページだとパフォーマンス管理が難しくなったという意見であるが、そもそもサーバサイドのレンダリングを引きずられていると高速化は難しく、たとえデータバインディングに特化できたとしても、効率よくしないと今度はそれがパフォーマンスに悪影響を与えてしまう。

ステートレス性というか、キャッシュを利用してなるべく通信を発生させないようにする。また、発生しても必要最低限のデータに絞ることが大事である。

究極の形態がオフライン耐性をもったアプリであり、Offline Firstのような概念や方向性をもって作っていけば、おのずとNW効率のよいものが出来上がるのではないかと思う。 

クライアント・サーバ間のデータバインディング

 
 クライアント・サーバ間のデータバインディングは、具体的にはAngularJSに代表されるようなMVVMに加えてサーバとのデータバインド機能を追加するようなイメージとなる(図)


これまでの、クライアントからのリクエストによってサーバ上で動的にコンテンツが作られるスタイルから、基本的にクライアントとサーバが同じデータを参照しており、どちらかが更新したタイミングでお互いに通知されて同期されるような、クライアントとサーバとの双方向バインディングのスタイルに変化していくと思う。(リアクティブプログラミングという)

これからの時代、このようなクライアント・サーバ間のリアクティブプログラミングなどが求められると思うのだが、一歩間違えれば密結合の世界になって、スケールしなくなるのも懸念される。 

同期的な密結合ではスケールしないことは述べてきた通りであり、それは、XML/SOAPでもREST/JSONでも同じことだといえる。
 
実際、WebサービスのAPIは今ではREST/JSONが主流であるが密結合になってしまって残念なものも多く見られる。 

クライアント・サーバ間のデータバインディングでは、図にあるように、XHRでリクエストを受けつけるが、レスポンスについてはWebSocketで非同期更新する。

バージョニングされたデータが非同期更新されるのがポイントであり、WebSocketであっても同期的な密結合に陥ることはないのが特長である。 

あと、バリデーションの問題(サーバとクライアントでどうバリデーションするか)といった新たな課題もあるが、長くなるのでここでは触れない。

通信のデータ形式、それから、BaaS

 
 クライアント・サーバ間のデータバインディングでは、通信のデータ形式はXMLでもJSONでもMessagePackでも何でもいい。 むしろ同じように扱えるべきだと考える。 
データとは本質的には普遍的なものだから。

よく見かける、XML vs JSONとか、SOAP vs RESTなどの議論はたいへん不毛である。
データ表現を変えたところでアーキテクチャーが密結合であれば意味がない。

ちなみに、弊社のReflexWorksは様々なデータ表現に対応している。
 (先日、既存のReflexWorksシステムにおいて、XMLからMessagepack+Deflateに変更したところ処理速度が4倍(1034msから250ms)、データ量が2%(892,799bytesから21,255bytes)になった。なんと98%削減できたことになる。業務ロジックに変更はない。) 

現在、AngularJSのクライアントとReflexWorksサーバ間との非同期データバインディングが可能なBaaS、vte.cx engine(ぶいてっくす えんじん)を開発中である。

ソフトスキーマと項目単位に付与できるACLやグループ機能などのSNS機能もある。 

乞うご期待。 

(補足:http://stream.itmedia.co.jp/enterprise/pdf/pdf_16_1235461490.pdf より)
 

・整合性の問題
マルチ・プロセスからマルチ・マシンへ拡張するとき、我々は、状態を失う。「システム」のグローバルな状態というのは、虚構である。興味深い分散システムには、整合的な状態というものは存在しない。(Lamport:http://research. microsoft.com/users/lamport/pubs/pubs.html)分散OSのプロジェクトは、グローバルな状態を導入しようとしたが、大々的に失敗した。
しかし、障害の局所化(何かまずいことが起きても、システムの部分は生きのこる)を手に入れた。

・信頼性の問題
(インターネットの)信頼できないマルチ・マシンたちに拡張するとき、誰を信ずることが出来るか分からない難しい状況の中で、我々は信頼を失う。しかし、我々はスケール(webスケール、インターネットスケール)」を手に入れた。
 


火曜日, 1月 14, 2014

Web API認証について このエントリーを含むはてなブックマーク

 最近、Web APIの認証をどうすべきか考えている。

 例えば次のようなケースをどうするか。
 「既存のWebサイトがあり、既にユーザIDとパスワードによる認証によって、ブラウザでデータを提供している。 今回、この提供データをブラウザの画面ではなく、REST APIにて取得可能にしたい。 このデータはユーザ毎に取得可能な値が違うので、認証、または認可によって制限をかけたい。」

 ユーザーがブラウザからIDとパスワード(以下ID/PW)を使ってログインする方式を、そのままWeb APIにも適用しても安全なのだろうか。 Web APIの先にはスマホアプリやシェルスクリプトなどから直接ログインするものなどが考えられるが、安全かつシンプルに実装するにはどうしたらいいのだろうか。 

私はセキュリティの専門家ではないので間違った考え方をしている可能性もあるが、誰かの目に留まって助言いただけるかもしれないので、それを期待して今考えていることを率直に述べようと思う。

APIで安全な認証はそもそも作れるか

 
 そもそも認証については原則ブラウザを使わないとダメなんじゃないかと私は考えている。
 その理由は、ブラウザじゃないとサイトの安全性について確認することが困難だから。
ブラウザであれば、利用者はアドレスバーでドメイン名を確認できるし、https通信されていることやベリサインシールも確認できる。その逆にアドレスバーを隠してあるサイトは信用できないしパスワードを盗む偽サイトかもしれない。
 世の中を見渡すと、ほぼ全ての認証の基本になっているのがブラウザからのログインケースであるように思う。

 一方、API認証で、たとえID/PWで認証できるようなAPIを作ったとしても、ブラウザのようにサイトの信頼性を確認する手段がないため、利用者が安心して使えるものを作るのは難しいのではないかと思う。
 例えば、コマンドラインのプロンプトやスマホアプリの画面でID/PWを入力するアプリを作ったとすると、どこで認証するかをユーザは正しく認識する必要があるが、送信先URLに不正がないことなどを確認するのは難しいと思う。(※1)  
 ただ、利用者に毎回ID/PWを入れさせなくてもアクセス制御さえきちんとできれば十分というケースもあるだろう。このようなケースであれば後述するOAuth2.0や認証キーを使う方法などがある。

API認証のパターン


 認証(認可)の方法は、大きく分けて以下の3つがあると思う。 


1.ID/PWを送信するBasic認証やDigest認証(含むWSSE)  
2.(ブラウザの管理画面等で発行する)認証キーを使う方法  
3.OAuth2.0 アクセストークンを使う方法  

1.のBasic認証APIでID/PWをハードコードして使うケースはよくありがちで、このような安易なやり方はセキュリティ上まずいのはわかる。 APIはプログラムから利用されることを前提として作られているため,PWリスト攻撃/辞書攻撃を試みるクラッカーにとって攻撃に役立つ便利な機能になり格好なものになることは容易に想像できる。

 2.の認証キーを使う方法では、まず管理者がログインして画面を開き、そこに表示されている認証キーを使ってAPIからアクセスする。MBaaSでもよく使われており、ID/PWを入力しないため安全な方法といえる。

 3.のOAuth2.0では、アクセストークンをAPIの認証キーとして使う。アクセストークンを入手する際に最低一回は必ず、正規のWebのログイン画面で認証する必要がある。 

DropboxUploader(https://github.com/andreafabrizi/Dropbox-Uploader)はbashでOAuth2.0を使っている。(しかし実際に導入してみるとわかるのだが設定がちょっと面倒くさい) 

OAuth2.0は「認可」である。これを使うということは、つまり、認証はAPIでは行わないことと同意となる。認証キーを使う方法も、Authorizationヘッダに付与するOAuth2.0 Bearerも同様に「認可」の一部であると考えられる。認可はユーザによって正しく認証されていることが前提であり、この方式を使えばパスワードが罠サイトに送信されるような心配もない。 

2.と3.は認証と認可を別々に分けることでリスクを減らすという考え方で、これらを採用できるのであれば特に問題はないし、また冒頭に挙げたケースもこれで解決できるかもしれない。 OAuthのアクセストークンを使えばAPIで認証やる意味はないという意見もアリだと思う。ということで、OAuth2.0などの認可の話はここで終わり

 問題は、1.のような「認証」をAPIでやってよいかどうかということである。  

次にこの認証の論点に絞ってもう少し掘り下げてみる。

APIの認証をやらなくなった現状


 Google Data APIには以前、Client Login方式 (https://developers.google.com/accounts/docs/AuthForInstalledApps)というのがあったが今は廃止されてOAuth2.0に移行している。 

かつてのClientLogin方式はID/PW認証ができてとても便利だったのを覚えている。しかし、しばしばCAPTCHAロックが発生するため、アプリでID/PW固定で使うのは困難であった。結局、Googleは不正ログイン対策のためID/PW方式を諦めざるを得なかったのだと思う。(廃止理由:http://adwords-ja.blogspot.jp/2013/09/adwords-api-clientlogin.html) 

GoogleのClientLoginの件もしかり、昔流行ったWSSEにしても、 https://www.google.co.jp/search?q=WSSE で検索してもわかるように、多くのサービスがOAuth2.0に移行している現状がある。これは、リピート攻撃に弱くて筋が悪いと言われているWSSEを廃止したいことや、Webの認証機能に一元化したいという背景があるように思う。また、前述したような、ユーザがどこで認証するかを正しく認識する必要があることや、認証と認可を分けて考えればそもそもAPIに認証が不要だったというケースも多いのかもしれない。

でも、よく考えてみると、WebのログインフォームからはID/PWが送信されているわけで、API認証を廃止したところでログインフォームの動作をAPIでエミュレートすることが可能である。むしろそれを禁止する方が難しく、例えば、ログインページをスクレイピングしてtokenを切り取るなどすれば大概はエミュレートできる。(ただ、ワンタイムトークンなど発行することで難しくすることはできる) 

ブラウザでできることをプログラムから実行するのがなぜまずいのか。 今のところ、ログインフォームのようにID/PWをうまく保存する方法がないとか、不正ログイン対策のためのCAPTCHAをプログラムでハンドルするのが難しい、ぐらいしか説明が見つからない。

ログインフォームなどのセキュリティ強化策


 ID/PWを送信するAPI認証がセキュリティ上まずいとはいったものの、そもそもログインフォームのエミュレートを防げないのであればAPI認証の非公開にかかわらず意味がないし、結局はログインフォーム自体のセキュリティを強化する必要があるように思う。 

例えば、生PWを送信するリスクをどう考えるか。 ブラウザのログインフォームにしてもAPIにしても認証を行う際には必ずID/PWを必要とするが、生のPWを送信するときには細心の注意が必要である。(かつて、URLパラメータにPWを含めてしまうとRefererに機能によってリンク先に送出されることがあったように) 
またSSLで暗号化していてもログに書かれてしまうことで漏洩の危険性が高まるため可能であれば生で送信はやめた方がよいと思う。 

そこで、Basic認証は使わずにDigest認証やWSSEのようにパスワードをハッシュ化(※2)することでパスワードそのものの推定を難しくすることを考えてみる。これで万一漏洩した際にも同じパスワード使ってるサービスへの影響が少なくなるというメリットが生じる。

 個人的には、(今さらかよ!と思うかもしれんが)WSSEをハッシュ化して送信する方法で十分だと考えている。ただし、サーバではリピート攻撃に耐えうるようにWSSEをワンタイム化する。 そのまま保存してしまうとハッシュ化パスが事実上のパスワードになってしまうため、PWをさらにハッシュ化(または暗号化)したものを保存する。 生パスワードを知らないので漏れることも悪用(生パスワードを保存しておいて、他のシステムの認証にも使ってしまうようなこと)される心配もないためユーザに対して安心感は増すと考えられる。 また、初回だけWSSEを送って認証に成功したらサービス側で有効なトークン(セッションID)を発行してクライアントは以後毎回それを送るというブラウザでのセッションクッキーと同等の仕組みにすると毎回パスワードを入力しなくてもよくなるのでユーザビリティが増す。

これを実装するのに、Javascriptでパスワードをハッシュ化してXHRで送信することになると思うが、CSRF対策も考える必要がある。 
http://matome.naver.jp/odai/2133794394551702001 
http://utf-8.jp/public/20131114/owasp.pptx (P24-25に対策が載っている) 

不正ログイン対策まで考えるのであれば、CAPTCHAの他にリスクベース認証や二要素認証などの防御対策の他、不正アクセスがないかチェックするためのログイン履歴機能なども必要になってくるだろう。つまり、API認証だけのセキュリティを考えるのではなく、ログインフォームを含む認証全体の機能についてよく考えないといけないという話になる。

APIKey+WSSE


 最後に、どうしてもAPIで認証したいという場合の簡易的な方法について思いついたことを紹介する。 

これは正規のIdpからお墨付き(APIKey)をもらうことでログイン認証できる権限を得る方法である。 
具体的には、WSSEのこれまでのPasswordDigest生成ロジックを改良したもので、管理画面から入手したapikeyを先頭にくっつけてsha256にしてbase64エンコードするだけ。(注:元のWSSEはsha1だった。WSSEはワンタイムである必要がある) 
 PasswordDigest = base64(sha256(APIKey.base64_decode(Nonce).Created.Password)) 
 ID/PW以外にsecretのAPIkeyを付けることで2要素認証的な意味でセキュリティを強化できる。 また、アプリごとにトークンを使い分けることや、(不正ログインなどの)問題が発生した場合にキーを無効にできたり、より柔軟な運用が可能になる。複数回ログイン失敗で検知できる仕組みは必要だが、CAPTCHAなどのロックの仕組みは必要ないだろう。

 ただ、このようなオレオレ認証にはリスクが伴う。 
snapchatの事件(http://www.thread-safe.com/2014/01/46m-usernames-phone-numbers-leaked-by.html) も起きたばかりなので慎重に考えていきたいところである。


(2016/1/6 追記) BaaS vte.cxでは結局、こんな感じで整理した。=> セキュリティについて

(※1) もしクローズドなHTTPクライアントだとしたらどうやってSSLチェックしてホスト証明したと示せるのかが問題になる。まずはクライアント自体が信頼できるものであることを証明できないといけない。詳しくはこちらの資料(http://www.slideshare.net/shunsuketaniguchi520/ssl-28267938)のP7-を参照。適切なサーバ証明書の設定により接続先のサーバの正当性は検証できるが、スマホアプリにはアドレスバーがないものが多く、利用者が能動的に接続先や接続プロトコル(https)を確かめるのが困難である。

(※2) Saltが無いハッシュでは生パスワードが推測可能になるという危険があります。sha256(APIkey+nonce+日時+パスワード)のnonceがソルトに相当します。さらにDBに保存するときにストレッティングして保存、または暗号化します。

(追記)
@ritouさんからご意見いただいた。感謝です。



ごもっとも。
ちょっと、Resource Owner Password Credentialsについて調べてみます。

https://github.com/applicake/doorkeeper/wiki/Using-Resource-Owner-Password-Credentials-flow によれば、

以下のリクエストをOAuth Providerに投げると、

{
  "grant_type"    : "password",
  "username"      : "user@example.com",
  "password"      : "sekret",
  "client_id"     : "the_client_id",
  "client_secret" : "the_client_secret"
}

以下のように、access_tokenが返ってくるので、

{
  "access_token": "1f0af717251950dbd4d73154fdf0a474a5c5119adad999683f5b450c460726aa",
  "token_type": "bearer",
  "expires_in": 7200
}

あとは、Authorizationヘッダに"Bearer " + access_token
をAPI実行時のヘッダに付ければいけます。

PS. しかし、これはPWが生で送られている。パスワードを送信してたらログに書くとかろくでもないことをする人が出てくるという経験則がある。
オレオレ認証作ったやつには税金かけろみたいな話( http://togetter.com/li/617280 )が聞こえてきて(;゜Д゜)ガクブル だしなあ。困った。

月曜日, 9月 16, 2013

Flashをミッションクリティカルシステムで使ってよいものか このエントリーを含むはてなブックマーク

 9月11日に出たFlash Playerのセキュリティパッチ(v11.8.800.168)に不具合があり、ニコニコ動画、サイボウズ、Samsungなどで大規模障害が発生した。
 Sumsungによれば、一日あたり$600,000の損失で、少なくとも10万人に影響が出ているとのこと。 
Daily estimated damage is $600K. This is from the customer’s quote - just one company but we're now having calls from many other companies.Samsung's groupware (mySingle) is using Flash as its UI and more than 100,000 people in Samsung are having problem.
 14日に緊急修正パッチ(v11.8.800.174)が出たが、これにも不具合が残っており、Googleストリートビューが見れなくなるなど、16日現在も影響が続いている。 弊社のReflexWorksもFlashを使った印刷機能があり、影響が出ている。

原因となっている問題の箇所

 168で私が把握した不具合はExternalInterface関係で以下の2つ。 詳しくは、bugbase を参照    
  • IE8以下でFlashの関数をJSから呼ぶことが出来ない
    • FLASHオブジェクトは見つかるが内部の関数を呼ぼうとすると「is not a function」でエラー発生
  • Flash -> JSコール時のパラメータが文字化け
 174で新たに発見したのが以下の不具合
  • Flash -> JSコール時の戻り値が文字化け 
  暫定対応  

 少なくとも174であればFlashの内部関数は呼べるので、Flash -> JSコール時において、エンコード/デコードすれば問題を回避できる。 参考:http://site.oukasei.com/?p=79

Adobeの対応のまずさ

不具合Flashがもたらす破壊力は大量破壊兵器に匹敵するものである。 
もちろん、ストリートビューが見れなくてもすぐに困ることはない。被害者であるGoogleもそれほど騒いでいるように見えないのは、これを機にIEからChromeへの乗り換えを期待しているんじゃないか、という邪推もある。
   しかし、インターネット上であっても、ミッションクリティカルな業務は当たり前になり、Flashを使った業務アプリも多数存在している。それがFlashの不具合のせいで日々の業務を行えずに大変な事態になってしまったのだ。 
 Adobe社は、今回のセキュリティパッチを、9・18 サイバー攻撃への対応に間に合わせようと相当急がれたんだと思う。不具合をパラメータの部分だけ直してリリースしてしまい、戻り値の部分は直さずに見過ごしてしまった。つまり、テストもろくにせずに出していたことがバレて世界中に恥をさらすことになってしまったのだ。不具合に対応しているはずの緊急パッチにも不具合があったなんて前代未聞である。不具合は2バイト文字の文字化けという単純なものであり、英語圏の方々は発見できなかったことは想像できるにせよ、対応がお粗末すぎて空いた口が塞がらない。


 Adobeさん、お願いだからテストしてくれ!リソースを減らしすぎてテスターいないなら私に声をかけてくれ! 
 HTML5に印刷コントロールさえあれば私もFlashなんて使わないのにと心底思う。  
 これじゃ、皆に見限られてもしょうがないよ。

<追記>

  •  この不具合(BUG3630443)は11.8.800.156 (beta)で発見されていたにもかかわらず修正されずにリリースされたもよう。9・11同時多発テロに合わせて攻撃するつもりだったのか!?
  • 9/18 test build が出たもよう https://sendnow.acrobat.com/Receive.aspx?i=hZcy1XNdqp6Qn57eviBpWw
  • 9/24 こっそりFIX版である11.8.800.175が出たようだが何の説明もない。 http://blog.livedoor.jp/fs00r612/archives/51960262.html
  • 9/25 昨日オープンになった11.9.900.96(Beta)に同じBugが残っていることが判明。これがリリースされるとまた大騒ぎになる。┐(´-`)┌ http://labs.adobe.com/downloads/flashplayer.html 
  • 9/25  11.9.900.110 で修正されていることを確認。ε-(^、^; ほっ.
  • 10/10 Win8+IE10+Flash(11.9.900.117)で不具合発生。なんでこんな変更を突然勝手にやるんだ!(`⌒´♯)
    • niconico動画のxsplit broadcasterが動かない状態に(http://com.nicovideo.jp/community/co1050862?zeromypage_nicorepo)

日曜日, 12月 23, 2012

【BPStudy#64】RDBじゃだめな理由を2つほど このエントリーを含むはてなブックマーク

皆さん、昨日はお疲れ様でした。 また、運営していただいたBeProudの皆さん、ありがとうございました。

10人くらいだったら完全非公開にしようと思ったんだけど、30人くらい来てくれたので公開することにしました。 UST録画はこちら



BDBの代わりにRDBのインスタンスを複数配置するんじゃだめなの?というのはよく聞かれる質問だけど、昨日も当然のように質問された。

いつもうまく答えられなくて歯がゆいけど、それは、O/Rマッパーも必要なくなって、RDBを導入管理する必要がないから。だけど、そこはやっぱり伝わんなかったかな~。

O/Rマッパー無くしてソフトスキーマで


開発の4割がマッピングといわれ、 それに、テーブル設計、正規化、SQL、チューニングなど、 ほとんどがデータベースまわり。要は、これを全否定したいわけ。 直接オブジェクトを格納するわけだからO/Rマッパは不要になる。

 昔、EUC(Enduser computing)とか言ってたっけ。 
Lotus Notesでアプリ開発したことある人ならわかると思うけど、 RDB不要な感じでスキーマを自由に作れる開発環境は、(流行ってるかどうかは別として)、珍しいものでも何でもなく、一度経験するとその高い生産性に慣れてしまって、もう元には戻れなくなる。RDBは大変な負荷だったんだと気づく。

最近では Google App Engineや最近ではBaaSといったWebサービスもソフトスキーマで、アプリケーション内で自由に項目を設定できる

ACIDのDどうすんの?

   だって、APサーバごとにRDB導入するわけにはいかないでしょう? だから普通はDBサーバのインスタンスを分けようという話になる。でもそれだけでスケールするんだったら苦労なんてしないわけ。 
 結局、AP側にキャッシュを入れようという話になって、今だとRedisが使われるじゃないですか。で、永続化すればI/Oが詰まるので、どう溜めるかをよく考えないといけなくなる。つまり、ACIDのDをどうするか。トランザクションリカバリーとか、むしろ障害対策を頑張って作る必要があるんだけど、皆さんどんなふうに解決しているんだろうね。Append only fileで頑張ってる? まあ、2.4で非推奨になったVirtual Memoryを自力で作るぐらいの覚悟があれば大丈夫なんだろうけど。
 一方のBDBは、DBMSと同等のACIDがあるので、トランザクションログからのリカバリもDBMSと同じようにできる。単純なログファイルに書かれるだけだから、スケールアウトして複数ノードになったとしても運用はチョー楽チンだ。

BDBは、他のKVSと比較して特別に速いもんじゃないし、高機能というわけじゃない。
でも、APのプロセスで動作する一つのjarがあって、処理自体は当然メモリー内で実行されるからキャッシュ技術と同等のパフォーマンスぐらいは出せる。また、APサーバ内だけで完結するからDBサーバは実質的になくても動作する。この辺りが魅力かな。

ついでにスクリプト言語も(ry


もうそろそろ、サーバはデータを返すだけでよくないか? 

Javaなんかダセーって、一方で、HTMLタグが混在したスクリプト言語のソースを堂々とgithubで晒している。そして、パフォーマンスを競って、サーバのレンダリングを一所懸命チューニングしている。 どうかと思うよ。

twitterの例はあるとは思うけど、レンダリングはクライアントでもう十分だろ?

こんな感じで、乱筆すみませんでした。

みなさん、よいお年を。

火曜日, 12月 11, 2012

ReflexWorks開発における選択と集中の話 このエントリーを含むはてなブックマーク


何事も選択と集中が大事です。時間もお金も人も無限にあるんだったら何も苦労しません。私たちは、ReflexWorksを作るにあたって何を自作すべきか、あるいは他を利用する方がいいのか、色々悩みながら開発してきました。

標準化技術は採用すべきでしょうし、車輪の再発明は避けるべきでしょう。それは当然です。私たちも無い機能は作るし既にあれば利用させていただくという方針で基本的にはやっています。

かつてJava全盛の時代は楽でした。単にJavaの標準化技術を後追いすればよかったからです。でも、今はなかなか思うようにいきません。

10年ぐらい前からJavaの動きに鈍くなり、後追いだけでは対応できなくなってきました。
今盛んに訴えられている、JavaScriptやThin Server Architectureの重要性の話なんて(私には)4年ぐらい前のメッセージに聞こえます。

ReflexWorksもThin Server Architectureが基本ですが、既に大規模事例があり1年以上前から運用されています。
ということはどういうことか。システム構築開始は2年前で、ReflexWorksの開発はもっと前ということです。

今、JavaのThin Server Architectureに影響を受けた人が、これからフレームワーク作ってシステム構築して運用したとして、評価されるまであと何年かかるのでしょう?そのときデビューして果たして競争力はあるのでしょうか。(ReflexWorksに競争力があるとは到底思ってませんが・・)

JSONにしても2006年時点では自作するしかありませんでした。シリアライザもXML pull parserもXStreamを改造して使っています。
でも、これは当時の事情でしかたなくです。今ではご存知のようにJAX-RSなどのJavaの標準APIがあります。でも遅いんです。

今更、それに準拠するためだけにフレームワークを修正することは、少なくとも私たちにとっては無意味です。
それは、ReflexWorksフレームワークのテストには数年、数百人月という膨大な工数が費やされており、高い品質を維持しているからです。
品質は不具合の数だけでなくパフォーマンスやセキュリティなど総合的な評価が大事です。いくらJava標準といっても品質が高いとは限りません。

テストがきちんと実施され本番でも期待通りに動作している。これが最も価値ある状態であり、これでやっと他のお客様にも自信をもって最高のパフォーマンスを提供できるのです。

一方で、そのクオリティをさらに高める努力をしていくつもりです。継続的に投資していきます。

例えば、MessagePack対応。

ReflexWorksのボトルネックである、通信とシリアライザと永続化の3つの大きな要素のうち、通信とシリアライザについてMessagePackを採用することで大きな改善を見込めます。

残る永続化の部分ですが、私たちはKVSをゼロから作るなんて無謀なことはいたしません。世の中にある優れたKVSを利用しようというスタンスです。
現在はOracle BDB JEと、Google App EngineのDatastoreの2つを採用しています。(選択できます)

Oracle BDB JEを採用している理由は信頼性です。いい意味で枯れており、情報が豊富で、何よりOracleの技術サポートを受けられます。(保守契約が必要)
FAQを見ていただけるとわかりますが、トランザクションログのリカバリやトラブルシューティングも大変わかりやすく運用も楽です。

BerkeleyDB JE FAQ

トランザクションはロック方式ですが、現在はそれほど大きなボトルネックになってはいません。(他の改善効果に比べて低い)

パフォーマンスに優れたKVSは他にもいろいろありますが、トランザクションをサポートしており、かつ安心して利用できるという点で、BDB JEには一日の長があると考えています。

とはいえ、私が知らないだけかもしれません。他に信頼性に絶対的自信のあるKVSがあればぜひご紹介ください。
ちなみに、次の候補としては、Amazon DynamoDBを検討しております。



月曜日, 12月 10, 2012

バーチャルがリアルに語ります。本物のクラウドを。 このエントリーを含むはてなブックマーク


BPStudy#64の集まりが悪いので決めました。 少人数だからできる本音トーク。資料も上げたりしません。tweetもやりません。ふっふっふ。 プライベート・クラウドは良くないアイデアっていうけれど本当は凄いこと。規模の経済性だけでは語れない、本当のプライベートクラウドのメリットは確かにある。それを僕は今回のプロジェクトで体験した。 この感動を来てくれた皆に伝えたい。

300万トランザクション/日を処理するスケールアウト型オンライン帳票システムの仕組み



Agenda(案) ※内容が変わる可能性があります
  • 分散KVSについて
    • なぜ分散KVSを採用したか
      • RDBのインスタンスを複数持つんじゃだめなの?
      • RedisやMongoDB、Cassandra、HBaseじゃだめなの?
    • 分散KVSを業務アプリで使うには
  • トランザクション処理
  • スケーラビリティ
    • シャーディングのPros/Cons
  • パフォーマンス
    • 本当のボトルネックはどこか
    • Thin Server Architecture
      • 動的コンテンツをサーバで作成しない
      • 帳票作成においてサーバに負荷をかけない
  • 可用性
    • ミッションクリティカル。でも実際は・・。
    • 実際に障害に遭遇した話
  • まとめ
    • 全体最適解としてのプライベートクラウド

土曜日, 12月 01, 2012

ajn22のBTで発表してきました このエントリーを含むはてなブックマーク


ajn22、皆さんお疲れさまでした! 

また大変盛り上がって楽しかったですね〜。ajnのこの独特の楽しい雰囲気の源は一体なんだろうといつも思います。単なる事例紹介とは違って、秒間最大2000reqを処理する大規模なサービスだったり、SharededCounterの議論だったり、機械学習だったり、あるいは、クライアントMVCやリアクティブプログラミングといったことを、皆さん独自の視点や発想で研究実践され、そしてそれが惜しげもなく披露される。それは刺激的です。 

そりゃ、勉強も大事ですよ。並列分散トランザクション技術やリアルタイム処理、関数型言語・・・、いろいろ勉強しないといけない。でも、それらは実際に使われてナンボなわけで、そこからは実践的なプラクティスはなかなか生まれてこない。

ところがajnでは実際に数億のトランザクションを運用していてCounterがボトルネックなんですよ〜という話が出てきて議論が始まり、そこで、実はShardedCounter がいまだにベストプラクティスということを知るわけ。過去議論したSkiplistとか、Memcacheの利用、あるいはロックフリー技術とか駆使して何とか解決できればいいのだけれど現状はまだまだという感じ。これは大きなテーマだから今後もぜひ研究していきたいところですね。私自身、モチベーションの原点を再発見した感じです。 

あと、DEMOを見ていただいた方にお願いです。

以下のFacebookページにもいいねしてあげてください。(昨日言い忘れてました)


公報一括ダウンロードサービス

以下にTwitterのまとめと僕の資料を置いておきます。
それでは。







金曜日, 8月 17, 2012

ぶいてく流 スケーラブルアプリの作り方 2012 (前編) このエントリーを含むはてなブックマーク

明日のクラウド温泉3.0@小樽の発表資料の前編(導入部分のみ)を公開しておきます。ちょっと詰め込みすぎて1時間以内に終わらないこと確実なので・・・。あらかじめ読んでおいていただけると嬉しいです。

日曜日, 7月 08, 2012

RESTに関する3つの間違い このエントリーを含むはてなブックマーク

楽観的排他制御を利用する非同期的なトランザクション実行であればスケーラビリティを損ねることなく2phase commitが可能である。これは、分散KVSにおけるスケーラビリティと一貫性の両立について で主張したように、同期的な2phase commitは密結合に誘導することになるため、矛盾するように思えるかもしれない。だがそんなことはない。
前半はまずこの話から入るが、後半ではRESTに関する間違いについて、3つほど思うところを述べたい。

楽観的排他制御と2phase commit

 reflexworksではFeedやEntry単位でatomicなトランザクション処理を行えるが2phase commitはサポートしていない。これを許すと密結合になってスケールしないからである。だが、これはあくまで同期的な処理の話であって、ネットワーク障害への耐性を考慮され、非同期処理やオフラインで使えるのであれば2phase commitでもスケールすると考えている。
 RESTにおける2phase commitについては、REST におけるトランザクションについて (Re: Web を支える技術) が詳しい。
トランザクションレイヤを RESTful に表現する手法は理解しておいて損がないものだと思います。具体的には、
  • トランザクションの開始は POST を使ったトランザクションリソースの生成
  • トランザクション開始後は POST ではなく PUT を使い、クライアントがリクエスト ID を指定することで、ネットワーク障害への耐性を確保
  • トランザクションのコミットも再送可能じゃないと困るので PUT
  • コミットの返り値を確認してからトランザクションリソースを DELETE
といったあたりが基本になると思います。
ポイントは、クライアントがリクエスト ID を指定する点。IDはPOSTで返却されたリソースIDになるが、楽観的排他制御のためには、論理シーケンス番号 (LSN)も同時に必要になるはずだ。
もう少し具体的に説明しよう。

reflexworksでは、LSNに該当するものはリビジョン番号であり、POST /buy でリソースURL(/buy/934085,1)が返却される。(この,1がリビジョン番号で更新の都度+1される)次に、 PUT /buy/934085,1 を実行。もし既に更新されていたらリビジョン番号不一致となるため、409 conflict(optimistic locking failed)が返る。DELETEも同じくリビジョン番号を指定して実行する。
前述の例では自分しか更新しない前提ではあるが複数回実行される可能性は考慮すべきところで、べき等性の保証が必要なのだが、この場合は楽観的排他エラーで「既に更新されています」というメッセージを出してあげる方が親切だと思われる。
ちなみに、同一リソースURLでリビジョン番号も同じであれば、システム全体で同じものであることが保証されている。これは、複数のノードに散在するデータの整合性を保証するためのreflexworksの基本的な考えにもなっている。(エンタープライズクラウドにおけるスケールアウトの現実解

また、POSTはリソース作成時にのみ使い、PUTは既存のリソース更新に使うのはrelfexworksも同様である。「 一般に、リソース作成時に PUT を使うべきでないとされる理由は、クライアント間で名前の競合が発生する可能性があるから」という理由はもちろんだが、CRUDの4つは明確に区別したいという理由もある。SQLにおいても、INSERTとUPDATEは区別されている。

RESTはステートレスでなければならないか

さて、ここからRESTの3つの間違いの話に入る。
ステートレスはRESTの原理原則であり、実際に疎結合にすることでシステムはスケールラブルになる。私は、ステートレスをスケーラビリティ確保の絶対条件と考え、reflexworksではセッションは使わない方針を貫いてはいた。そして、ログイン処理においてさえセッションIDは使わずに都度認証することを徹底していた。たしかにステートレスにするとスケールはする。だが、最近は度が過ぎるとかえって不自由な設計を招いてしまうことになることがわかってきて、逆にセッションはアリなんじゃないかと思うようになってきた。

はっきりいおう。私は間違っていた。


一つは、ユーザログイン管理において毎回認証を実行させる必要があり、擬似的なセッションIDを発行する必要があったこと。具体的には、認証に成功したら新たに時間制限をつけたcookieチケット(ワンタイムパスワード)を発行し擬似的なセッションIDで対応していた。また、そのチケットはあくまでログイン認証用であり、セッションオブジェクトは管理しないようにしていた。ユーザにログイン入力を強要させるわけでもなく、名前もStateless Session ID(SSID)にしていた。
 しかし、ユーザIDとワンタイムパスワードを含むトークンを毎回クライアントと通信してやりとりするのはセキィリティ上よろしくない。私は頑にステートレスを守る一方でセキュリティをないがしろにしていたわけだ。
 また、パフォーマンスも悪くなる。
前述した2phase commitでは、払い出したチケット(/buy/934085,1)は一時的な揮発性の情報であり、これらはむしろセッションオブジェクトかキャッシュメモリに置くべきである。こうすることで、パフォーマンスも格段によくなり、KVSとの間にメモリという緩衝材が入ることでI/O負荷も軽減できる。
というわけで、最近はRESTであっても必要最低限のセッションオブジェクトだけは認めてもいいと考えるになった次第である。実際に最新版のreflexworksでは標準のセッションオブジェクトが使えるようになっている。また、一時的なオブジェクトはなるべくキャッシュさせるような設計を推奨している。

リソースは本当にドメインロジックを必要としないか

2つ目の間違いはリソースとドメインロジックの置く場所についてである。
MVCの話のなかでも触れてはいるが、ドメインロジックはバウンダリだけでなくコントロールやエンティティの層においても構わない。



にもかかわらず、当初のreflexworksでは、リソースを特定するURLに対して単純なHTTPリクエストのCRUD操作(GET,PUT,POST,DELETE)しか用意していなかった。
これだと、例えばECサイトの金額チェックなどをサーバ側で行うことができない。金額を正確にチェックするにはサーバ側でマスター参照する必要があるはずである。たとえ認証を付けたところで、リクエストデータが正しいかどうかという話は別である。どんなに頑張ってクライアントでチェックしてもサーバ側では無条件では受付けられない。
だからといって、自由にサーバ側のインターフェースを作らせたくはない。
サービスを自由に作らせると、きっとこんな感じのインターフェースになってしまうだろう。

entry = getFoo(entry)
entry = updateFooByBar(entry)
・・・

これでは、サービス志向の二の舞になってしまう。せっかく、RESTでリソースを一意に定義できているのだから、こういう感じにしたい。

entry = Foo.service(entry)

 例えば、GET  /p/Foo?item=abc などと実行すると、/p servletが呼び出され、URLパラメータとentityにもとづいて処理されるといった感じになる。/p servletの中では、reflexContext.get("Foo");とすることで実際のリソースにアクセスできる。(もちろん、POST,PUT,DELETEメソッドもある)

ちなみに、/d/Fooに対して実行すると、/d servletが呼び出され、以前のバージョンと同様に、サービスは実行されずにリソースの内容がそのまま返される仕組みとなっている。(/dはdata、/pはproviderの頭文字)

最近では、単純にデータだけが提供される、BaaS(Backends as a Service)やDBaaS(Database as a Service)が流行っているが、本当にサービスのドメインロジックが必要ないか、よく検討して使うべきだろう。

イベントドリブンなアーキテクチャーは受け入れられるか

先日、MOVEが話題になった。
コントローラをOperationsとEventsに分けた方がいいという案だが、望まれなかった子にあるように、MVCの一つの解釈(あるいは間違えているの)であり、MVCを否定するものではない。
ただ、closureや、あるいは、Deferredのような非同期処理をなんとか中心に考えたいという気持ちもわからないでもない。

実は、reflexworksでもイベントドリブンな設計はいけると思った時期があった。実際にイベントをトリガーにサーバサイドJSを実行できる。でも開発者の多くが難しいと感じ、オーソドックスな方を好んだので、今は廃止しようと考えている。オーソドックスな方とは、前述したような単純な/p servletによるフィルターのような処理のことである。

単にユニークであることが使いやすいとは限らない。




また、コントローラをOperationsとEventsに分けるより大事だと思うのは、Modelをデータ(スキーマ)とドメインロジックに分けることである。スキーマは一貫させる一方でドメインロジックは各レイヤの最適な場所に配置させる。貧血症みたいなオブジェクトにはなってしまうが、それを恐れてはいけない。

以上が、RESTに関して私が犯した間違いの3点である。
だが、もしかしたら、こうやって原理原則を修正した結果、RESTのアーキテクチャーとは到底呼べないものになってしまっているかもしれない。だがそれでも構わない。なぜなら、これらは実務に落としたときに実際に問題となって一つ一つ解決してきたものだからである。

今流行のリーンスタートアップ流にいうなら、クールなアーキテクチャだと思って採用(Build)するのは結構なことだが、測定(Measure)して、学習(Learn)してから最終的に判断しないとだめである。それが本当に素晴らしいかどうかは、実務に落としたとき初めてわかるものだから。

<ご紹介>
reflexworks           <= 製品についてはこちら
reflexworksで分業開発しよう!  <= 開発案件募集中です


 
© 2006-2015 Virtual Technology
当サイトではGoogle Analyticsを使ってウェブサイトのトラフィック情報を収集しています。詳しくは、プライバシーポリシーを参照してください。