見出し画像

概念モデリングチュートリアル ~ ホテルのコインランドリー

本ドキュメントの利用は、https://github.com/kae-made/kae-made/blob/main/contents-license.md に記載のライセンスに従ってご利用ください。

https://github.com/kae-made/kae-made/blob/main/contents-license.md

先日の長旅の途中で宿泊したホテルでコインランドリーを使って思いついたチュートリアル。

筆者はテーマが思い浮かぶと、勝手に頭の中で、ドメイン定義、概念モデル構築が始まるレベルなので、今となっては、なかかなに概念モデリングの初学者の引っ掛かりどころが霞んでしまっているのだが、なんとか、そのような読者が参考になるように、ある主題から概念モデリングが進んでいく上での、思考の流れを紹介するようなチュートリアルを作ってみようと思う。

そもそものお題

そのホテルのコインランドリーの洗濯機は、暗証番号でロックが可能だった。しかし、掃除の申し込みなどは部屋のTVからできるのに、何故、連動しないの?と。
カードキーでロックすりゃいいじゃんと。
部屋から利用状況も確認できればいいじゃないと。
終わったら、部屋に連絡があればいいじゃないと。
終わったのに撤収しない客もいるから、ホテル側がどの洗濯機を利用しているか把握していて、対処したほうがサービス的にもいいよねと。
じゃぁ、このテーマで、宿泊客にもホテル側にもメリットのあるITシステム向けの概念モデリングをやってみるか!
諸事情が許せば、実際にシミュレーション可能なソフトウェア一式も作ってみよう!
ってことで、さっそく始める。

脳内検討

モデリングに入る前に、以下のようなことをもやもやと考えていた。

  1. カードキーを使ってのロック、これ、要はユーザーが宿泊している事の証明だから、別にスマホとかでもいいんじゃないか?いや、スマホ持ってない客もいるだろうから、カードキー限定で行こう。
    ⇒ビジネスシナリオとモデリング時の抽象度

  2. シナリオを広げると、ホテルのコインランドリーでなくても、街中のコインランドリーでもいいんじゃないか?
    ⇒ビジネスシナリオ(客とホテルのWin Winを考えるので、今回はホテルに限定)

  3. 事前に部屋で、洗濯機の空き状況が判ると便利。予約できると更に便利。予約して忘れている客がいるかもしれないから、何か対策が必要
    ⇒ビジネスシナリオ

  4. 状況確認の方法。予約の仕方も考えないと。
    ⇒ビジネスシナリオとドメイン分割
    ⇒部屋のTVを使うのは決まり。あとは、少なくともメール受信ぐらいはいいんじゃないか?

  5. ホテル側としては、稼働率とかメンテナンスタイミングとか最適化したいよな。
    ⇒ビジネスシナリオとドメイン分割

などなど。それぞれに付記した通り、ほとんどはビジネスシナリオをどう定義するかのお話。これは決めの問題(実際にやるためにかかるコストとやった時に得られるであろうメリットのバランス)。チュートリアルなので、この辺は作者の気分次第で決める。
4.については、使用状況、予約等は、「ホテルの他の貸し出し備品やサービス(例えば貸切露天風呂とか)などでも仕組みは再利用できるだろうから、別ドメイン(HUIドメイン)としてモデル化することにする」というドメイン分割の問題と、ビジネスシナリオとして、部屋のTVだけだと外出しているときに完了通知などが客に行かないので、スマホ無くてもガラケーぐらいは持っているだろうと、ということと、”会員登録すると使える”って事にして、「メールアドレスぐらいは事前登録されている」ということで話を進める。加えて、洗濯機も当然、ホテルのITシステムとやり取りしないといけないので、そのための仕組みが洗濯機側に必要。これは、別ドメイン(洗濯機ドメイン)としてモデル化する。
5.は、今時の派生シナリオとして絶対出てくるよな的ではあるが、多分、それをカバーしなくても、それを行うための基盤的な概念モデルが結局出来上がるだろうから、とりあえずは考えずに概念モデルを作成して、一段落したら、その時点で検討することにする。
案外大事なのが、1.。カードキーを、この概念モデル化対象として本質的な概念として扱うかどうか微妙。本質は、「その人がホテルの特定の部屋に宿泊している事をホテルの館内に設置された設備に対して証明できる何か」なので、別にカードキーでなくてもよいから。
※ 概念モデル作成時はこういく感覚が重要
しかし、まぁ、普通大抵のホテルでカードキーあるし、ホテル内の備品に対して、自分は宿泊客であり、特定の部屋にいることを示すには、うってつけの具体的アイテムだということで、カードキーは概念クラスの候補として採用することにする。
といったような、様々な事をあーでもないこーでもないと考えるのが最初の一歩。チームでやる場合は、もちろん、ホワイトボードなどを使ったワイワイやりながらのブレーンストーミングを行う。

概念モデルを作成する

モデル化対象に関する最低限の情報が集まったら(集めながらでも全然かまわない)、概念モデルを作成を開始する。先ずは概念情報モデルから。

初期段階の概念情報モデル作成

てなわけで、前述の主題領域をもとに、作業を開始することにする。
絵が下手(恥ずかしいw)だが、

主題領域のイメージ

とりあえず、ポンチ絵を書きながら、メンバー間でイメージを共有する。宿泊している部屋からの状況確認、予約もイメージのポンチ絵を挙げておく。

部屋から確認・予約

イメージを共有したら、概念情報モデルを作成し始める。
Art of Conceptual Modeling」で紹介している概念情報モデルは、全て、PowerPoint を使って描いている。しかしこの方法では、描くのも修正するのも、出来上がった図をデジタル的に再利用するのも難しいので、実際の開発では推奨されない。このチュートリアルでは、概念モデリングのベースになっている xtUML(eXecutable and Translatable UML:旧 Shlare-Mellor 法)の開発者コミュニティがオープンソースとして公開している BridgePoint を使ってモデルを作成する。インストール方法等は、Qiita のブログを参照していただきたい。概念情報モデルのコンセプトを理解していれば、それほど苦労せずに基本的な操作方法は想像できるだろう。筆者が引っ掛かった操作方法は都度解説を入れておく。
先ず、物理的にはっきりしているところから始めると、

ホテル - コインランドリースペース - 洗濯機

こんな感じで、モデル化が可能であろう。通常のホテルはコインランドリースペースがあってもせいぜい一箇所だけではないかと思うが、本館、別館があるような大きなホテルの場合、複数あってもおかしくないということでR1の多重度を定義している。概念クラス名は英語で定義しているので、誤解ないように、それぞれの概念クラスの意味をリスト化しておく。

  • Hotel ホテル

  • Laundromant Room コインランドリーの洗濯機が置いてある部屋

  • Washing Machine 洗濯機

Relationship の定義方法が判りにくいので説明をしておく。BridgePoint の Window の右上のパレットをクリックし、Classes → Association と選択すると、アイコンの形が変わるので、二項 Relationship を結びたいクラスの一方から他方へ線を引くと、Relationship が定義できる。

※ BridgePoint は、UML の表記・用語に従ったツールなので、教本の用語と若干異なりますが、宗教的なこだわりは捨てて適宜読み替えてください。

次に、宿泊客に関連する概念群をモデル化し図に追加する。

左側の4つのクラスが、追加した宿泊客周りの概念クラスである。

  • Guest Room
    それぞれの宿泊部屋

  • Guest
    宿泊客。二人が同じ部屋に宿泊するなら二つの概念インスタンスがそれぞれ対応する。

  • Guest Stay
    宿泊客の宿泊を概念として抽出したもの。一般的にホテルの宿泊部屋には1人からその部屋に収容可能な最大人数までの間の人数の宿泊客が利用する。同一の部屋に宿泊している客は全て、ホテル内のコインランドリーの洗濯機を使う権利を有しているが、その権利の発生要因は、ホテルの宿泊部屋にあると考えられるので、この様な概念クラスを定義した。

  • Card Key
    宿泊部屋のドアを開ける鍵。前述の抽象度の問題は含むものの、カードの形をしている事から生じる様々な固有シナリオがあると思われるので、敢えて、そのまま概念クラスとしている。

以上、4つの概念クラスが追加され、4つの Relationship も加えられた。それぞれの Relationship の意味を読者の皆さんは吟味いただきたい。R6 の Card Key 側の多重度は特に恣意的に決めている。

Relationship の多重度を決め方には、”必然的”と”恣意的”の二種類がある。前者は、概念モデリング対象の主題領域の特性から合理的、物理的な必然性で決まるものであり、後者は、ビジネス上の要件や一般的なシナリオ、あるいはモデル作成者の好みで決まるものである。後者の多重度を意図的に変えながら吟味すると、見落としていた知見や新たなビジネスモデルの発見につながるようなアイデアを発見できる可能性がある。

次に、ある宿泊客が洗濯機を実際に今使っている事を示す概念をモデル化する。

とりあえずの追加

図は、Guest が関係しているGuest Stay インスタンスと、現在使用中の Washing Mashine インスタンスを表現する R7 を追加しただけのモデルである。このモデルは、ある宿泊部屋に泊まっている客がどれかの洗濯機(多重度が*なので、同時に2台以上使う事が許容されている。加えてGuest Stay側の多重度が 0..1 なので、同時に使えるのは唯一つの Guest Stay であるという物理的に当たり前の制約を表している)を使っていることは示しているものの、

  • その利用においてどれだけの洗濯時間・乾燥時間がかかるのか全く情報がない。

  • ”利用している”という概念自体が、このモデルには存在しないため、利用に関わる情報をプロパティとして定義する場所がない

等の問題を含んでいる。この様な場合は、Guest Stay と Washing Machine の間の Relationship に紐づく Relationship Class を定義するのが妥当である。

利用中と利用に関する仕様を追加

結果として上図の様にモデルを修正する。追加された Washing Machine in Use クラスのインスタンスは、Guest Stay インスタンスが実際に Washing Machine を使っている時だけ存在し、洗濯機が使われている事を表す。この図には記載されていないが、例えば、洗濯の残り時間を保持したい場合には、このクラスのプロパティとして定義するのが妥当であろう。
また、大抵のコインランドリーの洗濯機は、洗濯物の重量を目安にした、洗濯時間、乾燥時間に応じた課金が行われるので、それらを仕様パターンでモデル化している。具体的には、

  • Washing Spec
    ホテルが有料サービスとして提供する、重量を目安とした洗濯時間、乾燥時間の組合せと利用料金

  • Available Washing Spec
    設置されている洗濯機の機種が異なる場合、あるいは、特定の事由により、選択可能な洗濯時間・乾燥時間のリストは変わる事を考慮して、それぞれの洗濯機で選択可能な Washing Spec インスタンスが変更可能なようになっている。また、洗濯機の利用を概念情報モデル的に表現すると、「この概念クラスのインスタンスを一つだけ選択しR9 でリンクされている」、ことになる。

だんだんモデルが込み入ってきたので、具体的な現実の情景を想定して、インスタンス群とリンクがどのようになるか、また、それらが現実を正確に表しているか等、妥当性を検証するとよい。

次に、事前予約に関する概念を追加する。事前予約の為には、現時点、あるいは、未来のある時点で、洗濯機が利用可能か、を判断するための情報が必要である。これを踏まえ、

の様に、モデルに、”Washing Mashin Reservation” というクラスと、R12、R13 という Relationship を追加する。利用中と同様、予約の Target は、Washing Mashin そのものではなく、洗濯時間・乾燥時間が明確になっているものでなければならないだろうということで、R13 が定義され、予約主体も同様な考えで、R12 が定義されている。

以上で、一通りの概念がほぼ入れ込まれたモデルが出来上がった。

概念情報モデルの形式度を上げる

お絵描きレベルのモデルであれば、(若干足りない情報もあるが)最後の図で十分であると思われるが、作成したモデルを電子的に後工程で利用するならば、もう一段のモデリングが必要になる。
現時点のモデルには、概念インスタンスの一意性を担保するための情報と、Relationship の妥当性を保証するための情報が存在していない。
前者については、一意性を持つ概念クラスのプロパティへの Identity付与を行い、後者は、Relationship のフォーマライズを行う。

BridgePoint 上での操作は、前者は、Model Explorer で、該当する Attribute を選択して右クリックして表示される、”Add to identifier"で行い、後者は、Model Editor 上で Relationship を選択し、右クリックすると、"Formalize…” が表示されるので、それを利用する。

識別子、Relationship の形式化

 結果として上図のように、Attribute の右側に識別子や Relationship が明記される。

概念情報モデルのチェック

概念モデル作成中には、適度なタイミングで現実の状態を正しくデータとして保持できるか、足りないものはないかをチェックしていくとよい。
現時点で作成したモデルをざっと眺めると、宿泊客が洗濯機を使った時の利用金額が保持されないことを見つけたので、どこか適切な場所に追加が必要になる。以降の図にはプロパティとして追加されるので、どこに追加されたか確認とその意味を考えていただきたい。
また、洗濯機のドアの利用開始時のロック、洗濯が終わって洗濯物を回収する時のアンロック、に関する情報もない。こちらは、便宜上、概念クラスとして追加することにする。
また、チュートリアル的には、スーパー・サブの Relationship もあった方が良いなとの判断から、予約可能な洗濯機と予約不可能な洗濯機の2タイプがあるという主題を追加して、モデルを修正することとする。
結果として、

一応の完成版

の様にモデルを修正した。前述の修正項目に加えて、”Washing Machine Reservation” に R17 を加え、インスタンスは時間重複しないことを印象付け
るように変更している。
"Door with Lock" クラスの PinNumber は、ドアロックを解除するための暗証番号を保持する。当初カードキーを近づけるだけでロックが解除されるという仕様を思い描いていたが、宿泊中のカードキーの紛失や盗難による悪意ある第三者の開錠もありうるのではないかという検討のもと、カードキーと利用者が使用開始時に設定する暗証番号も加えた方が防犯上良いと判断し、恣意的に加えた概念になっている。

"Washing Machine in Use" のプロパティの、MachinIDに、{I2, R16, R9}という制約が記載されている。これは、R9、R16 をフォーマライズした際に追加された2つのプロパティ(意味的に値は同じはず)をまとめたものである。Model Explorer で、まとめたい Attribute のどちらかを選択し、右クリックすると、"Combine"という項目が出てくるので、それを選択してマージすることができる。

以上で、本チュートリアルの流れとして、概念情報モデルは一応の完成とする。この時点でのモデルを、Github で公開しているので、実際に BridgePoint で開いてみてほしい。
artifacts-laundromat-in-hotel-tutorial/model/LaundromatInHotel at 20220530 · kae-made/artifacts-laundromat-in-hotel-tutorial (github.com)

さて、特徴値の Identity 宣言や、Relationship のフォーマライズというトピックスをサラッとぶち込んだが、「なにもの?」という読者も多くいるだろうし、特に、Java や C++、C#といった強い型付け志向のオブジェクト指向プログラミングに慣れているプログラマーということで、解説を加えておく。「Art of Conceptual Modeling」でも解説している通り、概念情報モデルは、現実世界の写しであり、概念クラスのインスタンスは現実世界に存在する有形無形問わず、主題として重要な個々の存在に一対一対応していて、何をやるにも、ベースとしてその存在一つ一つを認識、つまり識別しなければならない。Identity宣言された特徴値(BridgePoint では Attribute)は識別の為の情報の保存の場である。概念情報モデルで定義された概念クラスのインスタンス群で現実世界の写しは、概念クラスに定義された特徴値を列、それぞれのインスタンスを行とした表で表現できる。それぞれのインスタンスは、現実世界の異なるそれぞれの存在なので、現実世界の存在に対応するインスタンス群で表を埋めた場合には、現実世界の各存在は、表中のどれか一行に対応することになる。Identity宣言された特徴値(またはその組)は、表にその行が記載されてから削除されるまで変更してはいけないし、全ての行は異なる値でなければならない。この様な縛りを、”One Fact in One Place”とも言う。この様な制約を、概念モデル構築後に延々と続くシステム開発において課すことにより、現実世界の情報のシステムへの入力時のご入力や開発者の勘違いによる実装を防止する事に役立つ。
次に、Relationship のフォーマライズであるが、まず、

Binary Relationship

R3 に関係している特徴値を取り上げる。この Binary Relationship は、"Hotel" と "Guest Room" という二つの概念クラスの間で定義されている。それぞれの概念クラスは、現実世界のホテル客室に対応し、現実世界に存在する個々のホテルや客室は、それぞれのインスタンスとして表現される。この情報を保持するには、それぞれの特徴値を列とする二つの表が必要になる。実際に現実の世界の個々のホテル、客室をそれぞれの表の行として埋めていくと、"Guest Room" の "HotelID" が "Hotel" の表に存在するどこかの行の"HotelID" の列の値と同じ値を保持している事によって、どの客室がどのホテルに存在するのかが判断できることが理解できるだろう。"Guest Room" の "HotelID" が無い場合を考えてみてほしい。二つの表からこの所属関係を見つけることは不可能である。Relationship のフォーマライズは、そのような役割を持つ表の列がどれかを明確に宣言することを目的としている。

Super Sub Relationship

次に、R15 を取り上げる。この Relationship は、モデルズ上に記載はないが、概念情報モデリングの定義上、{complete, disjoint} 制約で定義されている。具体的には、洗濯機(Washing Machine)が一台あった場合(Washing Machine 概念クラスの定義から作った表の一行)、それは、「予約可能な洗濯機(Reservable Washing Machine)か、予約不可の洗濯機(Non Reservation Washing Machine)のどちらかですよ」という表明である。どちらかかに合わせて、"Washing Machine" の表の一行に対応する行が、"Reservable Washing Machine" または、 "Non Reservation Washing Machine" の表のどちらかの一行として保持されることになる事を意味し、その情報の保管場所の特徴値を明確化している。

Combine attributes

最後に、"Washing Machine in Use" の、"GuestStayID:unique_id {I2,R16}" と "MachineID: unique_id {I2, R16, R9}" を取り上げる。この概念クラスも現実世界の洗濯機の利用状況を保持するための表が作成される。R16 は、ある部屋に泊まっている客が、現在、ある洗濯機を利用している時にリンクが張られ、利用中であることを示す。GuestStayIDMachineIDには、I2 とマークされており、この制約があることによって、一台の洗濯機は同時に一つの部屋に泊まっている宿泊客だけにしか使われないという現実世界の当たり前の事実を満たすことができる。I22がついているのは、このクラスが、別に I とマークされた "UseID"という特徴値を持っていて、こちらが一次識別子、GuestStayIDMachineID が二次識別子だよ、という意味である。厳密に言えば、UseID は別になくても構わない特徴値でもある。UserID が無い場合、I2 は単に I となる。次に、"MachineID: unique_id {I2, R16, R9}" のR16R9 の添え字を説明する。これは、2つの Relationship が関連している事を表明している。元々、"Washing Machine in Use" の特徴値には、R16 をフォーマライズした特徴値と、R9 をフォーマライズした特徴値(どちらも MachineID)が、現実世界の洗濯機利用に関する情報を保持するために、存在しなければならない。R16 は、…

と書いてきて、現状のモデルが間違っている事を著者は発見する。
普通なら、前に戻って間違った部分を全て書き直し、何事もなくあたかも初めから正しいモデルを作っているんだよ…と偽装することも可能だが、チュートリアルでもあり、モデリングのリアルを伝えたいので、間違いを修正したモデルを以下に挙げ、説明を続ける。Super Sub Relationship までの説明はそのまま正しいので気にせず理解していただきたい。

間違いを修正した概念情報モデル ~ Combine 前

以前のモデルと比べて何が変わったか、以前のモデルの何が間違っていたか、読者自身で考えていただきたい。ポイントは宿泊客が利用可能な洗濯機の範囲である。まぁ、Non Reservation Washing Machine の定義次第で、以前のモデルも、あながち、完全に間違いという事でもないのではあるが…このボヤキの理由も読者自身で考えていただきたい(苦笑)。

では、最初に戻って…

R9、R18 周りの拡大

最後に、"Washing Machine in Use" の R9 と R18 のフォーマライズを取り上げる。この概念クラスも現実世界の洗濯機の利用状況を保持するための表が作成される。
どんな洗濯時間・乾燥時間で使っているかを示す R9 の Relationship のフォーマライズの結果、"MachineID:unique_id {I2,R9}" という特徴値が定義されている。I2 は、「2番目の Identity ですよ」という意味で2が添え字されている。I、I2 は独立していて、それぞれが、表内の全行で一意であることを意味する。
※ 何故、この特徴値がIdentity 足りうるのか、読者自身で考えていただきたい。現実世界を想像すれば理由は判明する。
2番目ということが1番目があるということで、該当するのが "UseID:unique_id" である。厳密に言えば、この特徴値は必須ではなく削除しても構わないが、チュートリアルの便宜上残しておく。
R18 は、ある部屋に泊まっている客が、現在、ある洗濯機を利用している時にリンクが張られ、利用中であることを示す。この Relationship をフォーマライズした結果として、"GuestStayID: unique_id {I3, R18}" と "MachineID:unique_id {I3, R18}" の定義が生み出される。
※ この二つの組には、I3 つまり、3番目の Identity を意味する。一意性は I2 の時と同じである。何故、Identity 足りうるのかも、読者自身で考えていただきたい。一台の洗濯機を同時に使えることに関する現実の制約を考えればよい。
さて、MachineID という名前の特徴値が二つ、一つの概念クラスに定義されてしまっている。これは、概念情報モデルの定義として間違っているので修正が必要である。現実世界の制約を、作成した概念情報モデルを使って、よくよく吟味すると、このチュートリアルの主題領域では、二つの MachineID は必ず同じ値になる事が判る。
※ こちらも、何故同じ値になるのか、読者自身で考えていただきたい。
同じ値になるので、BridgePoint の GUI機能を使って、二つの MachineID を一つにまとめる。結果として、モデル図は、以下のようになる。

Combine 後の概念情報モデル

最終的に"MachineID:unique_id {I2,I3,R18,R9}" という特徴値(BridgePoint 的には Attribute)が出来上がる。この概念クラスを元に表を作って現実世界のあり様を情報として保持する場合、最初の場所と最小の更新・参照が保証される。
念のため言及しておくが、このケースでは、モデル化対象の現実の世界が同じ値をとることを合理的に保障しているから二つの特徴値を Combine したのであって、異なる値をとりうる場合には、どちらかの名前をモデル化対象の現実の存在を吟味してふさわしい名前に変更しなければならない。

以上、修正版は、artifacts-laundromat-in-hotel-tutorial/model/LaundromatInHotel at 20220530_1 · kae-made/artifacts-laundromat-in-hotel-tutorial (github.com) から公開しているので、実際に BridgePoint で開いて確認していただきたい。

概念振舞モデル

概念情報モデルが一通りできたところで、次は、振舞モデルを作成していく。概念情報モデルが現実世界に存在する有形無形のモノ・コトに関する情報を保持するデータ構造の定義であるのに対し、振舞モデルは、概念情報モデルが定義するデータ構造を基盤に、モデル化対象の主題領域で発生する様々な事象を元に、どのように保持されたデータが変化していくかを定義する役割を持っている。詳しくは、「Art of Conceptual Modeling」の「概念振舞モデル」の章を参照のこと。
概念情報モデルで定義した概念クラスのうち、主題領域において発生する事象をきっかけに状態が変化する概念クラスをピックアップし、その概念クラスに対する状態モデルを作成する。

概念クラスに対して作成する状態モデルは、概念クラスを雛形に存在する全ての概念インスタンスの事象に対する状態変化の雛形であることを忘れないように。状態モデルを持つ概念クラスの概念インスタンスは、生成されると、状態モデルを雛形にしたそれぞれの状態機械を持ち、事象の発生に応じて状態遷移を起こし、定義されたアクションを同時並行的に実行していく。

洗濯機はそれぞれの状態機械を持つ

念のため、振舞モデルを作成するベースとなる現実世界のシナリオをざっと挙げておく。

  • コインランドリー部屋に設置された洗濯機にカードキーをかざし、洗濯機の扉を開け、衣類をぶち込み、洗濯時間、乾燥時間を選択し、洗濯完了時点でドアロックを開錠するための暗証番号を入力して洗濯を開始する。

  • 洗濯終了時、宿泊客にその旨通知されコインランドリー部屋に取りに行く。

  • 洗濯機にカードキーをかざし、暗証番号を入力して開錠し洗濯物を取り出し、ドアを閉める。

  • 宿泊部屋の オンデマンド機能付き TV 等で洗濯機の空き状況が判る。

  • 所望のタイムスロットで空きがある場合は洗濯機の予約ができる。

  • 予約時間ちょっと前と予約時間になった時点で宿泊客にその旨通知され、洗濯機の利用が可能になる。

以上を踏まえ、前述の観点からピックアップした概念クラスを以下のリストに示す。

  • Washing Machine

  • Reservable Washing Machine

  • Door with Lock

  • Washing Machine Reservation

  • Washing Machine Assigner

※ 振舞モデル作成の初期の時点では、状態モデルを持つ概念クラスの選定の正しさをそれほど深く気にしなくてもよい。モデル作成の過程で、なんとなくしっくりこないとか、間違い等発見するたびに修正していけばよい。様々な主題領域のモデリングをこなしていくと、ある程度は適切な選択ができるようになる。
※ 振舞モデル作成中、概念情報モデルの不備(概念クラスや特徴値、Relationship の過不足や間違い)が見つかる。一部修正で済むならその場その場で修正していけばよい。ただし、頻繁に大幅に修正が発生するようであれば、一旦、振舞モデルの作成は止めて、振舞モデル作成中に新たに得た知見を元に概念情報モデルを見直し、修正し、ラップアップした後に、再度、振舞モデルを作成するとよい。

以下、BridgePoint で作成した状態モデルを図に示しておく。

Washing Machine
Reservable Washing Machine
Door with Lock
Washing Machine Reservation
Washing Machine Assigner

念のため、状態モデルの記法を解説しておく。角が丸い四角は状態を表す。矢印は、状態間の遷移を表す。ある概念インスタンスの状態機械が、ある状態のとき、矢印に添付された事象(イベント)が発生すると、矢印の先の状態への遷移を引き起こす。遷移が発生すると状態を表す四角の中の "entry/" で定義されたアクションを実行し、実行が完了すると状態遷移が確定する。

※ BridgePoint が提供する状態モデルエディタは、UML の State Machine の記法に従っている。UML の State Machine と言えば、階層化された状態マシンや、Do や Exit などのアクションが定義できるなど、なんでもありの仕様となっていて、それを知ると、なんだかすべて使って状態モデルを描かなければならないような強迫観念に陥りがちであるが、概念モデリングの体系での状態モデルの使い方(概念情報モデルを基盤とした概念情報インスタンスの状態モデル)では、階層化無し、Entry アクションのみで十分であることが経験的に分かっている。ただでさえ複雑な現実世界の様相をモデル化で、複雑なモデリングの道具立てを持ち込む必要はない。

上に挙げた図の各状態の Entry アクションに、遷移が発生してから遷移が確定するまでに行うアクションの概要を記載している。
また、BridgePoint では状態遷移表も定義できるので、Can't HappenIgnore など、現実世界の様相を踏まえて定義しておく。参考までに、Washing Machine Reservation の状態遷移表を図に挙げておく。

参考 ~ Washing Machine Reservation の状態遷移表

さて、この時点での状態モデルは、ただ単にとりあえず描いたレベルのモデルであり、正しいとか正しくないとかいうレベルにはない。この段階で、プログラミングの実装を行っても、実装後の想定外の動きやデッドロックの発生など、後で大きな手直しが発生するだけであるのは容易に想像がつくので、先ずは、作成したモデルが正しく現実世界を反映した振舞をするかテストを行う。
…と、その前に。
Model Explorer でこれまで定義してきたモデル要素を見渡すと、

Model Explorer による俯瞰

イベント引数の名前付けが _ で区切っていたり区切ってなかったり、状態の名前付けがばらばらだったりと、ちょっと気持ち悪い状況になっている。プログラミングにおいて、変数やクラス、メソッド等の名前を Naming Convention に従って付けるのと同様、概念モデリングにおいても統一された名前付けになっている方が望ましい。状態モデルを複数人で手分けして作成する場合等、無用な誤解を生まないためにも、この段階で名前の付け方を統一しておく。

この段階の概念モデルは、artifacts-laundromat-in-hotel-tutorial/model/LaundromatInHotel at 20220531 · kae-made/artifacts-laundromat-in-hotel-tutorial (github.com) から公開しているので、実際に BridgePoint で確認していただきたい。

振舞のテスト

さて、モデルの名前整理が終わったところで、作成した情報モデル群のテストを行う。テスト方法は、シナリオ毎に

  1. 概念インスタンス群の初期値設定

  2. 開始用の事象(イベント)発生

  3. 事象を受信する概念インスタンスの次の状態を状態モデルで特定する。

  4. 特定された状態の Entry アクションを実行する。アクション実行中に生じる以下の項目を記録しておく。

    1. アクション実行中に新たなイベントが発生

    2. 概念インスタンスの特徴値の更新、概念インスタンスの作成・削除、リンクの作成・削除が発生

    3. モデル化対象の主題領域の外に影響を及ぼす操作が発生

  5. 実行完了時点で該当する概念インスタンスの

  6. 発生しているイベント(群)を状態モデルの実行セマンティクスに則って一つ選択し、3に戻る

    1. イベントは、Entry アクションで明示的に生成したものだけでなく、4‐3. のような操作を行った結果、反応として発生するものや、現実世界の物理的な現象の結果生じる事象を含む

といった流れで行う。あらかじめ、ホワイトボードなど、存在する概念インスタンス、及び、その特徴値群の値、概念インスタンス間のリンクの有無、未処理イベントを記録する場所を用意しておく。ホワイトボードや、PC 上の Excel 等を使うとよい。
4-3.の主題領域外に影響を及ぼす操作というのは、例えば、Door with Lock で、状態が "4. Locked for washing" に遷移した時に、現実の世界で対応する洗濯機のドアがハードウェア的、メカニカル的にロックが実際にかかるようなものを指す。モデル作成者の頭の中で”Lock”、”Lock”と叫んでも誰にも伝わらないので、この場合においては、現実世界のドア機構に対応する Door with Lock クラスに ”Close” という Operation を定義して、アクション記述内でその Operation を ”Invoke した” と記録する。Invoke した結果、発生する現実世界でのLock 機構へのオーダーは、「どうそれを実現するか」という設計の話なので、概念モデリングでは無視してよい。
次に、Door with Lock クラスの状態モデルの ”DoorwithLock3:Door Opened” は、現実世界の宿泊客が手(多分)で、ドアを閉めたことに起因して発生するイベントであり、現実世界のドア機器がセンサー等で閉められたことを検知して何らかのハードウェアを通じて、IT システムに通知されてくるものではあるが、その辺りは、モデリング対象の主題領域外のお話なので無視してよく、単に、テスト中の現実的なタイミングでイベントが発生したことにすればよい。

さて、現段階の、漠然とコメントされている状態の Entry アクションの説明では、テストを行う人によって思いつく実行内容が異なってしまい、テストが無意味になってしまう。テストの前にアクションの詳細化を行う。
詳細化は、概念情報モデルで定義された、クラス、特徴値、Relationship、イベント、Operation を使って行う。
経験上、現実世界の(IT化可能な)振舞は全て、これら概念モデルで定義されたエレメントと、以下の実行単位だけで記述できることが判っている。

  • 概念インスタンスの生成・削除

  • 概念インスタンスの特徴値の読み書き

  • 概念インスタンス間のリンクの切り貼り

  • 概念インスタンスからリンクを介して関連付けられた概念インスタンス、または、概念インスタンスセットの取得、及び、概念インスタンスセットが含む個々の概念インスタンスへの参照

    • 特徴値に対する条件付けフィルタリングも可

  • 存在する概念クラスの概念インスタンス群の収集

    • 特徴値に対する条件付けフィルタリングも可

  • 概念インスタンスの Operation の Invocation

  • 概念インスタンスへのイベント送信

    • 時間指定による遅延も可

  • 条件判断

    • 四則演算・論理演算を伴う概念インスタンスの特徴値を元にした論理判定

    • 取得した概念インスタンスの有無、及び、概念インスタンスセットが含む概念インスタンスの数

以上の道具立てで、アクションを詳細化する。例として、Washing Machine Assigner の "1. Wait for reservation request" 状態を使って実際を示す。

この状態は、WashingMachineAssigner1: Request reservtion(guestStayId, specId) というイベント受信時の遷移先なので、どの宿泊かを知るために guestStayId が利用でき、どの Available Washing Spec かを specId で見つけることができる。
 
Check if the requested time slot is available.

  • specId を元に該当する Available Washing Spec の概念インスタンス(specとする)を検索

  • 見つけた概念インスタンス spec から、R8 -> Washing Machine -> R13 -> と辿って、既に予約済みの Washing Machine Reservation セット(wmrSetとする)を取得

  • guestStayId を元に該当する Guest Stay の概念インスタンス(gsとする)を検索

  • 見つけた概念インスタンスから R12 を辿って Washing Mashine Reservation の概念インスタンス(newWmrとする)を取得

  •  newWmrReservationTimeEstimatedEndTime と重複する概念インスタンスが wmrSet にあるかどうか比較する

add it to the reservation list if it is available.

  • 重複がない場合、R17 のリンクを張りなおす

    • wmrSet の全ての概念インスタンスの ReservationTime が、newWmrEstimatedEndTime より遅い場合は、R17の先頭に newWmr をリンクする。

    • wmrSet の全ての概念インスタンスの EstimatedEndtime が、newWmrReservationTime より前の場合は、R17 の末尾に newWmr をリンクする。 

  • wmrSetR17 によるリンクを辿って、一つ前の概念インスタンス(preWmrとする)、一つ後の概念インスタンス(postWmrとする)が、
    preWmrEstimatedEndTime < newWmrReservationTime
    newWmrEstimatedEndTime < postWmrReservationTime
    になるように R17 のリンクをつなぎかえる

  • 自分自身と newWmrR13 でリンクする

  • # newWmr に対して、WashingMachineReservation2: Assigned イベントを送信する

  • pec から、R8 -> R15 と辿って、Reservalble Washing Machine の概念インスタンス(rwm とする)を取得し、rwm に対して、ReservableWashineMachine1: Check イベントを送信する

and notify you if it is not available

  • 時間スロットが重複している場合は、newWmrWashingMachineReservation6: Rejected イベントを送信する

Then waiting for new request

  • 一通りアクションが実行されたので何もせずに状態確定

と、こんな感じでアクションが詳細化される。この時点で読者の悲鳴が聞こえてきそうではあるが、概念モデリングに限らず、厳密で詳細な仕様を記述しようと思えば、別の方策を用いても、この程度の煩雑さは避けられないであろう。
本稿は、概念モデリングのチュートリアルなので、敢えて、かかる時間と労力と煩雑さを度外視して基本に忠実に書き下ろした次第である。実際の開発活動において、労力をかけて基本に忠実に全ての状態モデルの状態のアクションを詳細化することによって、その後の工程で消費した時間をカバーするに余りある価値が生じると確信できる場合を除き、このやり方を筆者は推奨しない。
作成した状態モデルを、人が見るだけのものであるなら、Entry アクションは概要レベルのコメントにとどめるべきだろう。
BridgePoint は、Object Action Language という、データフローベースの実行セマンティクスを持った高い抽象度のアクション記述用言語を用意している。この言語は、前述の実行単位を網羅しており、例示したアクションの詳細もこの言語で記述可能である。BridgePoint による概念モデルの実行や概念情報モデル、状態モデル、アクション記述から、実際にコンピュータ上で実行可能なプログラム一式も自動生成可能なので、アクションを詳細化したい場合は、この言語を使って記述することを推奨する。

参考までに、BridgePoint がサポートする Object Action Language を使って、各状態の Entry アクションを記述したものを、artifacts-laundromat-in-hotel-tutorial/model/LaundromatInHotel at 20220603 · kae-made/artifacts-laundromat-in-hotel-tutorial (github.com) から公開しているので、参照されたい。アクションを記述していく過程で発見した、状態モデルと概念情報モデルの間違いや過不足を、都度都度修正しながら作業を進めている。主な修正点を参考までに挙げておく。

  • 概念情報モデル

    • R19 の追加 - 予約列の先頭を示す Relationship

    • 識別子特徴値の名前の形式の統一 Id -> ID

    • 諸々のスペルミス

    • 振舞で必要な概念情報クラスオペレーションの追加

  • 状態モデル

    • Washing Machine の状態、イベント、遷移の大幅見直し(ほぼ作り直し)

    • Door with Lock の状態、イベント、遷移の大幅見直し(ほぼ作り直し)

  • その他

    • 外部ドメインが提供するサービスの追加 - 具体的には Time ドメインを External Entity として追加

      • サンプルの  MicrowaveOven で定義された TIM External Entity を Copy & Paste した

      • 時間の処理に関して必要なオペレーションを若干追加

Washing Machine の "Done washing" 、"Done drying"、Door with Lock の "Door Opened"、"Door Closed" 等のイベントは、現実世界で発生するイベントであり、モデル化対象のドメインの振舞を記述した状態モデルの Entry アクション内では、その発生を行うための記述がない。公開しているモデルでは、これらドメインの外部からのイベントの発生と、概念情報クラスのインスタンス群、及び、インスタンス間のリンク群構築の為のアクション(ドメインの振舞モデルに属する)を BridgePoint の Function として記述している。

この時点でのモデル図の一覧を参考までに挙げておく。

概念情報モデル
Door with Lock の状態モデル
Washing Machine の状態モデル
Reservable Washing Machine の状態モデル
Washing Machine Reservation の状態モデル
Washing Machine Assigner の状態モデル

状態モデル・アクションのモデリングでは、作業の一時中断や、要求のキャンセルのモデル化が案外難しい。本チュートリアルではそれほど複雑な様相ではないので、Washing Machine Reservation が表す予約の途中のキャンセルは、そのまま状態モデルに入れ込んで表現している。Washing Machine つまり、洗濯機の、洗濯や乾燥中の一時停止に関しては、状態モデル作成過程である程度の思考実験を行ったうえでの表現を行っている。実際のシナリオでは、洗濯や乾燥の開始後に一旦止めて、衣類を追加して継続する等が考えられるが、ここでは、「中断したら選択途中の衣類を取り出す」、だけに留めている。
一般的に、一時中断やキャンセル要求はいつでもできてしまうので、モデルを蜘蛛の巣状態に陥らせずにモデル化するにはそれなりのテクニックが必要になる。ある意味、これができるようになって初めて一人前の概念モデラーと言えなくもないのだが、モデル化ノウハウは、別稿で詳しく解説したいと思う。

参考までに、Model Explorer で、作成したモデルの全景を表示した図を紹介しておく。

Model Explorer で表示した全景

前述の公開モデルは、一通り必要な記述を行っているので、BridgePoint の振舞検証機能を使って、実際にモデルを動かして想定通り動作するかのテストが可能になっている。が、現時点では行っていない。そのため、まだまだ色々と不具合が残っている可能性であることをご容赦いただきたい。
モデルによる検証については、こちらも別稿で詳細を説明しようと思っている。

(振舞検証は未終了だが)ここまでで、概念モデル作成に関するチュートリアルは、一段落とする。
概念情報モデルを特に詳細に解説した「”商品販売”を例にした、概念モデルチュートリアル」も、是非、熟読していただきたい。

補足

その後のモデル検証で、二つの不具合を発見し、修正を加えた。
一つは R19 の Formalize に関するもので、修正前は、Washing Machine Reservation 側で参照変数を定義していたが、'next reservastion' は、どちらかと言えば、Reservable Washing Machine からの観点の方が強いと判断し、こちらの方で Formalize する様に変更した。
もう一つは、予約が順番に並ぶ様を抽象化した R17 に関するもので、Washing Machine Assigner の状態モデルのアクション記述が間違っていたので修正を行った。モデルは、artifacts-laundromat-in-hotel-tutorial/model/LaundromatInHotel at 20220608 · kae-made/artifacts-laundromat-in-hotel-tutorial (github.com) で参照可能。
Sequence パターンの操作は間違いやすいので、bp-model-patterns/Sequence at main · kae-made/bp-model-patterns (github.com) も参考にしていただきたい。

モデルの活用

BridgePoint を使った厳密な概念情報モデルの作成を、概念モデリング作成の Tips を散りばめながら解説してきたが、多くの読者は、「で?」というのが素直な感想なのではないだろうか。ここまでは、ソフトウェア開発というよりも、「何がしたいの?」という What の仕様記述にすぎない、というのが筆者の見解であり、ソフトウェア開発はここからが本番である。
作成した概念モデルを使った、IT システム開発に関する、以下のチュートリアルを順次公開していくので、定期的にリンクが作られたか確認し、順次、参考にしていただきたい。

  • Azure Digital Twins の DTDL 定義生成

  • In Memory で動く、概念モデルからのコード自動生成

  • Azure Digital Twins、Azure Functions で動く、概念モデルからのコード一式自動生成

  • シナリオ拡張 - 洗濯機管理

  • 一般化 ー 設備機器管理

  • 一時中断、キャンセルのモデル化

  • 概念モデルの検証

  • 色付け

おまけ

参考までに、概念モデル作成にかかった時間を目安として挙げておく。
※ 1日=約6時間

  • 脳内あ~でもないこ~でもない検討

    • 2 ~ 3日

  • BridgePoint によるモデル作成

    • 概念情報モデル

      • 1 日

    • 状態モデル群

      • 1日

    • アクション記述 ~ 概念情報モデルや状態モデルの見直し含む

      • 1.5 日

この記事が気に入ったらサポートをしてみませんか?