Block Rockin’ Codes

back with another one of those block rockin' codes

CouchDBをmockにしてAPI駆動開発(仮)

Appengineでのアプリはいくつか作っていますが、最近考えていたこれまでとは別のアプローチでアプリを作ってみたいということで、
以下のような技術達を取り入れながら、TODO管理アプリの作成に取りかかりました。


目的はTODOアプリを作るということより、今まで勉強して来た技術を取り入れて、
組み合わせた開発手法自体を試してみたいという感じです。

取り入れる技術

etc

API-Appengine

最初のモチベーションは、「サーバはJSONを吐くだけにして、全部Ajaxでやる。」
AppengineのAPIサーバ化です。
AppengineはDataStoreの中身をJSONシリアライズして吐くだけにして、全てAjaxでhtmlに反映する。
APIをHTTPで叩く、UIはそれぞれのプラットフォームに合わせるというスタイル。
そうすれば、クライアントがブラウザでも、iPhoneでも、iPadでも、Widgetでも、
好きな形で書いて、APIを叩けばいいだけなので、クライアントの選択肢が色々出てくる昨今、柔軟に対応できます。
今後HTML5等を使って色々やる上でも、やはりJSON-APIは扱いやすいはず。


考え方自体はまあ、珍しくもないかと思いますが、
特にフロントエンドを得意とされる方に影響を受けやってみることにしました。


今回はこのスタイルで簡単なTODOアプリを作成します。
アプリ自体はappengineからピュアなhtmlとjsのみ返して、データは全て$.getJSON()という感じで作ってみます。





APIGUIの仕様

このパターンでやる時、やはりAPIの設計が鍵になるので、
URIはもちろん、返ってくるJSONの構造もしっかり考えないといけない。
APIは使い回すことが前提なので、今回だけに最適なものも設計としては失敗な訳です。


ところがRESTfulなAPIを作ろうとした時、じゃあそのURIをどうするか、返ってくるJSONはどういう構造でどんな情報を持つべきか。
そのAPIは実際に叩いた時使いやすいのか。本当にCoolなURIになっているのか。
そういった設計は、やっぱりなかなか難しいです。


なれていれば、きっちりAPIを設計して、手戻りも無いかもしれませんが、
自分はそこまで慣れていないので、先にAPI(サーバ側)を作っても、
GUI側で、「やっぱ、このAPI使いにくいよね?」などとなった時、
サーバ側のコードも、DataStoreの設計も色々手戻りが生じます。


やはりAPIの設計は実際に使いながら試行錯誤してやらないとなかなか難しい。
db.Expandを使っていれば、フィールドの追加自体は簡単ですが、
開発途中に追加すると、フィールド追加前のentity達は値が無いので、
これもAPI化する上では色々面倒。
(このパターンのDB整理はmap/reduceができるようになり便利にはなりました。)


APIが先、だからGUIを先にやってみる


APIは先にきちんと設計煮詰めてから組むのが一番。
でもそのAPIの妥当性は使ってみないと分からなかったりするので、
それならそれを使うGUIを先に作ってしまうのもありだと思ったんです。
でも、サーバが無いとAjaxリクエストが投げられないので開発が進まない。
サーバはまだ組みたくないけどAjaxを使ったGUIを組みたい。
モックを組んでもいいんですが、そこで登場するのはCouchDBです。
CouchDBを使えば、ローカルでその環境を簡単に構築できます。






CouchDBAjaxのモックに

CouchDBは、いわゆるKVSなんですが、実は色々な特徴があって、
それが今回のニーズにぴったりだと考えました。

CouchDB

  • データをJSONで格納している!(変換の必要なし)
  • HTTPでGET、POSTできる!!(それ自体RESTfull API)
  • WEB,APサーバにもなる!!!(Same Origine Policy問題無し)
  • データの操作がGUIで簡単に編集出来る!!!!(簡単に変更してすぐに試せる)

という素晴らしいDB(?)です。
CouchDBにはCouchAppというツールがあって、
これで、Couchが入っててDBを作ってあれば、
すぐにアプリケーションを組むことが出来ます。


実際は

$ sudo easy_install couchapp
$ couchapp generate toodoo

で.couchapprcを編集。
もちろんjs

{
    "env": {
        "default" : {
            "db" : "http://localhost:5984/toodoo"
        }
    }
}

あとはデプロイするだけ、

$ couchapp push

こんな感じですぐに動きます。

本当はCouchDB自体に色々機能があり、
例えばCouchのデータにアクセスするjQueryライブラリなども用意されてます。
しかし、今回は普通のjQueryを使ってアクセスすることで、CouchAppに対する依存を無くし、
appengineに載せ変えたとき返るのはURIだけにできるように試みます。
なので、CouchAppの機能ではなくただのJSONと静的ファイル用のサーバとして使っています。


Couchappで作ったプロジェクトは、静的ファイルを
_attachemntsに格納するので、
そこにhtmlとjsを入れておけば動きます。
後は
http://localhost:5984/toodoo/
にアクセスするだけ。


例えば、taskというDBを用意していれば、
http://localhost:5984/toodoo/task
に対してAjaxリクエストを送れます。



この辺は@さんがこれまで発信されていた内容から学びました。
詳しいことは、こちらを見れば全て書いてあります。
(注意:今回はCouchDBのある側面を利用しているだけで、実際のCouchはもっと凄いです。あくまで自分が考えた一つのユースケースです。)


API = URIJSONの設計


CouchDBは、データベースのアクセス方法がHTTPであり、GETしたら返ってくるデータはJSON形式です。
もちろん、POSTでデータを追加できるし、DELETEすれば消えます。(まあとりあえずGETとPOSTしか使いませんが)

そのURICouch内のDBの構造を変更すれば変わります。
そしてCouchはそういった変更をFutonというWebGUI経由で簡単に変更できます。
このため、いちいち開発サーバにデータ入れ直したり、
app.yamlやModelやQueryを変更したりせずに、色々なURIの試行錯誤を気軽に出来ます。


つまりCouchをMockのような感じで使う訳です。




ちなみにCouchDBのテーマは「relax」です。

じゃあGUIから考えよう

GUIはこれで設計しました。
Cacoo: Online Diagram Software for Flow Chart & UML and More.
これは、最近知った図を作成し共有することが出来るサービスです。
使えばすぐに分かりますが、かなり作り込まれていて、今回取り入れてみました。
(このブログに出てくる絵も、Cacooで作りました。)


Webサイト用の作図パーツや、UML、ネットワーク図用のアイコンなんかも色々揃ってて、
他人と共有して、共同編集ができ、チャットもあります。
出来たものは公開できるし、PNG出力も出来ます。
これで具体的なUI構成のラフを検討します。

jQueryUIでデザイン責務を委譲

ラフで方向性が決まったら、実際にGUIを組みながら進めて行きます。
図だけでGUI全てを決めたりしません、作って使ってを繰り返します。


ところが等方ビジュアルデザインについてはセンスが全く無いので、
jQuery UI
これに完全に乗っかる形でデザインを完成します。
必要なパーツを拾って来て並べるだけ。
自分たちがやったのは、テーマカラーを選ぶだけw

ドッグフードを食べながら

これらが揃ったら、FutonCouchにデータを入れ、
getJSONでデータを取得し、jQueryUIの画面に反映。
これをQUnitでテストしながら行い、
JSONの構造やURIを煮込んで行きます。


作りながら、GUIが不便だったらどんどん変えます。そしたらAPIも変わるかもしれません。
APIGUI二つのドックフードを食べながら作って行く訳です。


こうして、常に自分が最初のユーザになって、APIを少しずつ完成させる方針です。


もちろんペアプロでTDD

これらの作業を、ペアプロでTDDでやっていきます。
今回は@とペアを組みました。
JavaScriptもでのペアプロは経験が浅いので、色々試行錯誤しながらやりたいと思います。


今回はJSのテストはQUnitを使うことにしました。
これは、JSで記述してRed-GreenはHTMLで確認していくスタイル。
非同期通信をテストするためのstop(),start()が用意されており、
asyncTestというものも用意されています。


あと、もしDOMにもHTTPにも触らないロジックだけを切り出せるなら、
こちらも試してみたいところです。
QUnit-TAP : JavaScript のテスティングフレームワークQUnitからTAP出力する - t-wadaの日記
QUnitのロジックテストをRhainoで実行する方向性です。
デプロイしないでも出来る、ライブラリ化出来る部分には効いてくると思います。


もちろんAPIが固まってappengine側を実装する時も、
PythonペアプロTDD、テストはnose-GAEを使う予定です。

Keep

  • Couchのmock的使用は上手くいっている。
  • QUnitも今のところ回っている。
  • ドックフードをしっかりと食べられているので、これも続ける。
  • @とのペアプロは二回目、その点も特に問題無し。
  • 最初の作業で各部の連携が上手くいったので、方向自体は定まった。

Problem

try

懸念

Couchへのデプロイ

始める前から気になっていた、Couchのデプロイ時間です。


Same Origine Policyでは「プロトコル + IP + Port」が同一でないと、Ajax通信が出来ません。
つまり、CouchからJSONを取得するのは、Couchから来たjQueryでないといけない。
またQUnitはテストをjsで書いてデプロイして走らせます。
だからコードはもちろん、テストを変えたらそれも毎回Couchにデプロイする必要があるのです。


Couchのデプロイ自体コマンド一発だし、今回の規模では「1秒」くらいで終わります。
特に再起動等はいりませんので、このくらいならまあ我慢できます。
しかし、本当にファイルのサイズが増えて行ったら、ちょっときついかもしれません。


ただ、ファイル達がある程度重くなったころには、APIも大体固まってるはずなので、そしたら決まったところだけ組んでしまって
dev-appsesrverにしてもいいかもしれません。ローカルならコンソールからデータも入れられますし、ある程度変更には耐えられる。
あとは、DOMや通信に触らないソースを切り出し、それはQUnit-TAPでやるなどの工夫が必要かもしれません。

QUnitテストのメンテナンス

CouchJSONの構成を変えた時の、テストのメンテナンスも懸念しています。


DOMに関するテストは限界があるので、あまり書くつもりは無かったんですが、
APIとその周辺ロジックについてはちゃんとテストを書きたいと思っています。
一方でAPIGUIはガツガツいじりたいので、テストも頻繁なメンテの可能性があります。


QUnitでのテストは経験が浅いので、今までのテストと毛色が違うだろうことは分かっていますが、
これはどうなるか、やってみないと分かりません。
何か分かったら、また書きたいと思います。

APIの汎用性

煮込んだAPIが、実はこのGUI最適であって、汎用的ではない実装になってしまうとか、
「テストしやすいだけ」のAPIにならないか。あとちゃんとRESTfulになるか。
本当はこの辺が一番自身無いので、出来たら誰か詳しい人にレビューとかして欲しいなぁ。





書き始めたらブレストになってしまいましたが、
とりあえず書きたいことと、今思っていること考えていることは全部書きました。


そしてこのやり方自体にも若干手応えを感じているので、また作り進めたら色々書いて行こうと思います。
しかし、CouchDB自体も色々話題になっているので、こういう使い方をしている人はそれなりにいるんじゃないかと思います。
もし何かノウハウやご指摘があれば教えてもらえると幸いです。