本稿では、ユースケース間の関係の一つである requires(前提関係)を取り上げます。
requiresは、
- あるユースケースを開始するために必要な状態を明示する
- その状態を成立させるユースケースを関連付ける
- ユースケース間の依存を契約として表現する
ための関係です。
前回取り上げたtriggersが、出来事に反応して別のユースケースを起動する関係であるのに対し、requiresは、あるユースケースを開始する前に何が成立していなければならないかを表します。
requires
- マージモード : Prepend
- マージ点 : Before start
- 機能 : 事前条件統合
- 由来 :
- Requirements Engineeringにおけるprecondition
- Design by Contract
- Goal dependency
- Workflow prerequisite
requiresの基本方針
requiresは、
ユースケースの開始に必要な契約を、別のユースケースの結果へ接続する関係
です。
UseCase A が UseCase B を requires する場合、Aを開始するために、Bが成立させる状態または保証が必要であることを表します。
例えば、注文を確定する前に、
- 注文内容が検証されている
- 購入可能な商品だけが含まれている
- 金額が確定している
必要があるとします。
このとき「注文を検証する」を独立したユースケースとして定義し、「注文を確定する」がそれをrequiresする関係として記述できます。
重要なのは、requiresが単に
Aの直前にBを必ず呼び出す
という手続き的な関係ではないことです。
requiresが表すのは、
Aの開始時点で、Bが保証する前提が成立している必要がある
という契約上の依存です。
前提がすでに成立している場合にBを再実行するかどうかは、実行設計で決めます。
マージモードのPrependは、AとBから合成シナリオを構成する場合に、BをAの前へ配置する基本戦略を表します。ただし、requiresの契約上の意味と、毎回Bを実行するという実現方式は同一ではありません。
シナリオ技術としてのrequires
includeやextendはユースケース内部のフロー構造を扱います。
precedesはユースケース間の時間順序を扱い、triggersは出来事に対する反応を扱います。
requiresは、これらとは異なり、
あるユースケースを実行可能にするための前提
を扱います。
このためrequiresは、ユースケースを単に時間順に並べるのではなく、
- どの状態が必要か
- その状態を誰が保証するか
- 前提が成立しない場合にどう扱うか
を明確にするために使用します。
意味定義
UseCase A が UseCase B を requires するとは、
Aの開始に必要な条件の一部または全部を、Bの結果が成立させる
ことを意味します。
基本的な流れは次のようになります。
- Aの開始前にPre(A)を評価する
- Bが保証する状態に対応する前提を確認する
- 必要に応じてBを実行し、Post(B)を成立させる
- Post(B)がAの必要な前提を満たしたことを確認する
- Aを開始する
契約としては、次の関係を期待します。
Post(B) => RequiredPre(A)
ここでRequiredPre(A)は、Aのすべての事前条件ではなく、Bとのrequires関係によって満たされる部分を表します。
直観的整理
- requiresは「これを始めるには、先にこの条件が必要」という関係
- 対象は実行順序そのものではなく、開始時の前提
- 前提を保証する側と利用する側を接続する
- 前提が成立しなければ、対象ユースケースは開始しない
契約との関係
requiresでは、起動先の順序よりも、
- 要求する側のPre
- 前提を提供する側のPost
の対応が重要です。
UseCase A requires UseCase B の場合、望ましい関係は次の通りです。
- BのPostが明確に定義されている
- AのPreのどの部分をBが満たすのかが分かる
- Bが失敗した場合、Aを開始しない
- Bの結果がいつまで有効かを判断できる
例えば、認証結果や在庫確認結果には有効範囲や有効時間があります。
一度成立した結果を無期限に利用できるとは限りません。
requiresを設計する際は、
- 対象となる利用者や注文
- 状態を確認した時点
- 結果を利用できる範囲
も前提条件の一部として扱う必要があります。
requiresとensures
requiresは、前提を利用する側から見た関係です。
一方、ensuresは、ユースケースが結果として何を保証するかを表します。
両者は次のように対応します。
UseCase A requires Condition C UseCase B ensures Condition C
ユースケース間の関係として表現する場合は、
UseCase A requires UseCase B Post(B) satisfies RequiredPre(A)
と整理できます。
requiresだけを記述しても、前提を提供する側の保証が曖昧であれば契約は成立しません。
このため、requiresとensuresは、利用側と提供側から同じ契約を確認するための関係として扱えます。
記述例
Pieris Booksで、注文を検証してから注文を確定する業務を考えます。
usecase ValidateOrder
pre:
- 注文に商品が含まれている
post:
- 注文内容が検証されている
- 購入できない商品が含まれていない
- 注文金額が確定している
usecase ConfirmOrder
pre:
- 注文内容が検証されている
- 購入できない商品が含まれていない
- 注文金額が確定している
post:
- 注文が確定している
usecase ConfirmOrder requires ValidateOrder
この例では、
- ConfirmOrderの開始には検証済みの注文が必要
- ValidateOrderの事後条件がConfirmOrderの事前条件を満たす
- ValidateOrderが失敗した場合、ConfirmOrderは開始されない
という関係になります。
ConfirmOrderがValidateOrderのフローを内部に埋め込む、と決めているわけではありません。
すでに同じ注文に対する有効な検証結果が存在する場合は、その結果を確認してConfirmOrderを開始する実行方式も考えられます。
precedesとの違い
requiresは、precedesと混同されやすい関係です。
どちらもユースケースの前後に関係しますが、モデル化している内容が異なります。
| 観点 | precedes | requires |
|---|---|---|
| 本質 | 実行順序 | 開始条件 |
| マージモード | Sequence | Prepend |
| マージ点 | After end | Before start |
| 関係 | Aの後にBを行う | AにはBの保証が必要 |
| 主な契約 | Post(A)とPre(B) | Post(B)とRequiredPre(A) |
| 前提が成立済みの場合 | 順序に従って実行 | Bを再実行しない場合がある |
A precedes B
は、Aの完了後にBへ進む時間的な順序を表します。
A requires B
は、Aの開始にBの結果が必要であることを表します。
したがって、
- 一連の業務手順として必ず順番に進むなら precedes
- 対象ユースケースの開始条件を別のユースケースが保証するなら requires
と考えると分かりやすくなります。
実際の業務では、同じ二つのユースケースに順序と前提の両方が存在する場合もあります。
その場合も、時間的な関係と契約上の依存を分けて考えることが重要です。
includeとの違い
includeは、別のユースケースのフローを対象ユースケースの内部へ組み込みます。
requiresは、対象ユースケースの外側にある前提を表します。
| 観点 | include | requires |
|---|---|---|
| 対象 | 内部フロー | 開始前の契約 |
| 関係 | 動作を組み込む | 必要な状態を要求する |
| 実行 | 原則として含まれる | 前提成立済みなら省略可能 |
| 結合 | フロー構造として強い | 契約を介した依存 |
例えば、注文確定の処理そのものに注文検証の手順を常に含めたい場合はincludeとして設計できます。
一方、どこで検証されたかにかかわらず「検証済みであること」を開始条件とする場合はrequiresが適しています。
triggersとの違い
triggersは、イベントの発生によって別のユースケースが起動候補になる関係です。
requiresは、イベントの有無ではなく、開始条件が成立していることを要求します。
ValidateOrder triggers ConfirmOrder
と書くと、注文検証の結果として発生したイベントにConfirmOrderが反応する意味になります。
ConfirmOrder requires ValidateOrder
と書くと、ConfirmOrderを開始するにはValidateOrderの保証が必要であるという意味になります。
前者は起動の契機、後者は開始の可否を表します。
実行時の扱い
requiresはモデル上の契約関係であり、前提をどのように成立させるかは実行設計で決めます。
代表的な方式は次の三つです。
確認型
開始時に前提の成立を確認し、成立していなければエラーにします。
- 前提は別の業務や利用者操作で成立する
- 対象ユースケースから前提ユースケースを起動しない
- APIやコマンドの境界で契約違反を検出する
場合に適しています。
必要時実行型
前提が成立していなければ、前提ユースケースを実行します。
- 前提を対象処理の直前に確立できる
- 実行しても業務上不自然な副作用がない
- 前提ユースケースの失敗を対象ユースケースへ伝播できる
場合に適しています。
事前準備型
前提ユースケースを別の時点で実行し、その結果を保存しておきます。
対象ユースケースは、保存された結果の有効性を確認して開始します。
- 審査
- 在庫確認
- 承認
- 本人確認
のように、準備と本処理の間に時間差がある場合に適しています。
どの方式でも、Aの開始時点でRequiredPre(A)が成立している、というrequiresの意味は同じです。
複数のrequires
一つのユースケースが複数の前提を必要とする場合があります。
usecase ConfirmOrder requires ValidateOrder usecase ConfirmOrder requires AuthorizePayment
この記述は、
- ValidateOrderの保証
- AuthorizePaymentの保証
の両方がConfirmOrderの開始に必要であることを表します。
ただし、requiresだけでは、
- どちらを先に実行するか
- 並行して確認できるか
- 一方の失敗時に他方を取り消すか
までは決まりません。
前提同士に順序が必要な場合はprecedesなどの関係で明示し、並行実行や補償は実行設計で扱います。
同期実行と非同期実行
requiresの前提確認は、対象ユースケースを開始する前に完了している必要があります。
しかし、前提を成立させる処理そのものが、対象ユースケースと同じ同期処理の中で実行される必要はありません。
例えば、審査ユースケースを非同期で実行し、その結果を後から利用することもできます。
この場合、
- 審査を開始する処理
- 審査完了イベント
- 審査済み状態
- 審査済み状態をrequiresするユースケース
を分けてモデル化します。
requiresが表すのは、あくまで対象ユースケースの開始時点における前提の成立です。
他の関係との違い
| 観点 | include | extend | precedes | triggers | requires |
|---|---|---|---|---|---|
| 対象 | フロー構造 | フロー変形 | 実行順序 | 起動条件 | 開始前提 |
| 本質 | 構造合成 | 条件付き拡張 | シナリオ連結 | イベント駆動 | 契約依存 |
| マージ点 | 任意Step | After指定 | After end | After condition | Before start |
| 前提成立済みの場合 | 関係なし | 条件による | 順序に従う | イベントに反応 | 再実行を省略可能 |
つまりrequiresは、
別のユースケースの動作を組み込む関係ではなく、その結果を開始契約として利用する関係
です。
設計上の注意点
requiresを使用する際は、次の点に注意します。
- 必要な前提を業務上の状態として明確にすること
- BのPostとAのPreの対応を確認すること
- 前提が成立しない場合の振る舞いを決めること
- 前提の対象、有効範囲、有効時間を明示すること
- requiresの循環を作らないこと
- 単純な入力値検証をすべて独立ユースケースにしないこと
特に、
A requires B B requires A
のような循環は、どちらも開始できないモデルになります。
複数段のrequiresを使用する場合は、依存グラフとして開始可能性を確認する必要があります。
また、単なるライブラリ関数や内部メソッドへの依存をrequiresとして表現すると、ユースケースモデルが実装詳細で埋まります。
requiresの対象は、
- 業務上意味のある前提
- 独立して契約を定義する価値がある処理
- 複数のユースケースから参照される保証
を基本とします。
まとめ
requiresは、
- ユースケースの開始に必要な前提を表す関係
- 要求する側のPreと、提供する側のPostを接続する関係
- フローの埋め込みではなく、契約を介して依存を表現する仕組み
です。
precedesが時間的な連続性を扱い、triggersが出来事に対する反応を扱うのに対し、requiresは、
ユースケースを開始できる条件
を扱います。
requiresを使用することで、
- 注文確定前の注文検証
- 出荷前の在庫引当
- 契約前の審査
- 操作前の承認
のような依存を、単なる呼び出し順ではなく契約として表現できます。
次回は、ユースケースの完了によって何を保証するかを表すensuresを取り上げます。
