体感!JavaScriptで超速アプリケーション開発 -Meteor完全解説

第7回クライアントとサーバのコード共有を促進するデータベースアーキテクチャ

従来のWebアプリケーションでは、フロントエンドとバックエンドは完全にコードが分離しているのが普通でした。そして、フロントエンドとバックエンドでは扱う言語が異なるのも一般的です。フロントエンドではJavaScript、バックエンドではJava、Perl、PHP、Python、Ruby、などの言語を用いていたのです。こうしたフロントエンドとバックエンドの断絶があったことから、両者の間ではコードの共有が行われることはほぼありませんでした。エラーメッセージのような、両者で統一して取り扱うべきリソースを共有するのも一苦労でした。

しかしMeteorでは、サーバもクライアントもJavaScriptで記述するというアーキテクチャを取っていることから、サーバとクライアントの双方で多くのコードを共有することができます。特にMeteorは、クライアントとサーバでできる限り同様のコードが動作するよう考えられたアーキテクチャのため、コードを共有できる範囲は非常に大きく、Webアプリケーション全体のコードも非常にコンパクトになります。

今回は、そうしたMeteorのアーキテクチャを、データベース処理を中心としながら学んでいきたいと思います。データベース処理の詳細については、次回以降に開設します。

Meteorにおけるデータベース処理の考え方

Meteorは、データベース処理に対し、これまでのWebアプリケーションとは全く異なるアプローチを取っています。それは、⁠サーバ上でデータベースを扱うのと同様のコードを、クライアントでも利用できる」というものです。

そのことを端的に表す例を挙げると、以下のコードはデータベースからデータを一件取得するためのコードですが、これはサーバだけではなく、クライアント上でも動作します。

// 名前が「白石」のデータを取得する
Employees.find({name: '白石'});

なぜこのコードがクライアント上でも動作するのでしょうか?それは、すべてのMeteorクライアントはサーバ上のデータをメモリ上にキャッシュするためです。上記のコードがクライアント上で実行された場合、それはキャッシュに対するデータ取得処理となります。

図1 Meteorクライアントは、サーバデータのキャッシュを保持する
図1 Meteorクライアントは、サーバデータのキャッシュを保持する

また、クライアント上でデータを変更した場合はどうなるのでしょうか?その場合には、まずクライアント上のキャッシュが変更され、即座にサーバに対して変更処理がリクエストされます。そして、サーバ上のデータが正常に変更されると、その変更はすべてのMeteorクライアントに伝えられ、全クライアントのキャッシュが変更されることになります(いわば、全クライアントのキャッシュが「同期」されるわけです⁠⁠。

図2 Meteorクライアントからの変更は、サーバ経由で全クライアントに伝わる
図2 Meteorクライアントからの変更は、サーバ経由で全クライアントに伝わる

こうしたアーキテクチャにより、以下に挙げるようなさまざまな利点を得ることができます。

  • サーバとクライアントのコードを共通化できる。たとえば、入力データの妥当性を検証したあとにデータベースを更新するようなコードを、サーバとクライアントの双方で利用できます。
  • クライアント側では、基本的にキャッシュを対象とした処理となるので、データの取得処理や更新処理が非常に速い。ネットワークの遅延を感じさせることなく、ユーザに素早く処理結果を表示することができます。
  • さらに、別のクライアントが行った変更が瞬時に伝わるため、UIのリアルタイム性が非常に高くなります。一般的なWebアプリケーションでは、実現が容易ではないリアルタイムWebアプリケーションを、いとも簡単に実現することができるのです。

Meteorはデフォルトでは、サーバ上のデータすべてをクライアントがキャッシュするようになっています。これは、Meteorを学び始めた開発者にとっての手軽さを優先させているためです。

とはいえ実際には、巨大なサーバデータのすべてをクライアント側にキャッシュすることは現実的ではありません。Meteorのデフォルト動作を変更し、キャッシュするデータを絞り込む方法については、次回以降にお伝えします。

データベースを扱う基本的なプログラム

では、Meteorにおけるデータベースプログラミングを見て行きましょう。

現在のMeteorは、MongoDBを使用することが前提となっています。将来的には他のデータベースも取り扱えるようになる予定ですが(⁠⁠他の」というのが、⁠他のNoSQLデータベース」を意味するのか、それとも「RDBも含めたデータベース」なのかは不明です⁠⁠、現在はMongoDBで利用できるJavaScript APIをほぼそのまま利用できるイメージです。

また、クライアント側でもサーバと同様のAPIを利用できるようにするため、Minimongoというライブラリを使用してキャッシュが実装されています。MinimongoはJavaScriptで書かれたインメモリデータベースで、MongoDBと同様のAPIを兼ね備えています。

以下のサンプルは、サーバ上のデータを一覧表示するだけのプログラムです。

図3 サンプル7-1の実行結果
図3 サンプル7-1の実行結果

サンプルは以下からダウンロードできます。

まず、このサンプルのHTMLを以下に示します。

リスト1 sample7-1.html
<head>
  <title>サンプル7-1</title>
</head>

<body>
  {{> employeeList}}
</body>

<template name="employeeList">
  <table id="employeeList">
    <thead>
      <tr>
        <th><input type="checkbox"></th>
        <th>名前</th>
        <th>年齢</th>
      </tr>
    </thead>
    <tbody>
      <!-- employeesの値をループして一覧を生成 -->
      {{#each employees}}
      <tr>
        <td><input type="checkbox" name="chk" value="{{_id}}"></td>
        <td>{{name}}</td>
        <td>{{age}}</td>
      </tr>
      {{/each}}
    </tbody>
  </table>
</template>;

ポイントは、employeesという変数をループ処理して一覧を生成している部分のみです。JavaScriptファイルは以下のようになります。ここに、Meteorのデータベースプログラミングに関するさまざまなポイントが凝縮されています。

リスト2 sample7-1.js
(function() {
    'use strict';

    // (1) コレクションの生成
    var Employees = new Meteor.Collection('employees');

    // クライアント上でのみ動作するコード
    if (Meteor.isClient) {
        // テンプレート内のemployeesという変数の値を返す
        Template.employeeList.employees = function() {
            // (2) コレクション内の全データを返す
            return Employees.find();
        };
    }
    // サーバ上でのみ動作するコード
    if (Meteor.isServer) {
        // 起動時の処理
        Meteor.startup(function () {
            // (3) 全データを消去する
            Employees.remove({});
            var data = [
                { name: 'しゅんぺい', age: '34' },
                { name: 'たえこ', age: '33' },
                { name: 'こうたろう', age: '4' },
                { name: 'ちほ', age: '2' }
            ];
            data.forEach(function(emp) {
                // (4) データを挿入する
                Employees.insert(emp);
            });
        });
    }

})();

ポイントとなる部分を説明します(番号はコード中のコメントに対応しています⁠⁠。

(1) MongoDBでは、RDBのテーブルに相当するようなデータの集合を「コレクション」と称します。Meteor.Collectionは、コレクションを生成するためのAPIです。このコードは、サーバでもクライアントでも実行されることに注意してください(clientやserver、publicといったディレクトリ以外に格納されているJavaScriptファイルは、サーバでもクライアントでも実行されます。連載第2回を参照してください⁠⁠。以降のコードでは、このコレクションに対して検索や更新といった処理を行いますが、コレクション内のデータがサーバとクライアントで完全に同期されるため、クライアントでもサーバでも全く同様のAPIでデータ処理を行えています。

(2) Employees.find()は、コレクション内のデータを検索するためのAPIです。ここでは、find()メソッドになんの引数も渡していないので、コレクション内の全データを返します。この部分のコードは、Meteor.isClienttrueの場合にのみ(つまりクライアント上でのみ)実行されます。

(3) Employees.remove({})の呼び出しにより、全データを削除しています。このコードはサーバの起動時に実行されますMeteor.isServertrueの場合にのみ実行されている⁠⁠。remove()メソッドの引数は削除対象を絞り込むためのクエリオブジェクトですが、ここでは空のクエリを渡しているため、すべてのデータが削除対象となります。コレクション操作のAPIについては次回詳しく触れます。

(4) コレクションに値を挿入するため、insert()というメソッドを使用しています。コレクションには任意のJavaScriptオブジェクトを格納することができます。

繰り返しになりますが、このコードのポイントは、Employeesというコレクションに対して、サーバとクライアントが全く同様にメソッド呼び出しを行なっている点にあります。findremoveinsertといったメソッドは、クライアントでもサーバでも動作します。

通常、サーバ上のデータを参照するためのコードを記述するには、サーバ上の処理をラップしたWeb APIを公開し、クライアント側はそれを呼び出すためにXMLHttpRequestを使用する…といったコードを必要とするものですが、Meteorでは一切そういうコードは必要ありません。まるで、クライアントが直接サーバのDBにアクセスしているようなコードを記述できるのです。

まとめ

Meteorでは、データベースに対する操作をクライアントでもサーバでも同様に行えるというのが非常に画期的です。そのことが、生産性を非常に向上させるだけではなく、UIのリアルタイム性も向上させており、圧倒的に使い勝手の良いWebアプリケーションを作成することができます。

次回は、今回詳しく触れることができなかったMeteorのデータベースAPIについて解説します。

おすすめ記事

記事・ニュース一覧