n階層システム設計の考慮点

第5回ビジネスレイヤの設計について

今回は、ビジネスレイヤの設計についての注意点およびノウハウについて解説していきます。

ビジネスレイヤ

ビジネスレイヤにはプレゼンテーションレイヤとのデータ受け渡し、渡されたデータに対するチェック、操作、加工、データレイヤとのデータの受け渡し、例外処理、そしてアプリケーションの中心となる業務ロジック機能が実装されます。

ビジネスレイヤは2階層、4コンポーネントから構成されています。

画像
サービスインターフェイス

ビジネスレイヤのビジネス機能をサービスとして公開する場合、クライアントからの呼び出し用に(プレゼンテーションレイヤだけではなく、Webサービスとして公開した場合やビジネスレイヤからの呼び出しも含みます)内部機能を抽象化したエントリポイントを公開する必要があります。

これらはファサード(複雑な処理の呼び出しを単純化するためのパターン。GoF[1]によって定義されました)と呼ばれ、Javaやほかのデザインパターンでも一般的なものです。

ビジネスワークフロー

ビジネスワークフローは業務の流れやデータの流れに沿ってビジネスコンポーネントや使用されるビジネスエンティティの流れをコントロールするコンポーネントです。.NET Framework3.0以降ではWindows Workflow Foundation(以降WFと略します)を使用して構築するのが効率のよい手法だと思います。

ビジネスコンポーネント

ビジネスコンポーネントはビジネスレイヤの核ともいえるコンポーネントです。ビジネスコンポーネントではさまざまな業務ロジックを組み合わせ、必要であれば、ほかのビジネスコンポーネントと連携しながら業務ロジックを実行します。この連携の際、サービスとして公開されたビジネスレイヤと連携する場合はビジネスエンティティと複数のビジネスレイヤの呼び出し、実行権限などのアクセスライセンス(デジタルID)を管理するWindows CardSpace(以降WCSと略します)を経由して連携します。同じビジネスレイヤ内であればWindows Communication Foundation(以降WCFと略します)でデータ連携し、ビジネスワークフローで定義された流れに沿って業務ロジックを実行していきます。

ビジネスエンティティ

ビジネスエンティティはサービスインターフェイスを通してサービスとして公開する場合、データレイヤとのデータのやり取り、ビジネスレイヤ内で共通して保持するデータなどを表します。ここで、定義されるデータの形式には以下のものがあります。

  • XML
  • DataReader
  • 型なしDataSet
  • 型付きDataSet
  • カスタムビジネスエンティティコンポーネント

    このデータ形式では、サービスとして公開したビジネスレイヤがほかのアプリケーションからの呼び出される際に、構造化データをやり取りする際に作成されます。これにより、データレイヤからのデータレイアウトや型を気にすることなく構造化データを作成することが出来ます。これにより、サービスとして公開されたビジネスレイヤの独立性を保てるとともに、アプリケーション内部構造を隠蔽することにも役立ちます。これらを保つためにもカスタムビジネスエンティティコンポーネントでは直接データレイヤにアクセスするような構造化データの作成は避けるようにします。

ビジネスレイヤの各コンポーネントは、配布されたPC上のOSのサービス権限で実行されるように設計される場合がほとんどです。しかし、ビジネスレイヤがサービスインターフェイスを通して公開されており、異なるドメインに所属するPCなどからサービスとして呼ばれる場合、呼び出し側の実行権限により実行できないことが想定されます。このような場合、WCFを用いて連携することで、より広範囲にサービスを公開することができるようになります。

それでは各コンポーネントの設計についての注意点およびノウハウについて解説します。

① サービスインターフェイス設計の注意点およびノウハウについて

ビジネスレイヤの機能を公開する場合、クライアントからの呼び出し用に内部構造、呼び出し方法を定義する必要があります。この定義をサービスインターフェイスとして実装します。

セキュリティ境界

サービスインターフェイスはアプリケーションのセキュリティの境界となります。つまり、サービスインターフェイスを経由して呼び出すクライアントは適切なアクセス権限を有する必要があります。つまり、サービスインターフェイスからビジネスコンポーネントの間でファサード作成し、アクセス権限チェックを行う必要があるということになります。しかし、このアクセス権限チェックはできるだけ単純なものとし、プラットフォーム非依存型の認証方法を採用する必要があります。これは、さまざまなプラットフォームやサービスとの相互互換性を確保し、より汎用性のあるサービスとして公開するためには必要なことです。

サービスインターフェイスの不変性

サービスインターフェイスを設計する場合に最も気を付けなければいけないことは、ビジネスレイヤの内部構造が変更になってもサービスインターフェイスに影響を及ぼさないようにすることです。サービスインターフェイスから呼び出されるビジネスコンポーネントは常に1つのサービスインターフェイスのみから呼び出されるとは限りません。ほかのサービスインターフェイスからや同じビジネスレイヤ内のビジネスコンポーネントからも呼び出される可能性があります。そして、業務仕様によってビジネスコンポーネントの仕様が現在の機能に影響がない程度に変更(内部のチェック関数の変更など)されるかもしれません。変更が行われてもサービスインターフェイスに影響しないように設計する必要があります。

サービスインターフェイスの多様化

1つのビジネスロジックはさまざまなクライアントから呼び出される可能性があります。そのため、そのクライアントごとにサービスインターフェイスが必要となる場合があります。しかし、この場合でもビジネスロジックの変更がサービスインターフェイスに影響を及ぼさないように設計する必要があります。

サービスインターフェイスの実装内容

サービスインターフェイスにはキャッシング、単純な型変換などの機能を実装しても構いませんが、ビジネスロジックは実装しないようにします。これはビジネスロジックへの仕様変更が発生した場合にこのサービスインターフェイスを呼び出しているクライアントへの影響が大きくなることを防ぐためです。

サービスとしての公開方法

サービスインターフェイスを通してビジネスレイヤをサービスとして公開する場合、以下のような方法で公開することができます。

XML Webサービスとして公開

ASP.NET Webサービスとして構築して公開するか、プロトコルとしてSOAPとHTTPを使用した.NET Remittingを用いたサービスとして公開するか選択することができます。

メッセージキューまたはEnterprise Services(以降COM+と呼びます)キューに対してサービスを公開

この場合、メッセージキューのトリガを実装し、呼び出しに対して実行、応答するようなサービスインターフェイスとしてきのうするコンポーネントを実装します。

独自サービスとして公開

ビジネスレイヤ自体を一つの関数として公開することもできます。この場合は、サービスインターフェイス内で通信プロトコル、通信手順、承認方法、データ受け渡し手順などすべてを定義する必要があります。しかし、社内等の限定的な範囲の公開であればこれらを明確に規定することができるため、サービスとして利用される場合は多いと思われます。

サービスインターフェイスでのトランザクションの扱い

サービスインターフェイスでは、トランザクションを宣言する場合と宣言しない場合の2つのパターンがあります。

メッセージキューに対してサービスを公開する場合はトランザクションの宣言が必要となります。この場合、サービスインターフェイスでトランザクションを宣言し、メッセージを受信・処理・応答してトランザクションを閉じるまでを行います。しかし、これらの過程のどこかでエラーが発生した場合はトランザクションをロールバックさせ、メッセージの受信自体を「未受信」とする必要がります[2]⁠。

XML Webサービスとしてサービスを公開する場合はトランザクションの宣言ができません。この場合はサービスインターフェイスからトランザクションルートとなるビジネスコンポーネントの呼び出しが必要となります。本方式ではトランザクション処理中にエラーが発生した場合は、サービスインターフェイスはクライアントに対してエラー発生を通知します。ただし、非同期として呼び出された場合は通知する手段がないので、ログなどでエラー発生を通知する方式を検討する必要があります。

WCFの利用

.NET Framework 3.0以降ではこれらのサービスの公開はWCFを用いて簡単に公開できるようになりました[3]⁠。WCFは上記のXML Webサービス、.NET Remitting、メッセージキューへの対応、COM+そしてWeb Services Enhancements 3.0(以降WSEと略します)に対応したWebサービスを公開することができます。

② ビジネスワークフロー設計の注意点およびノウハウについて

ビジネスレイヤはさまざまなビジネスコンポーネントで構成されています。そして、それは単体で稼働するもの、複数のコンポーネントで連携して稼働するものなどさまざまです。それらがビジネスルールに従って順序よく、同期または非同期で呼び出されます。これらは複数の手順、ルールに沿って実行され、ビジネスプロセスが終了するまで、状態を保持しながら実行される必要があります。レガシーシステムの汎用機の時代であればJob Control Language(以降JCLと略します)を使用して制御・実行・管理されていました。しかし、Windows OS標準ではJCLに相当する機能はありますが、機能的に貧弱です。そのため、Microsoft社ではこれらの機能を補完する製品としてBizTalk Serverをリリースしてきました。

BizTalk Serverはビジネスプロセスが終了するまでの制御・実行・管理を行うことができますが、.NET Frameworkとの親和性という観点ではあまりよくなく、やはり別製品であるという感じは否めませんでした。

しかし、.NET Framework 3.0以降ではWFを利用できるようになりました[4]⁠。これにより、BizTalk Serverを使用することなく、ビジネスプロセスが終了するまでの制御・実行・管理を行うことができます。

また、WFではバッチアプリケーションで使用されるような単純なシーケンシャルワークフローから複雑なステートマシンワークフローを作成・管理・実行・制御することができます。また、WCFと連携してワークフローをWebサービスとして公開することもできます。これについては後述します。

それではそれぞれのタイプのワークフローについて例も交えて説明します。

シーケンシャルワークフロー

シーケンシャルワークフローは、ワークフローが開始されてからさまざまなステップ、条件式などが次々に実行され、最後のアクティビティが完了するまで途切れることなく続行される処理に適しています。しかし、シーケンシャルワークフローはその中で定義された外部接続、外部イベントからの接続、条件により複数の処理の同時実行などにより、内部に定義された実行順序が異なる場合があります。

それでは前回取り上げた旅行代理店の店舗向けのアプリケーションのシナリオで、ビジネスワークフローの部分のみを考えてみす。このシナリオでは、ある旅行商品の見積もり業務で複数の交通機関を使用する旅行商品があるとします。まずは顧客の要望する旅行プランの旅行コードが存在するかを検索します。その際にビジネスレイヤのビジネスワークフローが使用されます。この場合はシーケンシャルワークフローで実現できます。

まず、プレゼンテーションレイヤから渡された入力データ(操作者のIDや旅行コードなど)をチェックします。エラーがあった場合はエラー処理でエラーデータを返します。入力チェックでエラーがない場合、入力データ内の旅行コードの問い合わせの準備をします。次にデータレイヤを呼び出します。呼び出しでエラーが帰ってきたらエラー処理でエラーデータを返します。データレイヤから問い合わせ結果データが帰ってきたらデータを返します。

これをフローチャートとVisual Studio 2005 ワークフローデザイナで記述した結果は以下の図のようになります。

フローチャート
フローチャート
Visual Studio 2005 ワークフローデザイナ
Visual Studio 2005 ワークフローデザイナ

このようにWFでビジネスワークフローを作成することができます。

本例ではエラーコード編集を入力データチェックとデータレイヤ呼出で別に作成しましたが、本来であれば共通関数化し、エラーコードなどもアプリケーションで一元管理するように設計します。

ステートマシンワークフロー

次にステートマシンワークフローについて説明します。ステートマシンワークフローは、一連の状態、遷移、および動作で構成されます。1つの状態を開始状態とし、その後は、イベントや動作により、別の状態へと遷移していきます。また、ステートマシンワークフローには、ワークフローの終わりを特定する最終状態を指定できます。

それでは上記の旅行代理店の店舗向けのアプリケーションのシナリオで考えてみましょう。このシナリオでは、ある旅行商品の見積もり業務で複数の交通機関を使用する旅行商品があるとします。シーケンシャルワークフローで顧客の要望する旅行プランの旅行コードが決定されました。この旅行プランは複数の交通機関を利用し、すべての交通機関で利用する座席が確定することで成立するものです。そこで、顧客の希望の日程で旅行ができるかそれぞれの交通機関に対してオンラインもしくは電話などで予約をすることになります。今回の旅行プランはA社の電車でα駅からβ駅に移動します。その後、B社の電車でB-β駅からθ駅に行きます。そこからはC社の専用車両(定員制)でD社が運営する観覧施設に入ります。しかし、この観覧は事前抽選制で観覧日付、入館時間と退館時間を指定し、観覧者が葉書で申し込みする必要があります。その後、往路の逆の経路で帰ってくるプランです。

A社とB社のそれぞれの座先予約アプリケーションにはオンラインで接続できます。しかし、C社は電話での問い合わせが必要です。また、D社は葉書、かつ抽選です。

この場合でも、ジネスレイヤのビジネスワークフローが使用されます。この場合はステートマシンワークフローが使用されます。

まず、A社とB社はオンラインアプリケーションをデータレイヤのサービスエージェントから接続し、結果を取得します。取得した結果は操作者のクライアントPCもしくはワークフローの状態保持機能を使用してサーバやデータベースに格納します。

次にC社です。これは操作者が電話で問い合わせを行い、画面から問い合わせ結果を登録します。

問題はD社です。これは葉書を使うことからロングトランザクションになることが分かります。また、抽選式ですので、A社、B社そしてC社の予約が取れても結果によってはすべてキャンセルになる可能性もあります。

これをVisual Studio 2005 ワークフローデザイナで記述した結果は以下の図のようになります。

Visual Studio 2005 ワークフローデザイナ
Visual Studio 2005 ワークフローデザイナ

今回の例でもエラーコード編集を入力データチェックとデータレイヤ呼出で別に作成しましたが、本来であれば共通関数化し、エラーコードなどもアプリケーションで一元管理するように設計します。

WFの設計時の注意点

そのほかにも設計上の注意点は下記のようなものがあります。

長時間永続化されたワークフローはできるだけ少なくする

長時間永続化されたワークフローは実装に多くの時間がかかります。また、実行時にシステムに負担をかけます。そこで、可能であれば、start-to-finishワークフローでの設計を検討します。

カスタムアクティビティは少なくする

カスタムアクティビティは最小限にします。特に、WCFやその他のコンポーネントで作成されたカスタムアクティビティは通常のstart-to-finishワークフローと比較してシステムに負担をかけます。

1つのアプリケーションドメインにはワークフローランタイム1インスタンスのみ

WFのアーキテクチャを見て頂くとわかるのですが、1つのアプリケーションドメインには1つのワークフローランタイムしか稼働しません。そのため、設計時からそれを意識して設計する必要があります。

WFの開発にはVisual Studio 2008を使用する

Visual Studio 2008では簡単なワークフローであればウィザードで作成ができます。そのため、工数の削減ができます。

③ ビジネスコンポーネント設計の注意点およびノウハウについて

ビジネスコンポーネントには以下のような機能が含まれます。

入力チェック

サービスインターフェイス経由もしくは直接ビジネスコンポーネントが呼び出された場合でも、業務ロジックにデータを渡す前にチェックを行います。ここでは単純な入力チェックだけでなく、データの相互性チェックや論理チェック、データレイヤからのデータとのマッチングなどさまざまなチェックを行います。これらは共通部品化され、ビジネスレイヤ内で再利用されることが多いため、それを意識した設計にする必要があります。しかし、共通化を図るあまり、内部が複雑になってしまうケースがよく見受けられます。共通部品の種類は多くなってしまいますが、できるだけ単純な機能の単位で設計するようにします。

ビジネスファサード

いくつかのビジネスコンポーネントを纏めて実行する場合やインターフェイスを纏める場合などで作成されます。ここにはビジネスロジックは実装しません。

業務ロジック

業務ロジックそのものです。これらも機能単位でカプセル化され、再利用、再試行、再構成が可能になるように設計します。しかし、これは下記のトランザクション管理と密接に関連し、同じデータの2重実行でもデータの一貫性を保つように設計します。

業務ロジックのデータの入出力はできるだけ一貫性を保つようにし、XMLやDataSet(型付き、型なし)などでやり取りします。

トランザクション管理

ビジネスコンポーネントはトランザクションを開始できます。そのため、トランザクションの境界を意識して設計する必要があります。また、短時間で終了するトランザクションだけでなく、長時間トランザクションも管理するため、状態、中間データ等の管理も考慮する必要があります。

トランザクション開始後、別のビジネスコンポーネントの呼び出しなどがある場合、トランザクションに参加しているすべてのビジネスコンポーネントのどこかでエラー発生・中断・例外等が発生した場合、これらをすべてロールバックできるように設計する必要があります。

また、トランザクション開始後、別のビジネスコンポーネントがデータレイヤのサービスエージェントを経由してアプリケーション外に接続している場合などでは、非同期処理として考え、ビジネスデータ的に一貫性、整合性が保てるように設計します。しかし、この場合、可能であれば接続先の状態を入手し、メッセージやログなどに出力し、痕跡が残るように設計します。

バッチ処理的にビジネスコンポーネントが設計・実装・利用される場合は中間コミット等を用い、エラー等が発生した場合の再試行性の向上を図ることも設計時に検討します。

また、長時間トランザクションについても一定時間の経過後トランザクションを終了させるポリシーを作成し、それに従って設計を行います。

④ ビジネスエンティティ設計の注意点およびノウハウについて

ビジネスエンティティはサービスインターフェイスを通してビジネスレイヤがサービスとして公開されている場合およびデータレイヤとのデータの受け渡しにおいて、さまざまなデータ形式が使用されます。

XML

W3C(World Wide Web Consortium)標準の準拠のため、プラットフォームに依存しません。 しかし、厳密な型の指定のエンティティを作成知るためにはXML スキーマ定義言語(以降XSDと略します)で定義する必要があります。

XMLを作成するためには専用のエディタもしくはVisual Studio等を用いて作成するとタイプミスなどが少なくなります。

また、.NET Language-Integrated Query(LINQ⁠⁠ to XMLを用いることで、メモリ内に読み込んだXMLの構文解析・ソート・ツリー構造化・操作が行えるようになりました。

DataReader

ADO.NET(ActiveX Data Objects for .NET Framework)を利用してデータベースに同期型で接続しデータを取得します。データの型は基本的にデータベース内のデータ型に依存します。

型なしDataSet

ADO.NETを利用して非接続型で接続し、データのやり取りを行います。

型なしDataSetはシリアル化に対応しており、ユーザインターフェイスコンポーネント(DataGrid、GridViewなど)への表示が容易にできます。 また、データ・項目の並べ替え、フィルタリングができ、DataSetの内容をXMLとして入出力することができます。この場合はXSDがないため、厳密な型指定はできません。

しかし、型なしDataSetのインスタンス生成やデータの出し入れ、検索の際に実行環境に負荷がかかります。

型なしDataSetは厳密な型指定がされていないため、コンパイル時に型のチェックがされず、エラーの原因となりやすいです。

型付DataSet

ADO.NETを利用して非接続型で接続し、データのやり取りを行います。型付DataSetはシリアル化に対応しており、ユーザインターフェイスコンポーネント(DataGrid、GridViewなど)への表示が容易にできます。 また、データ・項目の並べ替え、フィルタリングができ、DataSetの内容をXMLとして入出力することができます。この場合はXSDがあるため、厳密な型指定がされます。

しかし、型付DataSetのインスタンス生成やデータの出し入れ、検索の際に実行環境に負荷がかかります。

型付DataSetは厳密な型指定がされてるため、コンパイル時に型のチェックがされます。 この際、DataSetのデータ操作などのメソッド等も自動的に生成されるので、容量が大きくなります。

型付DataSetはXSDで厳密に型指定されているため、データの受け渡し側に変更が発生した場合は再作成する必要があります。

カスタムビジネスエンティティコンポーネント

カスタムオブジェクトは個別に型指定されたプロパティやメソッドを作成するため、プライベートロジック、個別の操作や検証用ロジックを実装することができます。

カスタムオブジェクトは厳密な型指定がされてるため、コンパイル時に型のチェックがされます。

Visual Studioを用いてソースを作成する際には、インテリセンスを使用してカスタムオブジェクト内のプロパティやメソッドを呼び出すことができます。

カスタムオブジェクトは個別に作成するソース内で厳密に型指定するため、開発工数が大きくなります。 また、厳密に型指定されているため、データの受け渡し側に変更が発生した場合は再作成する必要があります。

しかし、.NET Framework 3.5でサポートされたLINQ to Objectを用いることで、メモリ内に読み込んだカスタムオブジェクトの構文解析・検索・ソート・ツリー構造化・操作が行えるようになりました。

サービスインターフェイスを通してビジネスレイヤがサービスとして公開されている場合は、XML、カスタムビジネスエンティティコンポーネントを使用します。これらを使用することで、ほかのプラットフォームとの互換性を持たせることができます。

データレイヤとのデータの受け渡しにおいては、XML、DataReader、型なしDataSet、型付きDataSet、カスタムビジネスエンティティコンポーネントを選択し、より効率のよい方法を用いて設計します。これらの選択基準としては、ビジネスコンポーネントでのこれらデータの操作によって選択できると思います[5]⁠。

XML

XMLは下記に紹介するLINQ to XML技術を使用しない限り、検索・ソート等は低コストでは実装できません。しかし、軽量であることと値の読み出しなら低コストで実装できます。

DataReader

ビジネスコンポーネントでデータを加工し、データレイヤ経由でデータベースを更新する場合、DataReaderでは、Arrayなどへの値の入れ替え、更新前に型付きDataSetでのデータの確認後、更新となるので、非効率となります。しかし、接続したデータベースからの値の読み込みだけであれば高速で、低コストで実装できます。

型なしDataSet

複数のTableやデータ以外のオブジェクトを扱うのであれば型なしDataSetを選択するのが低コストで実装できます。

型付きDataSet

厳密な型指定がされているので、ビジネスコンポーネントでデータを加工し、データレイヤ経由でデータベースを更新する場合はそのままデータを渡すことができるので、低コストで実装できます。

カスタムビジネスエンティティコンポーネント

カスタムビジネスエンティティコンポーネントに関しては、上記の手法では実装が難しい場合などに選択することとなります。

LINQの登場

LINQが登場するまでのXMLおよびカスタムビジネスエンティティコンポーネントでは、構文解析・検索・ソート・ツリー構造化・操作などを行うことは非常に困難で、これらを行うために別にコンポーネントを作成したり、型付きDataSetに格納・操作後、再取り出す等のオーバヘッドを行っているプロジェクトもありました。

また、さまざまなデータベースへの接続、コマンド等はSQLで基準化されているとは言え、少しずつ作法や方言的なものがあり、使用するものによって使い分ける必要がありました。

さらにXMLドキュメントの操作や各種Webサービスなどそれぞれのデータソースに対してクエリ言語を習得する必要もありました。

しかし、LINQの登場により、これらは開発時に使用する言語の一部として扱えるようになりました。クエリは、C#とVisual Basicにおける言語構成要素となり、言語キーワードと使い慣れた演算子を使用することで、厳密に型指定されたオブジェクトのコレクションに対するクエリを記述できるようになりました。

LINQ技術はデータレイヤで扱うデータソースやサービスへの接続・操作であるため、データレイヤで扱うべきではないかとの意見もあります。しかし、私としてはビジネスロジックと密接な関係がある、開発言語の一部として提供されていることなどからビジネスエンティティで取り扱うべきであると考えています。

おすすめ記事

記事・ニュース一覧