目次


多忙な Java 開発者のための LoopBack ガイド, 第 2 回

モデル

モデルとデータ・ソースを定義して使用する

Comments

コンテンツシリーズ

このコンテンツは全6シリーズのパート#です: 多忙な Java 開発者のための LoopBack ガイド, 第 2 回

このシリーズの続きに乞うご期待。

このコンテンツはシリーズの一部分です:多忙な Java 開発者のための LoopBack ガイド, 第 2 回

このシリーズの続きに乞うご期待。

おかえりなさい!このシリーズの第 1 回では、LoopBack サーバー・サイドの JavaScript フレームワークの概要を説明し、LoopBack をインストールして基本的なアプリケーションの scaffold を生成しました。また LoopBack の API ツールについてもいくつか取り上げて、詳細を探りました。この第 2 回では、LoopBack でのモデルの処理方法を見ていきます。モデルとは、データをどのように保管および取得するのかを表すオブジェクトです。LoopBack はモデル・オブジェクトを定義するために、パーシスタンスの仕組みが異なる複数のメカニズムを使用します。

まずはモデル化して保管する対象を決めなければ何事も始まりません。

LoopBack モデル

モデルをどのように適用するかをデモするために、このチュートリアルでは単純な API を開発します。けれどもその前に、モデルでどのデータをどのように使用するのかを決めなければなりません。通常、デモ目的のサンプルでは解決する問題を比較的単純なものにする必要があります。理想的には、複雑化した定義をモデル化する方法を理解できるよう、ある程度の複雑さを持たせながらも、(架空の) アプリケーション・モデルの説明に時間の大半を費やすことがないよう、それほど込み入ってはいない問題が望まれます。これを目標に、現代のミレニアム世代を前提とすると、スーパーヒーローの映画に登場するさまざまなキャラクターのすべてを追跡するシステムを構築するのがぴったりだと考えました。結局のところ、現代のスーパーヒーロー映画の複雑さを理解するには、これらのスーパーヒーローが何者で、どのような力を持っていて、どのような弱点があるのかを知っていなければなりません (デモなので、今回はこの案で進めさせてください)。

サンプルの Superhero-Information-as-a-Service (SIaaS) アプリケーションではまず、基本的なキャラクター情報を追跡します。追跡する情報は、キャラクターの実名 (姓名)、「コード・ネーム」(スーパーヒーローとしての名前) がある場合はその名前、そして出身地が地球であるかどうかです。最終的には、スーパーヒーローの相対的な「高潔さ」または「邪悪さ」を示す抽象的な数値として、スーパーヒーローの「カルマ」インデックスも追跡します。そのために使用するのは、単純な賛成票/反対票のデータです。賛成票は「善人の行い」を反映する一方、反対票は「悪人の行い」を登録します。このようにすれば、善か悪かのバケットには単純に分類できないキャラクターでも追跡できるようになります。

ゆくゆくは、スーパーヒーロー・チームのメンバーとなっているキャラクターにも注目します。スーパーヒーローの多くは複数の同盟のメンバーになっていることを考えると、そう簡単には判別できないかもしれませんが、これについては、シリーズの今後のチュートリアルで対処します。今回は、「単純なデータ」(名前や出身地などの文字列) に焦点を絞り、キャラクターの全体的なカルマを示す賛成票 (ヒーローにふさわしい行い) と反対票 (悪人のような行い) を許容すると同時に、ユーザーがカルマを直接変更できないようにするためのメカニズムを構築します (つまり、一部のデータをカプセル化して、明確に定義された入力ポイントでなければデータを操作できないようにする必要があります)。

何らかのメソッドを想像できるとしたら、それがモデル固有のものでない限り、そのメソッドは LoopBack に用意されているはずです。

モデルを定義する

まずは単純なモデル・クラスを定義するために、LoopBack CLI を使用して、ファイルとコアから scaffold を生成します。lb コマンド・ライン・ツールをまだインストールしていない場合は、npm を使用して loopback-cli パッケージをインストールしてください。第 1 回では生成するアプリケーションのタイプを尋ねられたときにサンプル hello-world を選びましたが、今回は同じ質問に対して lb を選択して実行し、empty-server アプリケーションを生成します。これが、白紙状態の出発点になります。

    lb

         _-----_     
        |       |    ????????????????????????????
        |--(o)--|    ?  Let's create a LoopBack ?
       `---------   ?       application!       ?
        ( _U`_ )    ????????????????????????????
        /___A___\   /
         |  ~  |     
       __'.___.'__   
        `  |  Y ` 

    ? What's the name of your application? heroes
    ? Which version of LoopBack would you like to use? 3.x (current)
    ? What kind of application do you have in mind? empty-server (An empty LoopBack 
    API, without any configured models or datasources)
    Generating .yo-rc.json


    I'm all done. Running npm install for you to install the required dependencies. If this fails, try running the command yourself.


       create .editorconfig
       create .eslintignore
       create .eslintrc
       create server/boot/root.js
       create server/middleware.development.json
       create server/middleware.json
       create server/server.js
       create .gitignore
       create client/README.md

このコマンドは、指定されたコアの基礎構成からなる空のアプリケーションを起動します。client ディレクトリーには README ファイルしか含まれていないことに注目してください。前回も指摘したように、LoopBack はフル・スタック・アプリケーションのサーバー・サイド/API サイドだけを生成し、API を取り込むために可能な、あるいは必要な方法については何も想定しません。LoopBack がモデルとコントローラーを提供し、ユーザーがビューを作成するという仕組みです。

アプリケーションが生成されたことを確認するには、アプリケーション・コード・ディレクトリー内で node を実行し、http://localhost:3000 を参照して JSON レスポンスを表示します。このレスポンスに、(第 1 回で目にしたように) サーバーの現在のアップタイムが示されます。次は、モデルを構築する必要があります。初期モデルのタイプは、スーパーヒーローの世界にいるキャラクターを表す Hero タイプです。けれどもその前に、モデルの保管先を指定して、そこからデータを取得できるようにする必要があります。LoopBack の用語では、モデルの保管先はデータ・ソースと呼ばれています。デフォルトでは、empty-server アプリケーションにデータ・ソースは定義されません。現在作成しているアプリケーションは、既存の要素が一切ないグリーンフィールド・アプリケーションなので、最も簡単な方法として、ここでは、インメモリー・データ・ソースを定義します。それには、以下のように lb datasource コマンドを使用します。

    Teds-MBP15-2:code ted$ lb datasource
    ? Enter the data-source name: memdb
    ? Select the connector for memdb: In-memory db (supported by StrongLoop)
    Connector-specific configuration:
    ? window.localStorage key to use for persistence (browser only): memdb
    ? Full path to file for persistence (server only): ./mem.db

データ・ソースには一意の名前を定義する必要があります。LoopBack は単一のアプリケーション内で複数のデータ・ソースと連動できるため、それぞれのデータ・ソースの名前が異なっていなければなりません。データ・ソースの名前を定義した後は、構成に関する一連の質問に答えることになりますが、インメモリー・データベースの場合、必要な構成要素は 1 つしかありません。それは、データを保管するファイルの名前です。

内部では、lb ツールが単純にいくつかのファイルを配置します。具体的には、このツールによって、server/datasources.json ファイル (単純な JSON ファイル) 内にいくつかのデータ・ソースが設定されます。

    {
      "memdb": {
        "name": "memdb",
        "localStorage": "memdb",
        "file": "./mem.db",
        "connector": "memory"
      }
    }

各 JSON 要素の値は、データ・ソースのタイプによって異なりますが、通常は lb ツールがコマンド・ラインで要求した設定パラメーターと 1 対 1 で対応しています。この例の場合、memb.db はデータベース・ストレージ・ファイルの名前として最悪だと突如思い付き、database.json に変えたいとしたら、上記の JSON リスト内にある「file」エントリーを変更するだけでよいのです。データがすでに存在する場合は、そのファイルの名前を新しい名前と一致させるだけで、データ・ストレージがリファクタリングされます。localStorage を別のタイプのデータ・ソース (MongoDB インスタンスやリレーショナル・データベース) に切り替えるとなると必要な作業が増えますが、それについては後回しにします。

データ・ソースの構成が完了したら、モデルを追加できます。この例のようにアプリケーションがグリーンフィールド・アプリケーションであれば、lb ツールではコマンド・ラインから lb model を使用して、単純なモデルを直接接続することができます。ただし、モデルを接続した途端、開発者は注目すべき選択に迫られることになります。

    $ lb model
    ? Enter the model name: Hero
    ? Select the data-source to attach Hero to: memdb (memory)
    ? Select model's base class (Use arrow keys)
      Model 
    ? PersistedModel 
      ACL 
      AccessToken 
      Application 
      Change 
      Checkpoint 
    (Move up and down to reveal more choices)

最初の 2 つのクエリーは読んで字の如く、モデルに付ける名前、モデルを関連付けるデータ・ソースをそれぞれ尋ねるものですが、モデルの基底クラスについては説明の必要があります。

Model (つまり、LoopBack Model タイプを継承するモデル) は、PersistedModel とは異なります。PersistedModel は、データベースを操作する際に一般に必要となる基本メソッド (createupdatedeletefindfindById など) を導入するモデルです。けれども、システム内のすべてのオブジェクトをデータベース内に保管すべき、あるいはそうしたほうがよいというわけでは必ずしもありません。このことから、LoopBack では、モデル・タイプにパーシスタンスが必要ない場合には、モデル・タイプを単純に維持する手段として、基本 Model タイプを継承できるようになっています。Model タイプには、単独でも興味深い、多数の有用なイベント・メソッド (changeddeleted など) があります。その一例として、checkAccess メソッドは、特定のオブジェクトへの任意アクセスを可能にするために使用できます。

以下に、PersistedModel が提供するメソッドを記載します。

  • create: インスタンスを作成してデータベースに保存します。
  • count: 渡された述部条件 (ある場合) を満たすオブジェクトの数を返します。
  • destroyById: 特定のインスタンスをデータベースから削除します。
  • destroyAll: モデル・オブジェクトのコレクション全体を削除します。
  • find: 渡された述部条件 (ある場合) を満たすすべてのモデル・インスタンスを検索します。
  • findById: 一意のモデル ID を基準に特定のモデル・インスタンスを検索します。
  • findOne: 渡された述部条件 (ある場合) を満たす最初のモデル・インスタンスを検索します。
  • findOrCreate: 渡されたフィルター・オブジェクトと一致する 1 つのレコードを検索します。該当するオブジェクトがデータベース内に存在しない場合は、そのオブジェクトを作成して返します。
  • upsert: データ・ストア内にオブジェクトがすでに存在するかどうかによって更新処理または挿入処理のいずれを選択する、更新または挿入処理 (MongoDB の用語「update or insert」を流用) を行います。
  • updateAll: 渡された条件を満たすすべてのインスタンスを新しいデータで更新します (従来型リレーショナルの UPDATE ステートメントと同様)。

以上の静的メソッドに加え、PersistedModel は、どのインスタンスでも単一のオブジェクトに対して同様のコンビニエンス・メソッドを使用できるようにします。

  • destroy: 当該オブジェクト・インスタンスを削除します。
  • getId/setId: 当該オブジェクトの一意の ID を返すか、変更します。
  • isNewRecord: 当該インスタンスが新しいものであるかどうかを示す値を返します。
  • reload: 当該インスタンスをデータベースからリロードし、それまでにオブジェクトに対して加えられたすべての変更を破棄します。
  • save: 当該オブジェクトをデータベースに保管します。

上記はすべてを包括したリストとは程遠いものです。すべてのメソッドとそれぞれの詳細を完全に記載したリストについては、LoopBack API の資料を参照してください。何らかのメソッドを想像できるとしたら、それがモデル固有のものでない限り、そのメソッドは LoopBack に用意されているはずです。これらのメソッドはすべて、それぞれの I/O 特性と NodeJS 内での対応する慣例により、処理が完了した時点で呼び出すコールバック関数を取ります。

この例での Hero クラスは、PersistedModel タイプを継承します。PersistedModel を選択すると、後続する質問が示されます。

    $ lb model
    ? Enter the model name: Hero
    ? Select the data-source to attach Hero to: memdb (memory)
    ? Select model's base class PersistedModel
    ? Expose Hero via the REST API? Yes
    ? Custom plural form (used to build REST URL): Heroes
    ? Common model or server only? server
    Let's add some Hero properties now.

    Enter an empty property name when done.
    ? Property name: Codename
       invoke   loopback:property
    ? Property type: string
    ? Required? Yes
    ? Default value[leave blank for none]: 

    Let's add another Hero property.
    Enter an empty property name when done.
    ? Property name:

Web API 開発者の多くは、定義済みモデルを REST 風の API エンドポイントを介して公開するという方法を選ぶので、LoopBack はこの点を考慮し、「Expose via REST API (REST API を介して公開しますか)」という質問に対して「Yes (はい)」を選択すると、利便性のために、いくつかのエンドポイントと URL のパラメーターが自動的に定義されるようになっています。モデル名の複数形をマシンで常に推測できるとは限らないことから、LoopBack は文法的に正しいエンドポイントになるよう、モデル名の複数形を尋ねます。

次に、モデル定義がフロントエンドとバックエンドで共有されることはよくあるので、LoopBack はこのモデルの定義をエクスポートするために、クライアントとサーバー間の共通ディレクトリー内で共有するかどうかを尋ねます。このアプリケーションはサーバー・サイド専用であるため、「server (サーバー)」を選択してコードを自己完結型にします。

最後に、LoopBack はモデル・タイプのプロパティーを定義するよう促します。最初のプロパティーは、hero のコード名です。コード名は、文字列であり、必須の設定となっていて、デフォルトはありません。プロパティー名として空白を入力するまで、LoopBack は定義するプロパティーに関する質問を重ねます。

モデル定義とファイル構造

モデルの定義を完了すると、server/models ディレクトリー内に hero.js と hero.json というファイルのペアが定義されます。JSON ファイルには、以下のように、コマンド・ラインでの選択内容を反映する Hero タイプが定義されています。

    {
      "name": "Hero",
      "plural": "Heroes",
      "base": "PersistedModel",
      "idInjection": true,
      "options": {
        "validateUpsert": true
      },
      "properties": {
        "Codename": {
          "type": "string",
          "required": true
        },
        "FirstName": {
          "type": "string"
        },
        "LastName": {
          "type": "string"
        },
        "Origin": {
          "type": "string"
        },
        "Karma": {
          "type": "number",
          "required": true,
          "default": 0
        }
      },
      "validations": [],
      "relations": {},
      "acls": [],
      "methods": {}
    }

追加メソッドを定義する

hero.js ファイル内には、Hero タイプにさらにメソッドを追加することができます。このファイルを利用して、渡されたパラメーター (Hero プロトタイプ・オブジェクト) を取り、必要なメソッドとして、ヒーローのカルマを賛成票と反対票に応じて調整する upvote および downvote メソッドなどを追加します (これらのメソッドについては、後で説明します。今のところは、データ・フィールド/プロパティーに肉付けしているだけに過ぎません)。

このモデル定義ファイル内で使用できる項目はすべて、LoopBack の資料で説明されていますが、以下に、前のリストに示されている機能について説明しておきます。

  • base: 当該モデルの基本タイプ。ModelPersistedModel についてはすでに説明しましたが、LoopBack のモデルは事前定義されている一部のモデル・タイプ (UserEmail など) を継承することもできます。
  • idInjection: LoopBack によってモデル内の「id」フィールドを管理するかどうかを指定します。「id」フィールドは、そのモデルの主キーであると見なされます。ほとんどのモデルでは、この項目はデフォルトで「true」に設定されます。
  • relations: 当該モデルと他のモデルとの関係を定義します。モデルの関係の詳細については、このシリーズの第 3 回で取り上げます。
  • validations: モデルのこの部分を使用して、プロパティーに対する検証を定義することができます。LoopBack の最新の資料には、この項目はまだ実装されないと明記されています。おそらく将来のバージョンでは、最小長と最大長の検証や正規表現に基づく検証を設定して、LoopBack に自動的に検証を適用させることができるようになるでしょう。現在のところ、検証はいずれも手作業でコーディングする必要があります。
  • acls: NodeJS 分野でのその大半のフレームワークとは異なり、LoopBack ではリッチで強力なアクセス制御モデルに従い、オブジェクトに対するユーザーのアクセスを管理するためのロール、アクセス権限、アクセス制御リストを定義することができます。LoopBack のアクセス制御については、今後の記事で詳しく探る予定です。

モデル定義ファイルは API の基本構造を定義しますが、LoopBack の開発者たちは JSON のようなデータ・フォーマットにすべての属性を取り込むことはできないことを認識しています。つまり、開発者がコードを作成しなければならない場合もあるということです。このことから、hero.js ファイルを使用して、開発者が受け渡された Hero オブジェクトを取り、その特定のモデル・プロトタイプ・オブジェクトに適していそうな属性を任意に追加できるようになっています。

例えば前述のとおり、ヒーローの「高潔さ」はカルマによって評価します。ヒーローのカルマは、善い行いをするたびに高くなり、悪い行いをするたびに低くなります。通常、ヒーローの属性に影響を与えるには PUT リクエストを行って、新しいデータをリクエストの本文の一部として渡します。けれどもカルマの場合は、賛成票と反対票によってのみ属性に影響が与えられるようにするという方法が優先されます。LoopBack でこの目的を達成するには、Hero に 2 つのメソッドを作成し、それらのメソッドをリモート・メソッドとして登録するという方法があります。つまり、これらのメソッドを Hero Web API 全体の一部として統合するということです。

(空の) hero.js ファイル内で、この 2 つのメソッドを追加します。以下のように、最初にこれらのメソッドを Hero に定義してから、各メソッドをリモート・メソッドとして登録します。

    module.exports = function(Hero) {
        Hero.prototype.upvote = function(cb) {
            var response = "Yay! Hero did a good thing";
            this.Karma += 1;
            this.save(function(err, hero) {
                if (err) throw err;

                cb(null, hero);
            });
        };
        Hero.remoteMethod('prototype.upvote', {
            http: { path: '/upvote', verb: 'post' },
            returns: { arg: 'result', type: 'object' }
        });

        Hero.prototype.downvote = function(cb) {
            var response = "Boo! Hero did a bad thing";
            this.Karma -= 1;
            this.save(function(err, hero) {
                if (err) throw err;

                cb(null, hero);
            });
        };
        Hero.remoteMethod('prototype.downvote', {
            http: { path: '/downvote', verb: 'post' },
            returns: { arg: 'result', type: 'object' }
        });
    };

メソッドは、Hero のプロトタイプ上に定義されていることに注意してください。メソッドをインスタンス・メソッド (つまり、Hero の各インスタンス上のメソッド) として定義するためには、このようにする必要があります。このように定義されていないと、LoopBack はこれらの追加されたメソッドを、Hero の特定のインスタンスに関連付けられていない静的メソッドであると想定します (したがって、賛成票または反対票の対象となっている Hero のカルマを参照できません)。Karma が適切に変更された後は、HeroPersistedModel から継承する組み込み save メソッドに頼って、Hero の新しいデータをデータ・ソースに書き込むことができます。

リモート・メソッドを登録するのは比較的簡単で、remoteMethod() メソッドを呼び出せばよいだけです。このメソッドは、登録するメソッドの名前とそのメソッドを公開する URLに関するデータを取ります。これらのデータには、HTTP パスと動詞、引数の説明 (この例では必要ありません)、戻り値があります。

必要以上の情報を取るようにも思えますが、最終結果として、LoopBack は公開するエンドポイントに関する直接情報を入手して、その情報により、これら 2 つのエンドポイントを LoopBack GUI の「Explorer (エクスプローラー)」ビュー内で公開される Swagger UI に追加できるようになります。

モデルの検証

同じコード・ネームを使っているヒーローが 2 人いないことを確認する検証コードを追加することもできます。それには、hero.js に検証のための制約を追加します。

    module.exports = function(Hero) {

        // ... as above

        Hero.validatesUniquenessOf('Codename');
    };

上記のコードでは、validatesUniquenessOf メソッドを利用して、PersistedModel Validatable ミックスインを適用します。Validatable には、以下のメソッドが含まれています。

  • validatesAbsenceOf: 指定された 1 つ以上のプロパティーがモデルにないことを検証します。つまり、モデルに特定のプロパティーが含まれていなければ有効と見なされ、検証対象のフィールドがブランクでなければ無効と見なされます。
  • validatesExclusionOf: プロパティーの値が特定の値の範囲内にないことを検証します。
  • validatesFormatOf: プロパティー値が特定の正規表現と一致していることを検証します。
  • validatesInclusionOf:プロパティー値が特定の値の範囲内であることを検証します (validatesExclusionOf と論理的に逆の検証です)。
  • validatesLengthOf: プロパティー値の長さが指定の範囲内であることを検証します。
  • validatesNumericalityOf: プロパティーが数値であることを検証します。
  • validatesPresenceOf: モデルに特定のプロパティーの値があることを検証します (ValidateAbsenceOf と論理的に逆の検証です)。
  • validatesUniquenessOf: プロパティー値がすべてのモデルを通じて一意であることを検証します。すべてのデータ・ストアがこのメソッドをサポートしているわけではないことに注意してください。このチュートリアルを作成している時点でこのメソッドをサポートしているのは、Oracle および MongoDB データ・ソース・コネクターです。
  • validate: カスタム検証関数を追加するために使用できます。

さらに、Validatable は各インスタンスに isValid() メソッドを追加するので、明示的にオブジェクトを保管しなくても、随時 (例えば、オブジェクトを送信する前に) オブジェクトが有効であるかどうかを検証できます。

明確にしておくために言っておきますが、Validatable とそのメソッドには、基本 Model タイプを継承するすべてのモデルからアクセスできます。したがって、検証を利用するためにオブジェクトを PersistedModel にする必要はありません。

モデルを発見する

LoopBack は、ゼロから再構築できない既存のデータベースを使用している開発者のことを忘れてはいません。LoopBack には、リレーショナル・データベースやスキーマなどの既存のデータ・ソースからモデルを発見する機能が用意されています (現在のところ、MySQL、PostgreSQL、Oracle、および SQL Server がサポートされています)。この 1 回限り実行されるプロセスでモデル定義ファイルが生成されると、LoopBack はそれらのファイルを他のあらゆるモデル定義と同じように使用します。

LoopBack の資料では、このプロセスは、以下のようにスタンドアロンの NodeJS スクリプト内で実行することを推奨しています。

    var loopback = require('loopback');
    var ds = loopback.createDataSource('oracle', {
      "host": "oracle-demo.strongloop.com",
      "port": 1521,
      "database": "XE",
      "username": "demo",
      "password": "L00pBack"
    });

    // Discover and build models from INVENTORY table
    ds.discoverAndBuildModels('INVENTORY', {visited: {}, associations: true},
    function (err, models) {
      // Now we have a list of models keyed by the model name
      // Find the first record from the inventory
      models.Inventory.findOne({}, function (err, inv) {
        if(err) {
          console.error(err);
          return;
        }
        console.log("\nInventory: ", inv);
        // Navigate to the product model
        // Assumes inventory table has a foreign key relationship to product table
        inv.product(function (err, prod) {
          console.log("\nProduct: ", prod);
          console.log("\n ------------- ");
        });
      });
    });

このスクリプトを実行時に使用可能にするには、出力をファイル (通常は common/models/model-name.json) に書き込む必要があります。その後、手作業で server/model-config.json ファイルに登録してください。

リレーショナル・データベース以外のデータ・ソースの場合 (つまり、MongoDB データベース、REST データ・ソース、または SOAP データ・ソース)、LoopBack では非構造化 (JSON) データ・インスタンスからモデルを推測することもできます。それには、リレーショナル・データベースの場合と同様に、推測の事例となるインスタンスを取得し、それをデータ・ソースの buildModelFromInstance() メソッドに渡します。このメソッドから返される、生成されたモデル・タイプを、これまで説明した Hero オブジェクトと同じように使用できます。LoopBack は、ロー JSON オブジェクトを使用して、このようにモデルを作成する例をデモします。

    module.exports = function(app) {
      var db = app.dataSources.db;

      // Instance JSON document
      var user = {
        name: 'Joe',
        age: 30,
        birthday: new Date(),
        vip: true,
        address: {
          street: '1 Main St',
          city: 'San Jose',
          state: 'CA',
          zipcode: '95131',
          country: 'US'
        },
        friends: ['John', 'Mary'],
        emails: [
          {label: 'work', id: 'x@sample.com'},
          {label: 'home', id: 'x@home.com'}
        ],
        tags: []
      };

      // Create a model from the user instance
      var User = db.buildModelFromInstance('User', user, {idInjection: true});

      // Use the model for create, retrieve, update, and delete
      var obj = new User(user);

      console.log(obj.toObject());

      User.create(user, function (err, u1) {
        console.log('Created: ', u1.toObject());
        User.findById(u1.id, function (err, u2) {
          console.log('Found: ', u2.toObject());
        });
      });
    };

非構造化データを事例として使用する場合は、このスクリプトを、サーバーの通常の起動シーケンスの一環として実行されるブートストラップ・スクリプトとして実行するほうが簡単な場合もあります。けれども、データの構造が (おそらく開発者にもわからずに) 常に変わるのではない限り、サーバーが起動するたびにデータの再解析にリソースを使用するよりも、スクリプトを 1 回実行して、その結果をモデル定義ファイルに変換するほうが理に適っています。

モデル API と LoopBack GUI

サーバーを再起動して (ルート・ディレクトリー内で node を実行)、http://localhost:3000/explorer を参照すると、Hero タイプがすでに関連付けられていることがわかります。新しいヒーローを作成するには、対話式 GUI を使用できます。例えば、POST /Heroes エンドポイントを使用してシステムにヒーローを追加した後、GET /Heroes エンドポイントを使用すると、追加されているすべてのヒーローを一覧表示するリストを確認できます。

API を演習する単体テストの代わりになるわけではありませんが、LoopBack GUI ではこのように、開発中に対話式で簡単に API をテストすることができます。

モデルのブートストラップ

通常は、どのシステムにも起動時に自力で出現させなければならないオブジェクト一式があるものです。どのスーパーヒーローの世界にも常に存在するヒーローが何人かいるため、便宜上、それらのヒーローをシステムの起動時にあらかじめロードすることにします。LoopBack には、このようなブートストラップ・コードのフックがあります。それは、boot サブディレクトリーです。このディレクトリー内に保管されるコードはすべて、サーバーの起動時に実行されます。このディレクトリーに私たちが追加しなければならないのは、起動時に呼び出される関数をエクスポートするファイルだけです。この関数には、唯一のパラメーターとしてアプリケーション・オブジェクトが渡されます。このパラメーターを使用して、モデル・プロトタイプ・オブジェクトを取得し、オブジェクトがまだ存在していない場合は新しいオブジェクトを作成することができます。

    module.exports = function(app) {
        app.models.Hero.count(function(err, count) {
            if (err) throw err;

            console.log("Found",count,"Heroes");

            if (count < 1) {
                app.models.Hero.create([{
                    Codename: "Superman",
                    FirstName: "Clark",
                    LastName: "Kent",
                    Karma: 100
                }], function(err, heroes) {
                    if (err) throw err;

                    console.log('Models created:', heroes);
                });
            }
        });
    };

定義済みのすべてのモデル・タイプは app.models のメンバーになるため、コード・ベース全体にわたり、どこでアプリケーション・オブジェクトを参照するとしても、いずれのモデルも単純なプロパティーの逆参照となります。

まとめ

LoopBack モデルについては、このシリーズで今後モデルを探る過程でも、LoopBack を独自に調べる過程でも、その詳細を発見することになりますが、発見する内容のほとんどは、今回取り上げたテーマに基づくバリエーションです。これまでに説明した内容を理解していれば、LoopBack モデルの操作を開始できます。ただし、第 3 回ではさらに大きなトピックが待ち受けています。それは、モデルの間の関係をどのように確立するかというトピックです。LoopBack では、構造的に関係をサポートするようになってはいるものの、関係をモデル化するのは (特に異なる種類のデータ・ストレージ・システムの間でモデル化する場合)、常に単純明快で直感的な作業であるとは限りません。LoopBack は基礎となるデータを抽象化しようとしますが、あらゆる状況であらゆるシナリオを処理できるわけではありません。

今のところは気持ちを切り替えて、LoopBack をお楽しみください!


ダウンロード可能なリソース


関連トピック


コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Web development, Open source
ArticleID=1046981
ArticleTitle=多忙な Java 開発者のための LoopBack ガイド, 第 2 回: モデル
publish-date=06292017