uehaj's blog

Grな日々 - GroovyとかGrailsとかElmとかRustとかHaskellとかReactとかFregeとかJavaとか -

Grails React Scaffoldを目指すざます

これはG*Advent callender 2016の記事です。 前日は@Ziphilさんの記事でした。明日は@ hikariruさんの記事です。

去年のG*Advent Calendarでは、本ブログではReact Meets Grails 〜ReactはエンタープライズSPAの夢を見るか?〜 - uehaj's blogと題して、バックエンドを「GrailsのRestサーバ」+「React SPAによるフロントエンド」という組合せをボイラープレート(雛形)として実装してみました。

今年もこの路線を拡充してみます。

背景

GrailsにはScaffold機能、すなわちCRUD操作を行なうためのWeb画面の自動生成機能があり、確かに便利なものではあるのですが、以下の点では不満がありました。

ページナビゲーション・ルーティング機能不足
業務Webアプリ開発で必要になることが多い階層メニュー選択やページナビゲーション機能がそのままではサポートされません。 React Routerのように画面構築と連動したルーティング機能が利用できません。
ページ遷移ベースであること
データの各操作1つ1つ(テーブルのレコードを参照、編集、削除...)などにすべてページ遷移が伴ないます。
2-way編集の困難さ
Scaffoldで生成されたGSPやコントローラを何らかの理由で手で修正してしまうと、ドメインクラスを修正したときの再生成に問題がおきたり、もしくは手動での取り込みが必要になります。かといって動的Scaffold、Fieldsプラグインなどでカスタマイズしようとすると後述のカスタマイズの困難さに直面します。
カスタマイズの困難さ
Scaffoldの元になる「テンプレート」や部品のレンダリングテンプレートを修正することができますが、GSPはもともとHTMLを生成するテンプレートのようなものなので、「HTMLを生成するテンプレート(GSP)を生成するテンプレート」を書く必要があり、さらに分離されたJavaScriptのコードは生成の埒外なので、それとの連携を考えると3段階を考慮したプログラミングとなり、難易度が高く保守性の低いものとなります。結果的に、たとえば「顧客ごとにカスタマイズした別ビュー」を提供するなどが難しくなります。
JavaScriptと連携したときの再利用の困難
GSPにある「ページフラグメント」「タグライブラリ」などは単純なHTML断片を共通化するのには有効なのですが、動的なDOM構築が必要になったときに破綻します。JSを組合せたとき、画面部品の再利用や、保守性の高い構造を達成し保守するのが困難です。(サーバでのHTML生成がかかえる本質的な困難)
jQueryの問題
そもそもjQueryベースだと保守性を高くすることが難しい
GUIの自動試験のしにくさ
RESTfuul APIならコントローラも試験しやすくなる。またクライアントサイドのJSで利用できるReact Storybookなどの便利な画面部品試験ツールが利用できない。
クライアント・サーバ間の密結合
ビューだけの修正時にもアプリのwarパッケージの生成・再デプロイの必要がある。その結果、ライフサイクルが不一致な場合(たとえばiOSのビューを修正するときにAndroid版も止めなければならない)などの問題がある。
Webアプリとネイティブアプリ(モバイル+デスクトップ)間でのコード再利用・共有が困難
React NativeIonic2のような仕組で共有することはできない。

これらのほとんどは、GrailsのScaffoldの問題であると同時に、一般にページ遷移ベースそのもの、そしてアプリに自動生成を適用することの問題でもあります(つまりRailsなどでも同様)。結果として、Scaffoldの実システム開発での利用は管理画面(admin ui)などに留まり、コンテキスト依存サンプルコードとして有用ではあるものの、限定的でした。

解決策

前述の問題は一般的であるが故に、各所でSPAフレームワークやツール等が解決しようと努力なされてきました。その混沌たる候補の中から、ここ数年で突破口が見えてきた気がします。すなわち

  • サーバはRESTサーバなどのWeb APIサーバとする。
  • 画面はSPAフレームワークを使いJSで作り込む

上記により多くが解決・改善されます。このことを踏まえて、GrailsのRESTfulサービス開発機能を併用し、開発のベースとなるようなReactとの連携コードサンプルを提供するのが今回記事で紹介している「React Grails Boilerplate」の目的です*1

上記のような課題を克服することで、実世界アプリ開発を楽にできるようにすることを目指しています(将来的には!)。

作ったもの

以下画面です。

f:id:uehaj:20161222062739g:plain f:id:uehaj:20161223000101g:plain

ソースはこちら。

実行方法

% git clone  https://github.com/uehaj/grails3-react-boilerplate.git
% cd grails3-react-boilerplate
% ./gradlew bootRun

bootRunではサーバとクライアントは別プロセスになっていて、並列に動作させています。個別に起動する場合は以下のとおり。

% cd server
% ./grailsw run-app &
% cd ../client
% npm start

去年からの進展

主には以下のとおりです。

特徴

売りとしては、JSON-schemaをGORMドメインクラスから自動生成することで、以下を可能としていることです。

  • json-schemaを扱えるフォームライブラリreact-forms-jsonschemaにより、入力フォームの自動生成
  • GORMのドメインクラスの制約(constraints)をクライアントサイドの入力フォーム自動バリデーションに自動変換
  • ドメインクラスやフィールドの変更追加に対して自動追随

つまりどういうこと?

DBのフィールド増減の際に何箇所も直してまわったり、バリデーションに関しての同じ処理をサーバとクライアントの二箇所で別言語で書いたり*2、整合性を失なうようなことを避けることができます。 もっともこれは従来のScaffoldでも一部できていたことで、JSON-Schemaで汎用的に簡単にできたよ、ということです。

加えて、ドメインクラスの一覧をページ表示時に動的に取得し、対応するメニュー項目、react routerのRoute階層などを自動生成します。

JSONSchemaとは

JSONデータ用のデータスキーマです。バリデーション情報を含んでいます。これにFORMなどの見た目情報を追加する、uiSchemaというものも定義されています。

{
    "title": "Example Schema",
    "type": "object",
    "properties": {
        "firstName": {
            "type": "string"
        },
        "lastName": {
            "type": "string"
        },
        "age": {
            "description": "Age in years",
            "type": "integer",
            "minimum": 0
        }
    },
    "required": ["firstName", "lastName"]
}

このスキーマをフォームとして表示、入力、バリデーションができるReactのライブラリが ract-forms-jsonschemaです。

今後やりたいこと

  • grails application profile化
  • 関連の実装
  • 国際化
  • pageneteをきちんと実装してレコード数の制約を解除(今はクライアントサイドに一気読み込みのなんちゃってページネーション)
  • webpackのhot module replacement対応
  • grails constraintsに対応付かないJSONSchema、および uiSchemaをドメインクラス側で明示的に指定するための仕組みの追加。カスタムconstraintsを追加する、生のJSONScehma/uiSchemaを返すためのメソッドをサポートするなど。

まとめ

私がReactに注目しはじめたのは2014年頃とやや遅かったのですが、かれこれ2年たち、Reactは順調に発展・普及し、成熟してきました。今後もますます業務開発で採用される機会も増えてきた/来るのではないでしょうか。 Reactは本来的にはプログラマにとってとっつき易いものですが、それなりの構造を作るのはやはり難しいので、このボイラープレートがサーバサイドプログラマがフロント開発をするための役などにたてば嬉しいです。

ではみなさん、メリークリスマスイブイブ!

入門 React ―コンポーネントベースのWebフロントエンド開発
Frankie Bagnardi Jonathan Beebe Richard Feldman Tom Hallett Simon HØjberg Karl Mikkelsen
オライリージャパン
売り上げランキング: 46,989

*1:ちなみにAngular scaffold profileは既にあります

*2:もっとも、Grailsで言うvalidate:クロージャをクライアントで実行する、などはできない。