平田です。
前回のエントリではJBoss Toy Storeの概要について説明しました。今回はECサイト部分について掘り下げていきます。
ビジネスプロセス
まず、Drools Guvnorにインポートしたビジネスプロセスを見てみます。
アクティビティが六つ定義されています。
- Initialize
- スクリプトタスクです。Orderエンティティをファクトとして投入しています。
- Risk Assessment
- ビジネスルールタスクです。後述するRiskAssessmentルールを評価します。
- Review
- ユーザタスクです。利用者の注文を決裁します。
- VP Review
- ユーザタスクです。RiskAssessmentルールでhigh riskと判断された注文を決裁します。
- Emailと書いてありますが、プロセス変数をログ出力するダミータスクです。
- Shipping
- Shippingと書いてありますが、プロセs(ry
RiskAssessment ルール
前述のビジネスプロセスから呼ばれるデシジョンテーブルです。
注文合計金額が500ドル以上のとき、high riskと判断します。ここでhigh riskとなった注文は、後続のVP Reviewにかけられます。
ドメインモデル
続いてドメインモデル(業務データ)を見てみましょう。
jbwdemo-modelプロジェクトのmodelパッケージにドメインモデルが入っています。この図に無いクラスもありますが、注文に関係するものだけを抜き出しています。
jBPMとの統合
jBPM3.2ではProcessInstanceにkeyという業務キーを保持するフィールドが用意されており、これを用いてビジネスプロセスの実行データとドメインモデルを紐付けしていました。jBPM5ではこのようなフィールドは用意されていない為、自力で紐付けする必要があります。
JBoss Toy Storeでは、プロセス変数を用いてビジネスプロセスの実行データからドメインモデルへの紐付けを定義しています。また、ドメインモデル側(Order)でもビジネスプロセスのエンティティのPKを持っています。
個人的には、特定のミドルウェアに依存する要素をドメインモデルに持たせず、分離しておきたいです。
レイヤ構成
ECサイト部分の各レイヤごとに、どのようなコンポーネントから成るか、見ていきます。
プレゼンテーション層
jbwdemo-clientプロジェクト、jbwdemo-services-apiが、ECサイト部分のプレゼンテーション層になります。
プレゼンテーション層では、HTML5 + jQuery Mobileを使ってスマートフォン向けのUIを構築しています。Backbone.jsを使ってREST APIと通信する、画面遷移無しのステートフルなUIです。JavaScript MVCなどと呼ばれるアプローチですね。
- src/main/webapp/app/main.js: ブラウザ側のURLと、画面モジュールを対応付けしています。
- src/main/webapp/app/templates/*.html: lodash.jsのテンプレートエンジン機能を使ったUIテンプレートです。
- src/main/webapp/app/modules/*.js: Backbone.jsによるモデル・コレクションとコントローラです。
- jbwdemo-services-apiは、JAX-RSアノテーションを付与したインタフェースと、JAXBのJava Beanが定義されています。
Java EEのプレゼンテーション層と言えばJSF/faceletsですが、個人的にはJSFに良い印象を持っていない(複雑過ぎる、ヒープを喰い過ぎる、マークアップが独自で潰しが利かない)ので、このアプローチには非常に興味があります。
ビジネス層
jbwdemo-services、jbwdemo-processがビジネス層になります。
- src/main/resources/jbossworld.xml: カタログデータです。InitialiseData(..lizeだよなぁ)によってロードされます。
- ビジネスロジックはJava EE 6のSingleton Session Beanとして実装されています。
- jBPM5に関するロジックはjbwdemo-processのProcessControllerに集約されています。
インテグレーション層
jbwdemo-modelがインテグレーション層です。
- jbwdemo-modelにエンティティが定義されています。
- ModelUtilは、いわゆるDAOです。
イベントの舞台裏
ここからは、ECサイトの画面表示や利用者の操作といった各イベントごとに、裏側の処理を見ていきます。
- 入り口 – Registerボタン
- Registerボタンを押すと、/userにHTTP POSTします。
- UserResource#createUserが実行され、UpdateManagerが保持するユーザDB(ConcurrentHashMap)に追加します。
- 買い物カゴ – 注文ボタン
- 注文(Place Order)ボタンを押すと、/cart/{id}/checkoutにHTTP POSTします。
- ShoppingCartResource#checkoutからShoppingCartProcess#checkoutが呼ばれます。
- 注文データをRDBに保存します。
- ビジネスプロセスを開始します。
- 承認者用UI / 承認待ち注文一覧 – データのロード
- 承認待ち注文一覧を開くと、/order/openをHTTP GETします。
- OrderResource#getOpenOrdersからOrderProcess#getOpenOrdersが呼ばれます。
- jbwdemo-processのTaskorm.xmlに定義されたTaskIdsAssignedAsPotentialOwnerByStatusByGroupクエリ(長い…)を実行し、Review待ちのjBPMタスクを取得します。
- 承認者用UI / 承認待ち注文 – 承認ボタン / 否認ボタン
- 承認(Approve)を押すと、/order/{orderId}/approveにHTTP PUTします。
- OrderResource#approveOrderからOrderProcess#approveOrderが呼ばれます。
- Orderエンティティの承認フラグを立て、保存します。
- プロセス変数 approved=trueをセットし、jBPMタスクを完了させます。
- 否認(Reject)を押すと、/order/{orderId}/rejectにHTTP PUTし、以降は承認と同じです。
まとめ
jBPM5を除けばTicketMonsterと同じ構成です。jBPM5についてはjbwdemo-processプロジェクトや、その中のProcessControllerクラスが参考になります。
軒並みSingleton Session Beanとして実装してあるのですが、きちんとスレッドセーフに動作するのか気になるところです。
次回はGWTを使った販売実績ダッシュボードを見ていきます。