Dartを使った大規模なアプリ開発
こんにちは、Quireです。
Quireは、Dartで開発された初めてのWebアプリケーションではありませんが、クライアントとサーバーの両方でDartをふんだんに使ったものとしては、初めてかもしれません。
動作の軽い、徹底して階層構造のタスク管理ツールです。このプロジェクトはDartコードで合計53992行、1620 KB。コミュニティーからのオープンソース ライブラリも使っています。
私たちについて
このプロジェクトを開始するまで、私たちはRikuloというDartファンの集まりでした。Rikuloはこれまで、UIフレームワークやUIライブラリ、Webサーバー、メッセージサーバー、DBクライアントなどのDartライブラリをリリースしています。
Dartが2011年に初めてリリースされたとき、すぐに将来性を感じ、さっそく小規模のプロジェクトに着手。その後、ほとんどDartで書かれた大規模アプリの開発へと進みました。今回はそのときの経験をお話しします。Dartによる開発に興味を持っていただければ幸いです。
なぜDartか
私たちがDartを選んだ理由はたくさんありますが、以下に大きなものを挙げてみます。
- 強く型付けされていて、無数の小さなミスを防ぐことができる。公式IDEのDart Editorは、タイプミスへの即時フィードバックやオートコンプリート機能を備えており、トラッキングも容易。
- 継承がクラスベースで、プロトタイプベースよりも直感的に使える。
- クライアントとサーバーの両方で同じ言語が使えて、データモデルやコードベースを共有できる。
- JavaScriptの問題点をほぼ解消。完全ではないが、以前にあった問題の99%には対応している。
- JavaScript Harmonyで人気の特徴をいくつか備えている。例えばFuture(Promise)パターン、矢印関数など。
- 強力なチームの後ろ盾があり、ハイクオリティーな公式ライブラリや使いやすいAPIを利用できる。まだ開発途上ながら、仕様はすでにかなり安定している。
- サーバーサイドではマルチスレッドではなくイベントループ方式で、好みだった。
- JavaScriptへのコンパイル時にTree Shakingできる(下記参照)。
もちろんデメリットもあります。以下に挙げてみます。
- コミュニティーがJavaScriptよりも圧倒的に小さい。
- DartとJavaScript間の通信が自明でない。
- 強い型付けなので、APIのPolyfillが面倒。
- Dart Editorの性能が(今のところ)大規模プロジェクトに適さない。
強い型のサポートにより、書くプロセスがJavaScriptよりもずっとロバストです。また、Javaよりもシンプルで、Vanilla JavaScriptと比べてもシンプルなことがあります(関数の書き方など)。全体的にDartは扱いやすい言語ですが、以下のような例外はあります。
- コンストラクタのボディよりも先にイニシャライザでFinalフィールドを初期化する必要がある。
- ミックスインの仕様が使いにくく、2.0まで改善されそうにない。
- ジェネリック型パラメータがクラスにしかなく、関数宣言にない。(そのためコンパイラの負荷が増す)。
クライアントサイド
Dart VMがChromeに統合される日まで、JavaScriptにコンパイルして作成することになります。JavaScriptにコンパイルする言語はたくさんありますが、Dartには以下のような独自のメリットがあります。
- 私たちは開発時、Dart VMを内蔵したChromiumベースのブラウザー、DartiumでDartをネイティブに実行している。このイテレーションではコンパイルが不要で、スムーズに進めることができる。
- テストとプロダクションの段階でJavaScriptにコンパイルし、すべてのメジャーなブラウザーで実行する。このとき使用するコンパイラがTree Shakingを行い、不要なコードを除去するため、JavaScriptのコードサイズを小さくできる。
サーバーサイド
サーバーサイドについてはあまりコミュニティーで話題になりませんが、私たちは以下の理由から、Dartはサーバーサイドのプログラミング言語に入ると考えています。
- Webサービスは本質的に非同期なため、イベントループ方式と相性がいい(マルチスレッドと比べて)。
- クライアントサイドよりもサーバーサイドのほうが、よりロバストで安全なコードが必要。強い型付けの言語はこの点において有利。
サポートライブラリ
Quireは約30のライブラリをインポートしています。そのうち10ライブラリはコミュニティーからで、残りはDartからリリースされたものです。Dartに詳しい方ならAngularDARTとPolymer.dartがあると思うかもしれませんが、実はどちらも使っていません。
AngularDARTを使わない理由は、以下のとおりです。
- DOMを細かく制御したい。
- 私たちはクライアント構造の構築に独自のアーキテクチャガイドラインを使用しており、そのパラダイムがAngularのロジックと異なる。
- AngularDARTについて調べたとき、コンパイルされたJavaScriptのコードサイズにかなりのオーバーヘッドが生じた。ただし、これはその後かなり改善された。
私たちは以下の理由から、Polymer.dartも使っていません。
- カプセル化とイベント リターゲティングのため、ShadowDOMはBootstrapのようなセレクター指向のフレームワークと併用できない。
- ShadowDOMの外部からスタイルを適用できない。サードパーティーのコンポーネントセットがPolymerで作成されている場合、ユーザーが見た目や感じを変えることは不可能に近い。(更新:2013年12月時点で、内部のスタイルを外部から変更可能。Shadow DOM 201を参照。)
DQuery&Bootjack
私たちのクライアントサイド スタックの基盤は、Rikuloリリースのオープンソース プロジェクト、DQueryとBootjackです。
このようにアプリケーション スタックを構築して、JavaScriptについて知っていること、できることを活用しています。
Stream
私たちのWebサーバーは、Dartのみで書かれ、ルーティング、フィルター、サーバーサイドMVCなどを備えたStreamです。イベントループ方式とシームレスに連携して動作します。リクエストハンドラーも、ノンブロッキングのルーチンをつなげるだけで書くことができ、従来のマルチスレッド方式よりも生産的かつ快適に仕事ができます。私たちは、Webサービスをスケールし、nginxでHTTPSを処理し、Streamにリクエストをデリゲートしたりもしています。このアーキテクチャによりDart VMを個別にSpawn/Despawnでき、サーバーのアップグレードもユーザーの使用を妨げずに実施できます。
終わりに
Dartは使い勝手の良い言語です。この記事がDartコミュニティー成長の一助となることを願っています。最後に、Dartで何ができるか興味のある方は、ぜひQuireを試してみてください。
Discussion