1. はじめに
あなたは、フローに似たプロセスを実装する必要があるとします。そして、できれば組み込みで、それが設定可能であり、管理や保守に対して拡張性があり、容易であるべきです。さて、あなたが探している単純なフローのオーケストレーションに対して、重厚に思われる抽象化への道のりを進んでいるフルスケールのBPMエンジンを必要としますか?あるいは、フルスケールのBPMエンジンに委ねることなく利用することのできる軽量な代替手段を必要としますか?本稿では、アスペクト指向プログラミング(AOP)の技術を使用して、高度に設定可能で拡張性があり、さらに軽量な組み込みプロセスフローを構築し、オーケストレートするための方法を説明します。今回のサンプルでは、Spring AOPとAspect Jをベースにしていますが、他のAOPでも同様の結果を得ることができます。
2. 問題
本題に入る前に、始めに実際の問題についてさらなる理解を得る必要があります。その後で、利用可能なパターン、ツール、技術のセットに対する私たちの理解を合わせ、私たちにフィットしたものを見つけることができるかどうかを確かめます。私たちの問題はプロセスそのものであるので、それに関するさらなる理解を得ることにしましょう。プロセスとは何でしょうか?プロセスは、定められたゴールの達成に至るための協調されたアクティビティの集まりです。アクティビティは、ある実行命令の一つであり、プロセスを構築するブロックです。それぞれのアクティビティは、そのプロセスのゴール全体の一部を満たすための共有データ(コンテキスト)の一部で動作します。達成されたプロセスのゴールの一部は、残りのアクティビティの実行を協調するのに利用される事象を得ます。これは、そのようなプロセスを定義しているアクティビティの実行を協調するための事象のセットで動作する規則のパターンでしかないとして、基本的にプロセスを再定義します。アクティビティの実行を協調するためのプロセスとして、以下の特性について知っている必要があります。 :
- アクティビティ - プロセスを定義するアクティビティ
- 共有データ/コンテキスト - アクティビティによって達成されたデータや事象を共有するためのメカニズムを定義します
- トランジッションルール - 登録された事象をベースとし、前のアクティビティの完了後に次のアクティビティを定義します
- 実行の決定 - トランジッションルールを実行するためのメカニズムを定義します
- 初期データ/コンテキスト (オプション) - プロセスによて操作される共有データの初期状態
下記のダイアグラムは、プロセスの高レベルな構造を示しています。:
ここで、私たちは以下の一連の要求の中でプロセスを形式化することができます。:
- アクティビティの集まりとしてプロセスを組み立てるためのメカニズムを定義します
- 個別のアクティビティを定義します
- 共有データに対するプレースホルダーを定義します
- そのプロセスの範囲内で、それらのアクティビティの実行を協調するメカニズムを定義します
- トランジッションルールと実行の決定メカニズムを定義し、それは、アクティビティによって登録された事象をベースとしたトランジッションルールを強制します
3. アーキテクチャとデザイン
私たちは、先の4つの要求を解決することによってアーキテクチャの定義を始めようと思います。:
- アクティビティの集まりとしてプロセスを組み立てるためのメカニズムを定義します
- 個別のアクティビティを定義します
- 共有データに対するプレースホルダーを定義します
- そのプロセスの範囲内で、それらのアクティビティの実行を協調するメカニズムを定義します
アクティビティはステートレスなワーカーで、いくつかのデータ(コンテキスト)を含んでいるトークンを受け取ります。このアクティビティによって定義されたビジネスロジックを実行している間、共有データからの読み込みや共有データへの書き込みにより、その共有データのトークンで動作します。共有データのトークンは、プロセスの実行状態を定義します。
私たちがはじめに設定した軽量な原則を忠実に、Plain Old Java Interfaces (POJI)を実装するPlain Old Java Objects (POJO)として、私たちのアクティビティを定義することに異論はないでしょう。
以下は、Activityインターフェースの定義です。単一のprocess(Object obj)メソッドがあり、入力パラメータは共有データ(コンテキスト)のプレースホルダーです。:
public interface Activity { public void process (Object data); } |
共有データのためのプレースホールダーは、構造化されたものか非構造化された(つまり、Map)オブジェクトです。それは、完全にあなた次第です。今回は、簡単にするためにActivity インターフェースにjava.lang.Objectを定義します。しかし、実際の環境では、そのプロセスに参加するすべてが知っている構造を示したある構造化オブジェクトの型としてあらわされることでしょう。
プロセス
プロセスがアクティビティの集まりであることから、それらの集まりを組み立て、実行するためのメカニズムを理解する必要があります。
それを達成するための方法はたくさんあります。それらの一つは、ある順序づけられたコレクションの型にすべてのアクティビティを閉じこめ、事前定義された順序の中でそれぞれのアクティビティを繰り返し呼び出すことです。このアプローチの設定可能性と拡張性は、プロセスの制御と実行のすべての側面がハードコードされるという事実から、確実に劣っているでしょう。
私たちは、多少型破りや方法でそのプロセスを見ることができ、それは、プロセスが具体的な実装の無い、ビヘイビアレス(振る舞いのない)な抽象概念であると言えます。しかしながら、この一連のアクティビティによる抽象概念をフィルターすることで、そのプロセスの特性、状態、振る舞いを定義します。
さて、process(..)メソッドが定義されているGenericProcessクラスがあると仮定します。:
public class GenericProcess { public void process(Object obj){ System.out.println("executing process"); } } |
process(..)
メソッドは役に立たず、コンテキストの状態が変化しないので、私たちがインプットとなるオブジェクトを渡してprocess(..)
メソッドを直接呼び出しても、ほぼ何も起きないでしょう。しかし、私たちがprocess(..)
メソッドを呼び出す前に、何らかの方法でアクティビティを導入するための方法を見つけ、インプットのオブジェクトを変更するアクティビティがあるときは、process(..)
メソッドは不変のままにも関わらず、インプットのオブジェクトがアクティビティによって事前に処理されているので、プロセスの内容全体の結果は変化するでしょう。
対象のリソースにIntercepting Filterを適用する技術は、Intercepting Filterパターンとして十分に裏付けられているもので、今日のエンタープライズアプリケーションで広く利用されています。典型的な例としてサーブレットフィルターがあります。
Intercepting Filterパターンは、リクエストの受信やレスポンスの転送をインターセプトするフィルターで、既存のアプリケーションリソースをラップします。intercepting filterは、アプリケーションリクエストの事前処理やリダイレクトをおこなったり、アプリケーションレスポンスの内容の事後処理や置換をおこなうことができます。さらに、intercepting filterは、ソースコードを変更することなく既存のリソースに対し、一連の分離した、宣言的でデプロイ可能なサービスを加えるために、順に重ねて積み上げることも可能です。- http://java.sun.com/blueprints/patterns/InterceptingFilter.html (リンク)
歴史的に、このアーキテクチャはセキュリティやトランザクションなどといった非機能的な関心事を解決するために利用されてきました。しかし、あなたがはっきりと理解できることは、個々のアクティビティを表わす一連のintercepting filterからプロセスに似た構造を組み立てることで、同じアプローチがアプリケーションの機能的な特性の解決に対して容易に適用することができるということです。
下図は、プロセスへの呼び出しがフィルターチェーンによってインターセプトされる方法を示しています。そして、それらはそれぞれのフィルターがそのプロセスに関する個々のアクティビティに関連していて、何もすることが無い状態で実際の対象プロセスコンポーネントをおき、それを空っぽの再利用可能な対象オブジェクトとしています。フィルターチェーンを切り替えることで、あなたは異なるプロセスを持つことができます。
おこなうべきただ一つのことは、エレガントな方法でこういったものの組み立てを支援してくれる外部フレームワークが存在するかを探すことです。
プロキシベースのSpring AOPは、単純な構造と最も重要な実行メカニズムを提供していることから、うってつけのものとなりそうです。それにより、与えられたプロセスのアクティビティを表すintercepting filterの順序づけられた集まりを定義することができます。入ってくるプロセスのリクエストは、Activity Filterで実行される振る舞いで処理を形成しているこれらのフィルターによって代理されます。
これで、残りの要求は一つだけとなります。:
- トランジッションルールと実行の決定メカニズムを定義し、それは、アクティビティによって登録された事象をベースとしたトランジッションルールを強制します。
プロキシベースのフィルタの美しさは、トランジッションメカニズムそのものです。対象オブジェクト(プロセス)を呼び出す度に、Intercepting filterがプロキシメカニズムによって次々と呼ばれます。これは制限のない自由なもので、一つ一つのアクティビティが常に呼び出されなければならない状況下で変わることなく機能します。しかし、実際にはいつもそうであるとは限りません。私たちがあげた先の問題提起の説明の一つは、「達成されたプロセスのゴールの一部は、残りのアクティビティの実行を協調するのに利用される達成された事象を示している。」です。それは、一つのアクティビティの完了が、必ずしももう一つのアクティビティへの遷移を許可する必要があるわけではないということを意味しています。実世界のプロセスでは、トランジッションは前のアクティビティにより達成された/されなかった事象に正確に基づいているべきです。これらの事象は、共有データのプレースフォルダーと共に登録されなければならないので、それらを問い合わせて取り出すことができます。
それを達成することは、intercepting filter内でIF文を入れるのと同じくらい単純です。 :
public Object invoke(MethodInvocation invocation){ if (fact(s) exists){ invoke activity } } |
しかし、それはいくつかの問題を生じさせます。私たちがそれに入る前に、一つのことをはっきりとさせましょう。現在の仕組みでは、それぞれのintercepting filterは、対応するPOJOアクティビティと密結合であり、それは当然のことです。私たちは、intercepting filterそれ自身の中に全てのアクティビティロジックを保持することは容易にできます。そうしないようにするための唯一のことは、POJOとして私たちのアクティビティを保持したいと願うことです。それは、単純にintercepting filterのコードをアクティビティのコールバックに委譲することを意味しています。
これは、私たちがアクティビティの中にトランジッションの評価ロジックを置く場合、私たちは本質的に二つの関心事(アクティビティのトランジッションロジックとアクティビティのビジネスロジック)を結合することができるということを意味しています。それは関心事の分離という基本的なアーキテクチャの原則に違反し、関心事/コードの結合をもたらします。別の問題として、全てのintercepting filterにわたって同じトランジッションロジックの繰り返しが発生します。私たちはそれを関心事/コードの分散と呼んでいます。トランジッションロジックは、全てのintercepting filterをクロスカットします。そして、あなたがAOPを推測したように、技術の選択として、再度、脳裏に浮かびます。
私たちにとって必要なことは、対象となる実際のフィルタクラスのメソッドへの呼び出しをインターセプトすることができるようにするために、around adviceを書くことです。そして、インプットを評価し、実行する対象メソッドを許可するかしないのかのどちらかによって、トランジッションの判断をします。唯一の警告として、私たちの対象としているクラスそれ自身がたまたまintercepting filterとなることです。したがって、基本的にはインターセプターをインターセプトするようにしてください。残念ながら、Spring AOPがプロキシベースであるという理由から、それをサポートすることはできません。私たちのintercepting filterは、既にプロキシの基盤の一部となっているので、私たちは単純にプロキシをプロキシとすることができないのです。
しかし、AOPで最高の特徴の一つは、それがいくつかの異なる趣向や実装で提供されているということです(つまり、プロキシベースやバイトコードのウィービングなど)。そして、私たちが別のプロキシをプロキシとするために、プロキシベースのAOPを使用することができなくとも、何があってもバイトコードのウィービングを使用し、それらにトランジッションの評価ロジックをウィービングする(コンパイル時、あるいはロード時)ことにより、個々のプロキシのintercepting filterを扱うでしょう。それ故、トランジッションとビジネスロジックの分離が保たれるのです。これは、AspectJのようなフレームワークを使用することで容易に達成することができます。そうすることで、私たちは自身のアーキテクチャの中に第二のAOP層を導入します。それは非常に興味深い関係を持つでしょう。AspectJは、アクティビティのナビゲーションやトランジッションといった非機能的な関心事に対処するために利用されているので、私たちは、アクティビティでプロセスを処理するといった機能的な関心事に対処するためにSpring AOPを使用します。
下記の図は二つのAOP層で示されたプロセスフローの最終的な構造を表しています。機能的なAOP層は、順序づけられた一連のintercepting filterからプロセスを組み立てることに責任を持ちます。一方、非機能的なAOP層は、トランジッションの統治の問題に対処します。
ここで、このアーキテクチャを示すために、私たちはサンプルのユースケースを実装したいと思います。それは、単純なプロセスフローを定義する購入品目です。
4. ユースケース(購入品目)
あなたがオンラインショッピングをしていると想像してください。あなたは品目を選び、それをショッピングカートへ入れ、確認画面へ進みます。そして、あなたのクレジットカード情報を入力し、最後に、購入品リクエストを送信します。システムは購入品目プロセスを起動します。
前提条件
プロセスは、品目、請求書、出荷情報を含むデータを受信しなければなりません。
メインフロー
1. 品目の有効性を確認する
2. クレジットの認可を取得する
3. 出荷の処理をする
このプロセスは、下図で示されるように、現時点で3つのアクティビティを定義しています。:
この図では、統治されていないアクティビティのトランジッションも示しています。しかし、実際、品目が利用できない場合に何が起こるのでしょうか?「クレジットの認可を取得する」のアクティビティ実行でしょうか?それとも、「出荷の処理をする」が次に実行されるのでしょうか?
もう一つの興味深い警告として、クレジットの認可は自動で処理することができない(認可ネットワークはダウンしている)という条件を前提としています。そして、あなたか顧客サービスの代表者は、認可番号を得るためにクレジット会社に直接電話する必要があります。一旦、その認可番号を取得して、システムに入力したら、どのポイントでこのプロセスが再開、あるいは継続するのでしょうか?開始、あるいは直接出荷に進むのでしょうか?私は出荷に進むと言いますが、それはどのようにすれば良いのでしょう?私たちが多くの実行制御を維持、管理していない状態で途中からプロセスを再開するにはどうしたら良いのでしょうか?
興味深いことに、AOPを用いることで、私たちは実行制御の維持やプロセスの指示の維持をおこなう必要がないということです。それは、一連のintercepting filterアクティビティを通したリクエストをプロキシするので、それはフレームワーク自身によっておこなわれます。私たちが必要なことのすべては、登録された事象に基づいた実行のために、個々のフィルターを許可するかどうかのメカニズムを用意することです。
「品目の有効性を確認する」では、品目が有効であるという事象を登録します。その事象は、「クレジットの認可を取得する」のために事前に必要なものとして機能する必要があります。同様に、クレジットが認可の事象を登録すると、その事象は、「出荷の処理をする」のアクティビティのために事前に必要なものとして機能する必要があります。ある事象が存在するかどうかは、ある特定のアクティビティを実行しないときの判断として利用されることもあるでしょう。さて、「手動でのクレジット照合」のシナリオに戻り、途中からプロセスを再開する方法について考えます。つまり、次のより良い疑問についてです。プロセスの途中で、既に実行されたアクティビティを繰り返すことなく、そのプロセスを再開するにはどうしたら良いのか?
共有データのトークン(コンテキスト)が、プロセスの状態も表していたことを思い出してください。その状態は、プロセスと共に登録されたすべての事象を含んでいます。これらの事象は、トランジッションの判断をするために評価されます。したがって、私たちの「手動でのクレジット照合」のシナリオでは、私たちがまさに最初から全体としてプロセスを再送信した場合の、トランジッションメカニズムはこうです。まず、最初のアクティビティである「品目の有効性を確認する」に直面します。そこで、品目の有効性の事象は既に登録済で、アクティビティを繰り返す必要はないことをすぐに知ります。そして、次のアクティビティ「クレジットの認可」へとスキップします。クレジットの認可の事象も(何らかの手入力によって)登録されているので、次のアクティビティである「出荷の処理をする」へと再度スキップします。そして、そのアクティビティだけがプロセスを実行し完了することができます。
私たちが実際の例へ移る前に、まだ議論が必要なもう一つの重要な話題があります。それはアクティビティが定義される順序です。
それについては最初に議論すべきと思うかもしれませんが、アクティビティの順序はこれらのアクティビティによって定義されるプロセスのトランジッションの決定において、何の役割も演じてないのです。
プロセスのアクティビティの順序は、プロセスそのものの均衡を意味するだけです。 - t事象の見込みや可能性に基づいた戦略は、その存在によって、その実効を遂行するのか除外するのかに対して次のアクティビティのための理想的な環境を作ります。アクティビティの順序を変更することで、プロセス全体へ影響を及ぼしてはいけません。
例::
凡例:
d - 依存
p - 生成
プロセス:
ProcessContext = A(d-1, 2; p-3) -> B (d-1, 3; p-4, 5) -> C(d-4, 5);
上記の公式に従ってプロセスが所定のProcessContextの中で始まるとき、最初のアクティビティはAです。そして、それが呼び出される前に、それが存在するために事象1と2に依存しています。事実1と2がProcessContextの中に存在すると仮定すると、アクティビティAは実行され、事実3を生じます。次のアクティビティは、事象1と3に依存するBです。私たちのプロセスを知ることで、私たちはアクティビティAの前に起こっている事象3の見込みと可能性がとても低いと判断しました。しかし、アクティビティAが呼ばれた後で存在する事象3の見込みと可能性は非常に高く、それゆえに、Aに続くアクティビティBという順序となります。
しかし、私たちがアクティビティBとAの順序を変えたとしたら、何が変わるでしょうか?
ProcessContext = B (d-1, 3; p-4, 5) -> A(d-1, 2; p-3) -> C(d-4, 5);
ほとんど変わりません。プロセスが呼び出されるとき、事象のレジストリを維持するProcessContextは、アクティビティBの呼び出しを許可するために登録されるべき事象が足りていないということをすぐに判断します。そして、次のアクティビティのAもスキップされるでしょう。また、事象1と2が存在すると仮定すると、アクティビティAの呼び出しを許可するために登録されるべき十分な事象が存在するので、その事象の評価を判断することができます。アクティビティBを生成するための必要条件を失ったことで、同様に、アクティビティCもスキップされます。プロセスが同じProcessContextで再送信された場合、先のプロセス呼び出しのアクティビティAは、事前条件を満足させるためのアクティビティBによって要求された事象を登録したので、アクティビティBが呼ばれます。ProcesContextがアクティビティAがその作業を完了したことを知っているので、アクティビティAはスキップされます。同様に、アクティビティBは、アクティビティCの事前条件を満足させるのに十分な事象を登録したので、アクティビティCも呼ばれます。
さて、アクティビティの変更がプロセスの振る舞いに変更を与えないことを見てきましたが、それはプロセス自動化の特性に影響を与える可能性があります。
5. 例
この例は、以下のアーティファクトからなります。:
Generic Processの実行。これは、以下に見るように意味のあるコードを含んでいません。そして、実際に意味のあるコードを含めてはいけません。このクラスの唯一の目的は、個々のアクティビティを表すintercepting filterチェーンを適用するための対象クラスとして機能することです。
対応するSpring定義は、以下の通りです。:
購入品目プロセスの残りの構成は、3つの部分から成ります。:
パート 1 (14行目) - GenericProcessImpl.execute(..)メソッドをJoin Pointとして定義しているポイントカットから構成される組み立てのAOP設定を処理します。私たちがどのbeanをインターセプトしているのかを知るために、bean(purchseItem)ポイントカットのexpressionを使用していることを見ることができます。異なるフィルターチェーンを適用している異なるbean名称で、別のGenericProcessImplインスタンスを生成することにより、複数の処理を定義することができます。
それは、Aopaliance interceptorとして実装されたアクティビティフィルタへの参照も含んでいます。デフォルトでは、フィルターはほぼ上から下まで連鎖していますが、より明確に言うと、私たちはorder属性も使用しています。
パート2 (30行目) - ActivityFilterInterceptorの3つのインスタンスを定義することで、アクティビティインターセプターを構成します。それぞれのインスタンスには、facts属性だけでなく、後で定義された対応するPOJOアクティビティのbeanがインジェクトされます。facts属性は、単純なルールのメカニズムを定義し、それは、基底となるアクティビティの実行を許可するかどうかに基づいた単純な状態を特定できます。たとえば、validateItemFilter は「品目を確認する」という事象のルールを定義し、それはVALIDATED_ITEMの事象が事象のレジストリ内に登録されていなければ、アクティビティを呼び出すと解釈されるでしょう。この事象は、validatItemActivityが実行されるとすぐに、事象のレジストリに登録されます。そして、事象がまだ登録されていない場合は、このアクティビティは実行可能となり、同じ実行コンテキストに事象が既に登録されている状態でプロセスが再送信された場合は、このアクティビティが再度実行されないようにします。
パート3 (47行目) - 私たちのプロセスに対し、3つのPOJOアクティビティを設定します。
ActivityFilterInterceptor - これがおこなうすべてのことは、基底にあるPOJOアクティビティを呼び出し、このアクティビティ(53行目)によって返された事象を登録します。そして、POJOアクティビティによって、事象のレジストリやこのプロセスのその他すべての基盤となるコンポーネントを見せないようにすることができます(下図参照)。しかし、私たちが後で見るように、このインターセプター自身の呼び出しは、それぞれのインターセプターの設定で指定された事象のルールに基づいたAspectJのアドバイス(第2のAOP層)によって制御されます。このようにして、ここのアクティビティの実行を制御しています。
個々のPOJOアクティビティは、所有しているインターセプター(上記参照)によって事象のレジストリに登録されているもので、登録したいと思っている全ての事象をStringのArrayで返します。
TransitionGovernorAspect - これは、AspectJ のコンポーネントで、個々のアクティビティを表すSpringのAOPインターセプターそれぞれの呼び出しをインターセプトします。それは、Aroundアドバイスを使用しておこなわれ、事象のルールと現在の事象のレジストリを評価します。そして、基底となるactivity interceptorの呼び出しを実行するのか、スキップするのかについての決定を下します。それは、自身の呼び出しオブジェクト(ProceedingJoinPoint thisJoinPoint)のproceed(..)メソッドを呼び出すか、intercepting filterの呼び出しオブジェクト(MethodInvocation proxyMethodInvocation)のproceed(..)メソッドを呼び出すかのいずれかによっておこなわれます。
それはAspectJのアスペクトとして実行されるので、私たちは META-INF/aop.xml
(下記参照)で構成を規定する必要があります。
私たちはロードタイムのAOPを使うので、Springの設定にウィーバーを登録する必要があります。私たちは、 context
の名前空間を用いてそれをおこないます。:
この時点で、私たちはテストの準備が整っています。お分かりのように、テストについては大したことはありません。以下のステップとなります。:
- ApplicationContextを取得します
- GenericProcessオブジェクトを取得します
- 事象のレジストリリストを生成します
- 実行コンテキストだけでなく、入力データを表すオブジェクト(私たちのケースではMapです)を生成します
- プロセスメソッドを呼び出します
私たちはAspectJのロードタイムウィービングを利用しているので、VM引数として-javaagentオプションが必要になります。
The VM argument is:
-javaagent:lib/spring-agent.jar
既に、libディレクトリ内にspring-agient.jar
が存在します。
実行後は、以下のような出力となるでしょう。:
テストで見たように、事象の初期リストは空です。しかし、既存の事象とともにそれを投入するのであれば、プロセスフローは変わるでしょう。
レジストリに事業を追加する行をコメントアウトしてみましょう。
あなたのテストの次の行をコメントアウトしてください。:
// factRegistry.add("VALIDATED_ITEM");
そして、アウトプットが変わるはずです。:
6. 結論
今回のアプローチでは、プロセスフローを組み立て、協調し、制御するためにAOPの2つの層を使用する方法を実演しました。第1の層は、Spring AOPを使用して実装され、それぞれのフィルターが個々のアクティビティでインジェクトされ、一連のintercepting filterとしてプロセスを組み立てます。第2の層は、AspectJを使用して実装され、プロセスの協調とフロー制御を提供します。一連のintercepting filterによるプロセスをプロキシすることで、プロセスの方向を定義・維持することができます。しかし、メカニズムをプロキシすることで、BPMのような分離したエンジンを必要とせずに実行環境を提供することもできます。私たちは、制御と実行のメカニズムを提供する既存の技術(Spring AOP)を使用することでそれをおこないました。
そのアプローチは軽量で組み込まれたものです。既存のSpring基盤を使用し、プロセスが協調されたアクティビティの集まりであることを前提として構築しています。それぞれのアクティビティはPOJOで、それを管理しているすべての基盤/制御のコンポーネントについては、全く知りません。これにより、いくつかのメリットが与えられます。一般的なアーキテクチャの利点である疎結合の他に、ますます高まる人気、OSGiなどの技術の採用、アクティビティの保持とアクティビティの呼び出し制御の分離、OSGiのサービスとして実装されたアクティビティへの門戸の解放、それぞれのアクティビティが独立した管理ユニット(デプロイ、更新、アンデプロイ など...)となることを可能にしていることなどがあります。テストでは別の利点があります。アクティビティがPOJOであるので、適用されたプロセスを除いたPOJOとしてのテストが可能です。それらには非常に良く定義された入力/出力の規約(必要とするデータと出力を期待しているデータ)があります。あなたはそれぞれのアクティビティのテストを分離しておこなうことができます。
さらに、ビジネスロジック(POJOアクティビティ)から制御ロジック(intercepting filter)を分離することで、テストのトランジッション制御ロジックが、基底のアクティビティによって実装されたビジネスロジックに作用されないことをテストするだけでなく、事象のルールを処理するためのより洗練されたルールのファサードのプラグインを可能とします。
アクティビティは構築したブロックから独立していて、ある別のプロセスの一部として再利用することができます。たとえば、「クレジット確認」のアクティビティは、クレジットの確認を必要としている別のプロセスを組み立てるときに、容易に再利用することができます。
7. 参考文献と情報源
8. 著者について
Oleg Zhurakousky は、現在、SpringSourceでシニアコンサルタントとして働いているITプロフェッショナルです。ソフトウェアアーキテクチャ、設計、コンサルティング、ビジネス分析、アプリケーション開発を含む複数の分野で、ソフトウェアエンジニアとして14年上の経験があります。
90年代前半にCOBOLとCICSの世界で彼の職歴が始まったのを皮切りに、1999年以降は、専門のJavaとJava
EE開発に注力していました。2004年以降、Olegはいくつかのオープンソース技術とプラットホーム(特にSpring)を使うことにかなり熱中していました。その一方で、電気通信、銀行業務、法令施行、Telecommunication、Banking、法Enforcement、米国国防総省など、数多くの仕事に取り組んでいます。