この特集のトップページへ
Chapter 5:ビジネスロジック層の構築

5.5.1 サンプルの概要
 ここでは,以上で示した在庫テーブルと受注テーブルを使って受注するためのCOMコンポーネントを実装してみることにする。簡単にいえば,次のような仕様のCOMコンポーネントを実装しようということである。

  1. 製品ID,受注者名,数量を引数とする
  2. 在庫テーブルを開き,指定された製品IDの在庫を数量分だけ減らす
  3. 受注テーブルを開き,日付(これはCOMオブジェクトのメソッドが呼び出された瞬間の日付としよう),受注者名,製品ID,数量を記録する

 このようなプログラムをCOMではなく,一般的なプロシージャとして実装すると,List 5-8のようになるだろう。

 List 5-8では,上記の2と3の処理を,ADODB.ConnectionオブジェクトのBeginTransメソッド(19行目)とCommitTransメソッド(53行目)で括り,トランザクション処理にしている点に注目してほしい。40行目では在庫が負にならないかどうかを調べているが,このあいだに別の注文が入ってしまうと,在庫が負であるにもかかわらず,受注を受け付けてしまうおそれがある(Fig.5-40)。そのために,トランザクションとしてひとまりで処理されるようにしているのである。

Fig.5-40 トランザクション処理にしなければならない理由
fig5_40.gif

 このように,ここで実現したい処理はList 5-8のようにBeginTransメソッドとCommitTransメソッドを使って比較的単純なプログラムで表現できる。

 では,これをCOMコンポーネント化してみることにしよう。ただし,List 5-8をそのまま1つのCOMコンポーネントにするのではなく,次の4つのCOMコンポーネントに分割するものとする。

1.在庫を調べるコンポーネント

 次のような引数で呼び出されるメソッドを実装する。

Public Function checkStock(ByVal ProductID As Long) As Long

 このメソッドは,在庫テーブルを開き,引数ProductIDで指定された製品の在庫の数(STOCKフィールドの値)を返すものとする。
 

2.在庫を設定するコンポーネント

 次のような引数で呼び出されるメソッドを実装する。

Public Sub updateStock(ByVal ProductID As Long, _
                       ByVal newStock As Long)

 このメソッドは,在庫テーブルを開き,引数ProductIDで指定された製品の在庫の数を引数newStockで指定した値に設定する。
 

3.受注テーブルに新規レコードを追加するコンポーネント

 次のような引数で呼び出されるメソッドを実装する。

Public Sub insertOrder(ByVal ProductID As Long, _
                       ByVal CustomerName As String, _
                       ByVal Number As Long)

 このメソッドは,受注テーブルを開き,CustomerNameという受注者がProductIDで指定された製品をNumber個発注したという情報を受注テーブルに書き示す。
 

4.受注を受け付けるコンポーネント

 次のような引数で呼び出されるメソッドを実装する。

Public Sub acceptOrder(ByVal ProductID As Long, _
                       ByVal CustomerName As String, _
                       ByVal Number As Long)

 このメソッドは,上記の13のCOMオブジェクトを組み合わせて呼び出し,受注を受け付ける。
 

 14のコンポーネントをCOM+に登録して利用するときのイメージは,Fig.5-41のようになる。

Fig.5-41 COMオブジェクトの組み合わせ
fig5_41.gif

 つまり,4がルートオブジェクトとなり,そのCOMオブジェクトが123のCOMオブジェクトを呼び出すことになる。もちろん,これらは1つのトランザクションで動作させるわけだから,13のCOMコンポーネントのトランザクションの設定は[必要](ただし,4のCOMコンポーネントは[新しく必要]でもよい)に設定する必要がある。

 List 5-8を1つのCOMコンポーネントにするのではなく,4つに分解した理由は,次の2つである。

 1つ目の理由は,分解しておけば,ほかの用途で再利用できるからである。たとえば,2のコンポーネントは,引数ProductIDで指定された製品の在庫をnewStockで指定された在庫数に変更するためのものである。このコンポーネントは受注のときに在庫を減らすという用途以外に,在庫を追加する場合にも使えるだろう。また,1のコンポーネントは,特定の製品の在庫の数を調べる機能を備えているが,在庫数を調べたいときというのは,何も受注処理をするときだけではない。その時点の在庫情報を参照するような処理は,将来的に在庫を集計したい場合などで必要とされてくるはずだ。もっとも,List 5-8の場合には,その程度のメリットしかないが,より複雑なデータベース処理では,単機能のCOMコンポーネントを組み合わせて1つのまとまった仕事をさせたほうが,同じような処理を複数のCOMコンポーネントに実装しなくてすむため,開発効率やメンテナンス効率が向上する。

 2つ目の理由は,より高速に実行するためである。COM+では,ジャストインタイムアクティベータの機能によって,用済みになったCOMオブジェクトをサーバー上からさっさと除去することができる。そしてこのとき,確立されていたデータベースコネクションも,接続プールに戻す。1つのCOMコンポーネントに複雑な処理を実装すると,それだけ1つのCOMコンポーネント内で処理すべき仕事が増えるので,実行時間は長くなる。つまり,サーバー上でのオブジェクトの存続時間も長くなる。しかし,複数のCOMコンポーネントに分割しておき,それらを呼び出すようにしておけば,個々のCOMオブジェクトは仕事が終わり次第,サーバーから除去することができるので,サーバー側の負担は軽くなる。つまり,パフォーマンスが向上する。

 「Chapter 1 クライアント/サーバーアプリケーションの仕組み」でも少々説明したが,COM+を使った開発においては,できるだけCOMコンポーネントを分割して,1つのCOMコンポーネント内で実行する仕事を減らすように努めるべきである。そうすることによって,COM+のジャストインタイムアクティベータは,より効率よく機能するようになる。

 ここでは,トランザクションの実験をわかりやすくするため,それぞれの処理をコンポーネント単位で分割するが,別にメソッド単位で分割してもかまわない。というよりも,そのほうが一般的である。普通は1つの処理を1つのCOMコンポーネントに実装するようなことはしない。なぜなら,COMオブジェクトを保持するためには,若干のメモリが必要となるためである。特に,ネットワークを経由してアクセスするような場合には,実体化したCOMオブジェクトの数だけプロキシとスタブが必要となる。そして,COM+の内部で利用される場合も,コンテキストの境界にはインタセプタが生成されるので,その分だけメモリが必要になる。

 では,どのような基準でCOMコンポーネントを分割するのがよいのだろうか。基本的にはCOMコンポーネントの仕事の範囲で分割することになるのだが,特別これといった基準はない。しかし,わかりやすさのうえで,一般的に1つのデータベーステーブルに対してアクセスするメソッドを,まとめて1つのコンポーネントにすることが多い。そして,それらのデータベーステーブルに対してアクセスするCOMオブジェクトを組み合わせ,ルートオブジェクトとなるCOMコンポーネントを作り,利用するわけである。このとき,データベースにアクセスするCOMオブジェクトのことを特に,「データオブジェクト」と呼ぶ。つまり,N階層アプリケーションとして見た場合,ビジネスロジック層は2つに分かれ,全体では4階層となる(Fig.5-42)。

Fig.5-42 データオブジェクトの導入
fig5_42.gif

 Fig.5-42では,データベーステーブルとデータオブジェクトを1対1で対応させているが,このようにしなければならないということではない。複雑なビジネスアプリケーションでは,同じテーブルに対する操作であっても,細分化することもある。

 なお,COM+においては,COMコンポーネント単位でトランザクションを設定するということに注意してほしい。そのため,たとえば,同じCOMオブジェクトに備わるAというメソッドのトランザクションは[必要]と設定するが,Bというメソッドのトランザクションは[新しく必要とする]と設定する,というようにメソッド単位でトランザクションの設定を変えることはできない。したがって,最低限,トランザクションを設定する単位でCOMコンポーネントを分割しなければならないということになる。

prevpg.gif Chapter 5-2 3/16 nextpg.gif