エンタープライズアプリケーション・アーキテクチャーパターン入門
はじめに
本資料は、株式会社アクシオン・ソフトウェアが主催するSAN(SaturdayActiveNetwork)で行う、勉強会用の資料である。 「エンタープライズアプリケーションアーキテクチャ入門」の書籍から、勉強会用にピックアップしたものになります。
対象読者としては、ビジネスアプリケーションに携わるプログラマ、設計者、およびアーキテクトとしている。 また、JavaやC#の知識があることが必要である。
本資料では、開発者が早期に確認したいと思っている決定事項やアプリケーションの主要となるパーツについて、パターンを通じて説明する。 また、エンタープライズアプリケーションとしているため、給与計算・診療記録・出荷管理・会計・顧客サービスなどのアプリケーションを対象とする。
アプリケーションアーキテクチャ
アーキテクチャは、システムを開発・存続していく上で、重要な要素である。 しかし、なかなか決定できない要素でもある。 アーキテクチャには少なくとも2つの要素があると考えられる。 1つは、システムから個々のパーツへブレークダウンできること、1つは、簡単には変更できない決定事項であるということである。 また、1つのシステムには、複数のアーキテクチャが存在してもよく、システムの存続期間中に変わることもありえる。
レイヤ化
レイヤ化は、ソフトウェアシステムの分割のために使用する一般的な技法である。
以下のようなメリットがある。
・ 各レイヤは、その下位のレイヤについてのみ知っていればよく、さらにその下位のことは知らなくてもソフトウェアが構築できるのである。 たとえば、イーサネットの動作を知らなくても、TCPの上のFTPサービスを構築できる。
・ レイヤを入れ替えることも可能となり、柔軟なシステムを構築することができる。 たとえば、FTPサービスは、イーサネット・PPP・ケーブル会社が変わっても、実行に支障はない。
また、デメリットもある。
レイヤを追加するとパフォーマンスを損ねる可能性がある。
1. 3層アプリケーションアーキテクチャ
これまでのクライアント/サーバシステムでは、ファットなクライアントがUIおよびコントロールをつかさどり、データベースサーバが存在する形であった。 Webの発展に伴い、UIはブラウザで行うことが主流となり、また、オブジェクト指向の普及に伴って、3層のアーキテクチャが注目されるようになった。 ここでの3層とは、プレゼンテーションレイヤ(UI)・ドメインレイヤ・データソースレイヤとなる。 プレゼンテーションレイヤでは、画面の入力・表示を主体とし、ドメインレイヤでは、そのシステムのビジネスロジックを担当する。 データソースレイヤは、DBMSまたはメッセージングシステム、トランザクションモニタ、ファイルシステム、その他のパッケージとの通信などを行う。 プレゼンテーションレイヤとドメインレイヤが分離したことで、プレゼンテーションレイヤに依存しないドメインレイヤの構築が可能となった。 また、クライアント/サーバシステムでは、必然的に、クライアントとサーバの構成をとることになるが、3層のアーキテクチャでは、クライアントであるブラウザとサーバサイドのプレゼンテーションレイヤが分離されるほか、DCOM、J2EEやWebサービスなどの普及にも伴い、ドメインレイヤの物理的な配置にも自由度があがっている。
図1を参照。
図1 レイヤと物理的配置
2. ドメインレイヤ
ドメインレイヤの構築について、3つの主要なパターンに分類して考える。
2.1. トランザクションスクリプト
プレゼンテーションレイヤからの入力に従い、妥当性検証・計算やデータベースアクセス、他システムの操作の呼び出しなどを行い多くのデータを伴ってプレゼンテーションレイヤに応答する。
トランザクションスクリプトには、以下の長所がある。
・ 開発者の大半が理解できる手続き型のモデルである。
・ トランザクションの境界がはっきりしている。 処理の開始と終了がその境界となる。
・ 後に述べる行データゲートウェイやテーブルデータゲートウェイなどのシンプルなデータソースレイヤと連結する。
また、短所を挙げる。
・ 同じような機能では、コードが重複することが多い。
・ 上述より保守性にかける。 これは、開発者が増えれば増えるほど、保守費用の増大を招く。
2.2. ドメインモデル
主にドメイン内(ビジネス内)の名詞について体系化したモデル。
たとえば、賃貸システムなら、賃貸借契約、資産などのクラス分けを行い、妥当性確認や計算のロジックは各クラスに配置する。
ドメインモデルには、以下の長所がある。
・ ドメインに変更や追加があったときに、対象オブジェクトの変更や追加を行うことで対応可能となる。
・ ドメインモデルになれてくれば、複雑なロジックでも体系化された多くの技法(継承やストラテジ、デザインパターンなど)が存在する。
・ 関連する複数のドメインから、利用することができる。
また、短所を挙げる。
・ 手続き型からのパラダイムシフトとなり、慣れるまでに時間を要する。
2.3. テーブルモジュール
データベーステーブルやビューのレコードセットに関するビジネスロジックを扱うひとつのインスタンス。
ドメインモデルでは、契約ごとにインスタンスがあるのに対して、テーブルモジュールでは、1つのインスタンスしかない。 クライアントは、契約に関する操作を呼び出していろいろなことが行える。 個別の契約に対しての操作では契約IDを渡さなければならない。
テーブルモジュールは、さまざまな意味でトランザクションスクリプトとドメインモデルの中間に位置する。
テーブルモジュールの長所を述べる。
・ 直線的な手続きではなく、テーブルを中心にドメインロジックを構築することで、構造がわかりやすく、重複の発見と削除が容易になる。
・ 多くのGUI環境は、レコードセットにまとめられたSQLクエリの結果にしたがって動作する。テーブルモジュールは、レコードセット上で動作するため、容易に処理できる。
・ MicrosoftのCOMや.NETは、この開発スタイルを採用している。
また、短所を挙げる。
・ ドメインロジックが持つ多くの技法(継承やストラテジ、デザインパターンなど)は使用できない。
・ 大きなレコードセットを扱ってしまうことがある。
・ 選択
3つのパターンからどうやって選ぶか。 主に、ドメインロジックの複雑度によって決まることと考えられる。
ドメインモデルはシンプルなドメインロジックに対しては魅力的ではない。 しかし、他の手法では、ドメインロジックが複雑になると飛躍的に難しくなる傾向がある。
図に、科学的ではないが3つのパターンに関するドメインロジックの複雑性と労力の関係を表す。
ドメインモデルに習熟したチームであれば、初期コストがかからないため、ドメインモデルを採用することが望ましいと考えられる。 テーブルモジュールの魅力は、共通のレコードセット構造を環境がサポートしているかどうかで異なる。 レコードセット用の多くのツールが存在する.NETの場合、とても魅力的である。
これら3つのパターンは、相互に排他的な選択肢ではない。 ドメインロジックの一部にトランザクションスクリプトを使用し、それ以外にテーブルモジュールまたはドメインモデルを使用することは珍しくない。
3. サービスレイヤ
ドメインレイヤを2つに分割する共通の手法がある。 サービスレイヤは、ドメインモデルまたはテーブルモジュールの上に配置される手法である。 プレゼンテーションロジックは、アプリケーションのAPIの役割を果たすサービスレイヤだけを通してドメインとやり取りする。
サービスレイヤは、明確なAPIを提供するだけでなく、トランザクション制御やセキュリティの確保のための最適な場所となりうる。 .NETでは属性で直接記述することができる。
振る舞いに関して2つのパターンが存在する。
サービスレイヤをファサードとするパターンでは、実際の振る舞いはすべて下位のオブジェクトにあり、サービスレイヤは、下位のオブジェクトに呼び出しを転送する。トランザクションラッパーやセキュリティチェックなどを追加するのに便利である。
次に、サービスレイヤ内に、トランザクションスクリプトを置き、シンプルなドメインモデルを呼び出すパターンがある。 ドメインモデルがシンプルなためデータベースと1対1の関係になり、シンプルなデータソースレイヤを使用することができる。
4. リレーショナルデータベースへのマッピング
現在のシステムの大半においてデータベースとはリレーショナルデータベースを指している。 このため、ドメインロジックがリレーショナルデータベースとやり取りするためのアーキテクチャに関するパターンを説明する。
4.1. ゲートウェイ
SQLをプログラミング言語に組み込む手法はあるが、チューニングやインデックスの配置などにかかわるトラブルが発生する。 このため、ドメインロジックとSQLアクセスを分離し、異なるクラスに配置することは懸命である。
これらのクラスを組織化する適切な方法は、データベースの構造に基づいて、テーブル毎に1つのクラスを持つように設計することである。 これらのクラスはそのテーブルに対するゲートウェイを形成する。 これにより、アプリケーションはSQLについて知る必要はなく、SQLはすべて容易に見つけられる。
4.2. 行データゲートウェイ
クエリから返された行ごとにインスタンスを持つオブジェクトである。 オブジェクト指向の考え方に自然に適合する手法である。
4.3. テーブルデータゲートウェイ
レコードセットを持つゲートウェイ。 テーブルデータゲートウェイは、データベースを検索し、レコードセットを返すメソッドを持つ。 また、ストアドプロシージャのコレクションをテーブル用のテーブルデータゲートウェイと考えることもできる。 この場合、テーブルデータゲートウェイは、ストアドプロシージャの呼び出しをラップする。
4.4. アクティブレコード
テーブルまたは行をラップし、データベースアクセスをカプセル化してデータにドメインロジックを追加するオブジェクト。 シンプルなドメインロジックでは、この手法は有効であ
ドメインロジックが複雑化し、ドメインモデルに移行する場合、アクティブレコードでは対応しづらくなってくる。
ドメインモデルが豊富になると、間接化が強要されてくる。 ゲートウェイで一部解決はできるが、結果としてゲートウェイのフィールドからドメインモデルのフィールドに変換されるため、ドメインオブジェクトが複雑化してくる。
これに対応する手法は、ドメインオブジェクトとテーブルとのマッピングを間接参照のレイヤに行わせ、データベースからドメインモデルを完全に分離させる方式である。
4.5. データマッパー
データマッパーは、データベースのドメインモデル間の読み込みと保存のすべてに対処し、両方の変更を独立して行うことができる。
データマッパーにより、ドメインロジックはデータベースが存在するかさえも知る必要はなく、SQLを使用したり、データベーススキーマの知識も必要ない。(データベーススキーマは使用するオブジェクトについて何も知らない。)
挿入や更新を行う場合、データベースマッピングレイヤは、どのオブジェクトが変化し、どのオブジェクトが新たに作成され、また、どのオブジェクトが削除されたかを知っておく必要がある。 この作業のため、ユニットオブワークパターンを後述する。
4.6. 振る舞いに関する問題
振る舞いに関する問題は、さまざまなオブジェクトをデータベースに読み込ませる方法と保存させる方法である。
アクティブレコードでは、読み込みと保存のメソッドを持つ。
メモリにオブジェクト群を読み込んで修正する場合、修正したオブジェクトを記録し、それらすべてをデータベースに書き戻さなければならない。 2,3のレコードを読み込むだけであれば、これは、容易である。 多くのオブジェクトを読み込むほど行うことが多くなる。 これは特に、行の作成や修正を行う場合に、参照する行を修正する前に作成された行のキーが必要になることなどからである。
オブジェクトを読み込んで修正する場合は、処理するデータベースが一貫性のある状態であるようにしなければならない。 他のプロセス・スレッドが同じオブジェクトを読み込んで変更することがないように分離する必要がある。 これについては、並行性の問題で後述する。
これらの問題の解決策としてユニットオブワークがある。 ユニットオブワークは、少しでも修正されたすべてのオブジェクトとともに、データベースから読み込まれたすべてのオブジェクトを記録する。 また、データベースの更新方法にも対処する。 プログラマは明示的に保存メソッドを呼び出すアプリケーションプログラムの変わりに、コミットする処理単位を設定する。 そして、この処理単位はすべてのデータベースに対する適切な振る舞いを順序付け、1箇所にすべての複雑なコミット処理を配置する。
オブジェクトを読み込む際は、同じオブジェクトを2度読み込まないように注意が必要である。 2度読み込むと1つのデータベース行に対応するオブジェクトが2つ存在することになる。 両方を更新すると問題が発生する。 これに対応するために一意マッピングで読み込んだ各行の記録を保持する必要がある。 データを読み込む際に、最初に一意マッピングをチェックし、読み込まれている場合は、そのデータに対する2つ目の参照を返すことができ、このことにより、更新も適切に調整される。
ドメインモデルを使用する場合、注文オブジェクトの読み込みが関連する顧客オブジェクトを読み込むというようにリンクされたオブジェクトが一緒に読み込まれるように配置することが多い。 しかし、多くのオブジェクトが連結している場合、膨大なオブジェクトをデータベースから抜き出すことになる。 この無駄を回避するため、抽出データは減らすが、その後の必要に応じて、データを抜き出すためのドアを開けておく。 レイジーロード(後述)は、オブジェクト参照用のプレースホルダーがあることを期待する。 レイジーロードでは、実際のオブジェクトを指す代わりに、プレースホルダーであるというマークをつけておき、リンクをたどろうとするとき、実際のオブジェクトがデータベースから呼び出される。
4.7. 構造的なマッピングに関するパターン
関係のマッピング
ここでの問題は、オブジェクトとリレーショナルデータベースのリンクに対処する方法の違いに関する違いであり、2つの問題がある。 1つは、表現の違いであり、オブジェクトがメモリ管理環境か実行時のメモリアドレスによって保持される参照を格納することでリンクに対処しているが、リレーショナルデータベースは、他のテーブルにキーを形成することでリンクに対処していることである。 もう1つは、オブジェクトが1つのフィールドから複数の参照を処理するために容易にコレクションを使用できるのに対し、正規化はすべての関連リンクを単一の値にすることを強要する。 注文オブジェクトは注文を参照する必要のない明細オブジェクトを自然に持っているが、テーブル構造はその逆で、注文は複数値フィールドを持つことができず、明細が注文への外部キーの参照を持たなければならない。
この表現の問題に対する対処法は、各オブジェクトのリレーショナルな一意性を一意フィールドとしてオブジェクトに保持し、オブジェクト参照とリレーショナルキーとの間を双方向にマッピングするために、これらの値を参照することである。
4.8. メタデータの使用
シンプルで繰り返されるマッピングを使用すると、シンプルで繰り返されるコードを作ることにつながる。 同じコードの繰り返しがあるということは設計に間違いがあるといえる。
共通の振る舞いを継承や委譲で処理するようにすることで多くのことが行える。(これは、有効で正当なオブジェクト指向のプラクティスといえる)
メタデータマッピングは、データベースの列をオブジェクトのフィールドにマッピングする方法を記述したメタファイルへのマッピングを集約することに基づいている。 メタデータにより、コードの生成、あるいは自己反映的なプログラミングのどちらかを使用して同じコードの繰り返しを回避することができる。
<field name=”customer” targetClass=”Customer” dbColumn=”custID” targetTable=”Customers” lowerBound=”1” upperBound=”1” setter=”loadCustomer”/>
このメタデータから読み込みと書き込みの定義、任意のジョインの自動生成、すべてのSQLの実行、関係の多重度の強制、参照整合性がある場合における書き込み順序の認識など、さまざまな実行が行える。
4.9. データベース接続
多くの環境では、接続の作成にかかるコストは高いので、接続プールを作成するほうがよい。 開発者は接続の作成と終了を行うのではなく、プールから接続を要求し、終了時に解放する。 さまざまなプラットフォームでプールが提供されているため、自分で作成することは稀である。 自分で作成する場合はパフォーマンス測定を行ったほうがよい。
プールを提供する環境では、新しい接続を作成するようなインターフェースの裏側にプールを置くことが多い。 このため、最新の接続なのか、プールから割り当てられたのかはカプセル化される。
接続はトランザクションと強く結びついているので、トランザクションと接続(プール)を関連つけて管理する方法がよい。 ユニットオブワークによって、自然と管理できる。
5. Webプレゼンテーション
モデルビューコントローラ(MVC)
モデルを完全にWebプレゼンテーションから分離できるメリットを持つ。(これは、プレゼンテーションレイヤとドメインレイヤの分離を意味する。) 現在では、一般的にこの手法を用いることが多い。 図にシーケンス図を示す。
アプリケーションコントローラ
Strutsなどのフレームワークが想定されるレイヤ。 Strutsでは、画面に対するアクション(ドメイン)・画面遷移・リクエストパラメータ・パラメータ検証などをコントロールすることが可能である。 これらの機能により、プレゼンテーションレイヤとドメインレイヤの分離を容易にしている。
補足
スクリプト形式
CGIスクリプトやServletでのレスポンス生成を行う方式。 プログラムですべてをコントロールする。
サーバページ形式
PHP,ASP,JSPなどのテキストページを返すことを主体とした形式。
5.1. ビューに関するパターン
3種類のパターンが考えられ、2種類の選択がある。
・ トランスフォームビュー
ビューの変換を行う。 通常はXSLTである。 これは、XMLフォーマットまたは容易にそれに変換できるドメインデータを扱っている場合に、有効である。 コントローラは、適切なXSLTスタイルシートを選別し、モデルから収集されたXMLに適用する。
XSLT:XML文書をHTMLやプレーンテキスト、任意のXML文書などへと自由に変換して出力するプログラム。
・ テンプレートビュー
ページの構造にプレゼンテーションを書き込み、ページにマークをつけて動的な内容が必要なところを示すことができる。 (ASP,JSP,PHP)
これは、多くの能力や柔軟性を提供するが、とても複雑なコードになることもある。 JSPの場合、これを軽減するためにカスタムタグライブラリなどの技術がある。
・ ツーステップビュー
ツーステップでビューを生成するパターンである。 図にシングルステップビューおよびツーステップビューのイメージを示す。
シングルステージビュー ツーステップビュー
ツーステップビューは、論理的なプレゼンテーションが同じで異なる画面の場合に有効と考えられる。
たとえば、同じ基本的な予約システムを使用する複数の航空会社などの複数のフロントエンドの顧客にサービスが使用されているWebアプリケーションが考えられる。 論理的な画面の限度内で、異なる第2段階を使用することでフロントエンドごとの外見を得られる。 また、Webブラウザと携帯端末に対し、別々の第2段階を用いたりする場合も考えられる。 (但し、現時点では携帯の能力がどうしても劣ることからUIが大きく異なる場合などには不向きではある。)
5.2. コントローラに関するパターン
・ ページコントローラ
ページごとのコントローラ。 ビューとコントローラの2つの役割を併せ持つパターンとなる。 シンプルなWebアプリケーションの場合、このタイプとなるケースが多いと考えられる。
・ フロントコントローラ
すべてのページのリクエストを処理するコントローラ。 URLを解釈し、適切なオブジェクトを生成し、処理を委譲する。 このパターンは、Webサイトのアクション構造の変更によるWebサイトの再構成を回避することができる。 Strutsなどもこのタイプであり、構成定義を行うことで構造の変更を行うことができる。
6. 並行性
6.1. 並行性の問題
複数のプロセスやスレッドが同じデータを処理するときは常に並行性が問題になる。 エンタープライズアプリケーションでは、特にデータベースおよびアプリケーションサーバが問題となる。
まず、本質的な問題を整理する。
もっともわかりやすい問題は、更新結果が失われることだろう。 たとえば、英志がファイルを編集してcheckConcurrencyメソッドを変更するとしよう。 このとき、和郎も同時に同じファイルのupdateImportantParameterメソッドを編集しようとしているとしよう。 和郎は英志よりも後に作業を始めてのだが、英志より早く作業が終わったとする。 ここで不幸が生じる。 英志がファイルを開いた時点では、和郎の更新は反映されていない。 そのため、英志がファイルの編集を終えたとき和郎が更新したファイルに上書きすることになり、和郎の更新は無に帰してしまう。
次に一貫性のない読み込みがある。 読み込んだ2つの情報は間違っていないが、正しいものではないという場合である。 たとえば英志が、lockingとmultiphaseの2つのサブパッケージを含む並行性パッケージの中にクラスがいくつあるか調べているとする。 Lockingパッケージを見るとそこには7つのクラスがある。 このとき、尚遠から電話があり、難しい質問をされたとしよう。 英志が電話に答えている間に和郎が4フェーズロックコード内のバグを修正し、7つのクラスがあったlockingパッケージに2つ、5つのクラスのあったmultiphaseパッケージに3つのクラスを追加する。英志が電話を終えてmultiphaseパッケージを見ると、そこには8つのクラスがある。したがって、英志の計算では7+8の15となる。 これは、正しい数ではない。 和郎が修正をくわえるまえのクラスの数は7+5の12で、修正後の数は9+8の17である。どちらもその時点では正しい数である。しかし、15はどの時点でも正しくない。 英志は一貫性のないデータを読み込んだことになる。
これらの問題は正確さ(または安全性)を脅かし、2人が同時に同じデータで作業しなければ起こらなかったかもしれない不正確な振る舞いを引き起こす。
正確さのみを重要と考えるならば、同じデータを2人で作業できないようにすれば問題は解決する。 しかし、正確さを確保できても並行して作業を行うことができない。 並行性プログラミングの重要な条件として、正確さに加えて即応性も重要である。 アクティビティをいくつ並行して処理できるかということも大事だ。 また、エラーの重要度、頻度、データの平行性の必要性にもよるが、即応性を重視して正確さを犠牲にすることもある。
6.2. 実行コンテキスト
システム内で処理が行われるとき、常に何らかのコンテキスト内、それも複数のコンテキスト内で処理が行われる。
外部との相互作用という観点では、2つのコンテキストが存在する。
リクエストとは、ソフトウェアが動作する世界の外部からの呼び出しのことであり、ソフトウェアはこれに対してレスポンスを返す。
セッションとは、クライアントとサーバが一定時間内に相互作用することである。 セッションはユーザからは一貫した論理シーケンスに見える一連のリクエストから構成される。 一般にセッションはユーザのログインで始まり、クエリーやビジネストランザクションの実行などさまざまな処理を行う。 セッションの最後にユーザはログアウトする。
エンタープライズアプリケーションでは、クライアントからサーバへと、サーバから別のシステムへという角度から見ることができる。 HTTPセッションと各種データベースなどへのセッションなど、複数のセッションが同時進行する。
OSから見るとプロセスとスレッドがある。 プロセスは重量級の実行コンテキストで、プロセスで扱う内部データを分離する。 スレッドは、軽量級のアクティブエージェントであり、1つのプロセス内で複数のスレッドが動作することができる。 現在、1つのプロセス内で複数のリクエストに対応でき、リソースを効率的に使用できるスレッドの方が好まれている。 しかし、スレッドはメモリを共有するのが一般的で、これが、並行性の問題を引き起こす。 CGIでは、リクエストごとにプロセスを開始していた。 しかし、プロセスを開始するには多くのリソースが必要なため、最近ではあまり使われなくなった。
データベースの場合は、トランザクションという重要なコンテキストがある。 トランザクションでは、クライアント(またはクライアントプログラム)からの複数のリクエストをあたかも1つのリクエストであるかのように扱う。
6.3. 分離性および不変性
分離性は、データを区切ってそこに1つのアクティブエージェントしかアクセスできないようにする方法である。 OSのメモリ内では、1つのプロセスに対しメモリを排他的に割り当て、そのプロセスがリンクしているデータの読み書きを実行できるようにしている。 このような例としては、Office製品でファイルがロックされるのを経験したことがあるだろう。 たとえば英志があるファイルを開いているときは、そのファイルは他の人は開けられなくなる。 開けた場合でも、英志が作業を始める前のファイルのコピーを読み取り専用で開くだけで変更することはできず、英志が加えている変更もそのファイルでは見ることができないのと同じである。
分離はエラーを起こしにくくするという意味でとても重要な技法である。 常に並行性に注意していなければならない技法を使用したために、問題が起こっている場合がとても多い。 分離を行えば、プログラムが分離された領域内に挿入される。 この領域内では並行性について懸念する必要はない。 つまり、優れた並行性設計とは、このような領域を作成する方法を模索し、できる限り多くのプログラムをいずれかの領域で行えるようにすることとなる。
並行性の問題は、共有しているデータが修正できる場合にしか発生しない。 つまり、不変データを認知することが、並行性のコンフリクト(衝突)を回避する方法の1つとなる。 だが、多くのシステムではデータの修正を主な目的としているためすべてのデータを不変なものにすることはなかなかできない。 そこで、不変データ、または不変であると認知したデータを特定することで、そのデータの並行性を考慮しなくても安心して共有できるようになる。 別の方法としては、データを読み込むだけのアプリケーションを分離して、そのアプリケーションにデータソースのコピーを使用させることで、並行性の制御の懸念をなくすることもできる。
6.4. 軽い並行性制御および重い並行性制御
並行性制御には大きく分けて軽い制御と重い制御の2つの形態がある。 英志と和郎がCustomerというファイルを同時に編集しようとした場合を想定しよう。 軽ロックでは両者ともにファイルのコピーを作成し、自由に編集することができるので和郎が先に編集を終えた場合、彼は自分の作業を問題なくチェックインできる。並行性制御が機能し始めるのは、英志が変更をコミットしようとしたときだ。 この時点で、ソースコード管理システムが、英志が加えた変更と和郎の変更とのコンフリクトを検出して、英志によるコミットは拒否され、その状況の処理は英志