BT

最新技術を追い求めるデベロッパのための情報コミュニティ

寄稿

Topics

地域を選ぶ

InfoQ ホームページ アーティクル Yogaを使ってRESTを柔軟にする

Yogaを使ってRESTを柔軟にする

ブックマーク

原文(投稿日:2013/06/27)へのリンク

REST APIは設計がエレガントであり、とても魅力的です。REST APIを使えば、GoogleのAdam Bosworth氏が言うところの、“シンプルさ、緩やかさ、いい加減さ、拡張しやすさ”を手に入れられます。しかし、安定した性能を提供する設計ではありません。

既存のRESTフレームワークはドメインモデルをJSONやXMLの応答に変換するのに優れた仕事をします。しかし、各リソースが単一のドキュメントビューを持っているという仮定で動作します。リソースに対するすべてのリクエストは完全なドキュメントを返します。リクエストがデータの一部を要求した場合もそうです。

さらに重要なことは、REST APIに対するすべてのGETリクエストは単一のリソースタイプしか返さないということです。それゆえ、複数のリソースタイプを集約したデータが必要なリクエストは、必要なデータを構成するために最低でもひとつ以上のリクエストを実行しなければなりません。データベースの世界のジョインを考えてください。純粋なRESTモデルでは、ジョインで返される各行を取得するには、各行毎にネットワーク越しにGETリクエストを実行しなければならないのです。このような複雑なやり方はすぐに性能を悪化させます。

Yogaはどのようなソリューションを提供するか

Yogaはオープンソースのツールキットであり、既存のRESTサーバの実装と統合でき、APIをカスタマイズできます。また、GETリクエストに、どのデータが欲しいのか正確に定義するためのセレクタを追加します。リレーショナルデータベースの世界の列の射影のようなものです。また、関連を表す式を定義して、異なるリソースタイプから複数のドキュメントを集約して単一の応答に変えることができます。

これは新しいコンセプトではありません。初期のバージョンのLinkedIn APIGoogleのGDataの仕様など、プロプライエタリな公開APIで使われていました。しかし、これらのAPIとは違い、Yogaは利用者のJava RESTアプリケーションのにシンタックスを追加します。

ここではこのYogaの使い方をデモし、RESTシンタックスの使いやすさを保持しながら、Yogaがアプリケーションの性能を改善することを示します。

リソースのフィールドを定義する

次は典型的なRESTリクエストで、リソースのインスタンスを検索しています。これはUserリソースタイプに関連するすべてのフィールドを返します。

GET /user/1.json

セキュリティや性能を考慮してあるユーザの名前とロケーションデータだけを取得するリクエストを生成したい場合はどうすればいいでしょう。この場合は、リクエストにセレクタを追加できます。

GET /user/1.json?selector=(id,name,city,state,country)

公開APIを公開するためには無制限にセレクトできる機能をエンドユーザに与えたくないかもしれません。これは、定義されたセレクタにエイリアスを付加することで達成できます。

GET /user/1.json?selector=$locationView

複数のリソースタイプを探索する

ドメインのモデルのオブジェクトグラフをナビゲートして、単一のAPI呼び出しで複数のクラスからデータを取得したい場合を考えます。

 

上のグラフは複数のエンティティを集約して単一の情報にするモバイルやJavascriptクライアントによって利用されるデータのエンティティモデルです。このようなAPIを呼び出すとユーザ、ユーザの友人、友人の好きなアーティスト、それらアーティストの歌やアルバムが返されます。

Concepts like UserやArtist、Songというような概念はRESTのリソースとは別です。したがって、標準なRESTの手法では、別々のネットワーク越しの呼び出しを行う必要があります。

GET /user/1.json (Get user)  

GET /user/2.json (Get detailed friend entities)     
GET /user/3.json     
...     
GET /artist/1.json (Get favorite artists)     
GET /artist/2.json     
...     
GET /album/1.json (Get albums for artists)     
GET /album/2.json     
...     
GET /song/1.json (Get songs for albums)     
GET /song/2.json     
...

この方法では明らかにスケールしません。オブジェクトグラフの深さを横断してしまうからです。ネットワークの遅延はすぐに性能のボトルネックになります。

セレクタを使えば、必要なデータを定義して、単一のリクエストで取得できます。開発時や信頼できるクライアント向けの場合、セレクタを明示できます。

GET /user/1.json?selector=friends(favoriteArtists(albums(songs)))

本運用環境では次のようにエイリアスを付加すればいいでしょう。

GET /user/1.json?selector=$friendsFavoriteMusic

Yogaを実装する

既存のアプリケーションに対してYogaを追加する場合、構成を変更する必要があります。YogaはSpring MVC REST、Jersey、RESTEasyと統合します。次の例ではSpring MVC RESTアプリケーションを使ってYogaを実装します。

このRESTアプリケーションはSpringのMappingJacksonJsonViewを使って応答をシリアライズします。

        <property name="defaultViews">
            <list>
                <bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView"/>
            list>
        property>

コントローラはパラメータ化されたURIを使ってGETリクエストをUserリソースを処理します。Springの@ResponseBodyアノテーションはMappingJacksonJsonViewの出力をレンダリングします。

@RequestMapping("/user/{id}")
    public @ResponseBody User get( @PathVariable long id )
    {
        return _userRepository.fetchUser( id );
    }

Userドキュメントのレンダリングの仕方を細かく制御する必要があるなら、RESTアプリケーションからYogaアプリケーションへ移行することもできます。まず、Mavenの依存物をインポートします。

        <dependency>
            <groupId>org.skyscreamergroupId>
            <artifactId>yoga-coreartifactId>
            <version>1.0.0version>
        dependency>
        <dependency>
            <groupId>org.skyscreamergroupId>
            <artifactId>yoga-springmvcartifactId>
            <version>1.0.0version>
        dependency>

次に、SpringのMappingJacksonJsonViewをYogaSpringViewに置き換えます。YogaSpringViewはセレクタのパースの仕方を知っています。

        <property name="defaultViews">
            <list>                 <bean class="org.skyscreamer.yoga.springmvc.view.YogaSpringView"p:yogaView-ref="jsonView"/>

            list>
        property>

jsonViewに注入される依存物はSpringMVCへ、YogaがJSONリクエストを処理しJSONの出力をレンダリングすることを伝えます。jsonViewはアプリケーションコンテキストに定義します。

    <bean name="jsonView" class="org.skyscreamer.yoga.view.JsonSelectorView"
          p:selectorParser-ref="selectorParser" />
    <bean id="selectorParser" class="org.skyscreamer.yoga.selector.parser.GDataSelectorParser"/>

次にGDataの仕様のシンタックスはセレクタで利用できます。YogaにはSelectorParserの代替実装である、LinkedInSelectorParserもあります。これは、LinkedIn APIのセレクタフォーマットを好む開発者向けです。

最後にUserControllerから@ResponseBodyアノテーションを除去します。@ResponseBodyアノテーションはMappingJacksonJsonViewに依存しています。MappingJacksonJsonViewはYogaSpringViewに置き換えられます。

@RequestMapping("/user/{id}")
    public User get( @PathVariable long id )
    {
        return_userRepository.fetchUser( id );
    }

この時点で、開発者はアプリケーションを立ち上げ、適切なセレクタをリソースリクエストに追加できます。

GET /user/1.json?selector=id,name

出力を描画できます。

{
    "name": "Carter Page",
    "id": 1  
}

開発者はセレクタにfavoriteArtistsを追加できます。

GET /user/1.json?selector=id,name,favoriteArtists(id,name)

そして、オブジェクトグラフをArtistリソースのビューインスタンスにナビゲートします。

{
    "id": 1,
    "name": "Carter Page",
    "favoriteArtists": [
          {
                "id": 1,
                "name": "Arcade Fire"           
          },
          { 
                "id": 3,
                "name": "Neutral Milk Hotel"           
          }
     ]
}

中核のフィールド

前の例で、Userのidフィールドとnameフィールドは必須でUserリソースに対するすべてのリクエストを返却されなければならないと仮定します。この2つのリソースは安くて、小さくて、不可欠なフィールドです。毎回この2つのフィールドを明示的に指定するために、方言を取得できるセレクタを作成します。

Yogaは@Coreアノテーションを提供します。このアノテーションはシリアライズしたドメインモデル(DTO)に利用でき、Yogaを使ったリクエストから必ず返却されるフィールドを示します。次の例はUserドメインオブジェクトのgetterにこのアノテーションを付加しています。

    @Core
    public long getId()
    {
        return _id;
    }
    @Core
    public String getName()
    {
        return _name;
    }

これで、セレクタで明示的にidとnameを指定する必要がなくなりました。

GET /user/1.json?selector=favoriteArtists(id,name)

id、nameそしてセレクタで指定されたものが返されます。

{
     "id": 1,
     "name": "Carter Page",
     "favoriteArtists": [
           {
                 "id": 1,
                 "name": "Arcade Fire"              
           },
           {
                 "id": 3,
                 "name": "Neutral Milk Hotel"            
           }
     ]
}

エイリアス

セレクタを段階的に作っていくと、素早い開発/デバッグサイクルが可能になります。しかし、APIを本運用環境へ配置する場合、外部のユーザが任意のセレクタを作って使うのを避けたいかもしれません。オブジェクトグラフの操作を無再現にやられてしまうと、すぐにセキュリティ上の問題が発生したり、性能が劣化したりする可能性があります。

Yogaを使うとセレクタのエイリアスを作成して、ユーザに定義されたエイリアスだけを使わせるようにできます。例えば、下記のセレクタを公開したいとします。

?selector=id,name,favoriteArtists(id,name)

まず、本運用環境の構成で明示的にセレクタを使えないようにします。ユーザが本運用環境でGData(またはLinkedIn)のシンタックスを使ってセレクタを構成できないようにするためです。

    <bean id="selectorParser" class="org.skyscreamer.yoga.selector.parser.GDataSelectorParser"
            p:disableExplicitSelectors="true"/>

次に、エイリアスを定義します。Yogaはエイリアスを定義するために複数の仕組みを提供します。このケースでは、プロパティファイルでエイリアスを定義します。

    <bean id="aliasSelectorResolver" class="org.skyscreamer.yoga.selector.parser.DynamicPropertyResolver"
          p:propertyFile="classpath:selectorAlias.properties"/>

このプロパティファイルでは、エイリアスをセットアップし、名前を付けます。Yogaのエイリアスは$で始まるのが規約です。

$userFavoriteArtists=id,name,favoriteArtists(id,name)

これで、運用環境でエイリアスが使えるようになりました、エイリアスの挙動はAPIドキュメントで確認できます。

GET /user/1.json?selector=$userFavoriteArtists

結論

多くの開発者にとって、RESTモデルは自分のアプリケーションのドメインにウェブAPIを提供するのに十分な方法です。ドキュメント応答に対してより細かく制御したいのならYogaを既存のRESTアプリケーションに統合すれば、リクエストにセレクタを追加することができます。

Skyscreamer SoftwareはYoga 1.0をリリースしました。YogaはSpring MVC REST、Jersey、RESTEasyと直接統合できます。ここでこの記事で紹介したデモの動画をみることができます。

著者について

Corby Page氏は20年、ソフトウエア開発に従事しています。ASP Methods社でJavaソリューションに従事し、Carter Page氏とSolomon Duskis氏と共にSkyscreamer Softwareというオープンソースイニシアティブを主催しています。

この記事に星をつける

おすすめ度
スタイル

BT