BFFでモックAPIを作ろう
UIに表示させるデータはどうすればよいでしょうか。バックエンドを全て取りそろえてからフロントエンドを開発する方法だと、プロトタイピングで求められるスピードに到達できないでしょう。
これを解決する手法の1つがモックAPIです。モック(mock)は「模造的な」という意味で、モックAPIはRDBMS等のバックエンドのサービスは呼び出さずに、HTTPリクエストに対して固定的なダミーのJSONを送受信します。重要な部分以外はモックAPIを使って、スピーディーにフロントエンドを形にして価値を評価しましょう。
K5 PlaygroundでモックAPIを作成する
K5 PlaygroundでモックAPIを作成するのは簡単です。既存のAPIロジックと空のAPIロジック(Empty Logic)を交換して、モックデータを書くだけです。具体的な手順を見てみましょう。
- まず中央にあるAPIロジックを、×をクリックして削除します。
- 次に右メニューのCustomにある「Empty Logic」を中央にドラッグ&ドロップします。
-
最後にEmpty Logicの
next()
メソッドの引数にモックオブジェクトを渡します。
ここでは静的なオブジェクトを next()
に渡しただけですが、条件に応じて動的にオブジェクトを生成すると、より高度なモックAPIが作れます。HTTPリクエストの内容に応じてレスポンスを分岐させたい場合は、HTTPリクエストのオブジェクトreq
を参照します。例えばURLのクエリ文字列ならば、 req.query.KEY_NAME
で参照できます。HTTPリクエストオブジェクト req
の詳細は、K5 PlaygroundのBFFでも利用しているNode.jsのWebアプリケーションフレームワークexpressのドキュメントを参照してください。
APIの設計を向上させるためのモックAPI
モックAPIのもう1つの利点は、動作する設計であることです。
API単体ではよさそうな設計であっても、フロントエンドの設計を考えるとの設計を考えると「1ページあたりのトランザクション数が多い」「レスポンスのJSONの構造や型が扱いづらい」といった不満が出てくることは珍しくありません。早期にモックAPIを作成することで、フロントエンドの動作を考慮して改善点を洗い出し、APIの設計を修正できます。モックAPIの段階でさまざまなAPI利用者の声を聞いて質を高めることが大切です。
またBFFがないアーキテクチャの場合、技術的にはモックAPIそのものを容易に修正できても、本物のバックエンドは組織やコストの面から修正が困難なケースが多々あります。K5 PlaygroundにはBFFがあるため、バックエンドと距離を置いてフロントエンドと親和性の高いAPIをBFFで実現できます。
FluxでモックAction CreatorやモックStoreを作成しよう(1)
開発のスピードアップをさらに追求したい場合、データの生成点をモックAPIではなく、フロントエンドの内部に移動させることもできます。APIを呼び出した後にダミーデータを生成するのではなく、APIを呼び出さずにフロントエンド内部でダミーのデータを生成するアプローチです。
一般的にSingle Page Application(SPA)では、バックエンドから取得したデータなど、フロントエンドの「状態(state)」を何らかの方法で管理して、UIに表示させます。ReactとFluxの場合は、「Store」というFluxのコンポーネントがこのstateを管理する役割を担います。stateをさまざまな方法でモック化し、フロントエンド内でダミーデータを生成することでアプリを高速に開発できます。
まずは、state管理にとって重要なFluxアーキテクチャを見ていきましょう。
Fluxアーキテクチャ概説
FluxはFacebookが提唱したフロントエンドのためのアーキテクチャです。フロントエンドの「状態」の管理に重きを置いています。Reactと親和性が高いだけでなく、JavaScrip以外の言語やフレームワークのフロントエンドに適用できるアーキテクチャです。
FluxアーキテクチャはView、Action/Action Creator、Dispatcher、Storeから構成されます。
Fluxの 構成要素 |
概要 |
K5 Playgroundでの ファイル格納場所 |
---|---|---|
View | React ComponentやContainer |
frontend/app/components |
ActionCreator | イベントを処理して結果をActionという形式にしてDispatcherに渡すメソッド |
frontend/app/actions |
Action | イベントの処理結果(APIレスポンスなど) |
frontend/app/actions |
Dispatcher | ActionをStoreに送信するハブ機能 |
frontend/app/dispatcher |
Store | 状態を管理する非永続的なデータストア |
frontend/app/store |
FluxアーキテクチャをFacebookのFluxライブラリを用いたサンプルコードを通して説明します。
まずはViewであるReact Componentを起点とします。ユーザーがReact Componentに対してマウスクリックなどのイベントを発生させるとイベントハンドラが呼ばれます。ButtonというReact Componentを利用する例です。クリックされると同じReact Component内に定義された handleClick
メソッドが呼ばれます。
<Button onClick={this.handleClick} title="Update"/>
イベントハンドラは、Action Creatorを呼び出します。
handleClick = () => ActionCreator.updateOrder();
Action Creatorの基本的な書式です。イベントに対する処理を行い、結果をActionというオブジェクトにして、Dispatcherの dispatch()
メソッドに渡します。イベントに対する処理の例は、API呼び出しやDB呼び出し、業務ロジックの実行などバックエンドが関わるものからフロントエンド内部に閉じたものまで多岐に渡ります。 dispatch()
に渡されたActionはその後、Storeに送信されます。
//複数のAction Creatorの集合 const ActionCreators = { //Action Creator updateOrder: () => { //1. APIを呼び出す api.get(url).then(body => { //2. APIのレスポンスからActionを生成する const action = { type: 'order/update' //Acitonの種別を表す識別子 data: body //イベントの結果 } //3. Dispatcher経由でStoreにActionを送信する Dispatcher.dispatch(action); }) }, }
上記は説明的に記述しましたが、より簡潔にも記述できます。
updateOrder: () => api.get(url).then(body => Dispatcher.dispatch({type: 'order/update', data: body}))
StoreではActionCreatorがDispatcherに渡したActionを受け取り、新たな状態(state)を作り出します。StoreはgetInitialState()
と reduce() の2種類のメソッドを持ちます。 getInitialState()
の戻り値にはStoreの初期値を設定します。 reduce()
では、現在の state
と受け取った action
から新たな state
を作成します。
import { ReduceStore } from 'flux/utils'; //ReduceStoreを使うのが基本です。 import AppDispatcher from '../dispatcher/AppDispatcher'; class OrderStore extends ReduceStore { //Storeの初期のstateを返す必須のメソッドです。 getInitialState() { return {}; //初期のstateを定義します。 } //現在のstateとactionを受け取って新たなstateを返す必須のメソッドです。 reduce(state, action) { switch (action.type) { // actionのtypeによってStoreのstateの作り方を分岐させます。 case 'order/update': { //注文更新の例 return action.data; // 受け取ったactionをそのまま新たなstateとする例です。 } case 'order/delete': { //注文削除の例 return {}; // 空のオブジェクトを新たなstateをとする例です。 } default: { return state; } } } } export default new OrderStore(AppDispatcher);
Storeの内容が更新されると、それを自動的にContainerが受け取ります。Containerは次のとおり、Storeからデータを受け取るだけの特殊なReact Componentです。ContainerがStoreの内容を受け取ったら、配下のReact Componentに渡します。
import React, { Component } from 'react'; import { render } from 'react-dom'; import { Container } from 'flux/utils'; //サンプルの(Reduce)Storeです。 import OrderStore from './stores/OrderStore'; import RecommendationStore from './stores/RecommendationStore'; class OrderContainer extends Component { static getStores() { return [OrderStore, RecommendStore]; //stateを取得したいReduceStoreを定義します。 } static calculateState() { return { //取得したstateはここで定義した形式で参照できます。 order: OrderStore.getState(), recommendation: RecommendationStore.getState(), }; } render() { //Storeから取得したstateを配下のReact Componentに渡します。 <OrderPage order={this.state.order} recommendation={this.state.recommendation} /> } } export default Container.create(OrderContainer);
Viewからスタートして、ActionCreator、Dispatcher、Storeを経由して再びViewに戻ってきました。本ページ冒頭の図のように、データを一方通行で流して単純化するのが特徴です。
次のページから、Fluxアーキテクチャでモックを実現する方法を2つ紹介します。