Open8

[WIP]新幹線予約システムをドメインモデリングしてみる

モチベーション

  • ICONIXプロセスの練習
    • ロバストネス分析の美味しさがよくわかってないから、それを理解できるようになる事を期待
  • モデリング手順の整理
  • Zenn使ってみたい
    • 今年も終わりだし、一個くらいなんかしらアウトプットしたい
    • とはいえ体型だった知識をまとめるのはハードルが高い
    • スクラップだったら雑に思考過程まとめるにはちょうど良い気がした
  • PlantUMLが好き

要件の洗い出し

RDRAでは要望/要求/要件のように整理していくが、今回は大体要件が決まり切ってるので想像で書く

要件一覧

  • 空いている席の検索
    • 空いている全ての席を検索したい
    • 空いている指定席を検索したい
    • 空いている自由席を検索したい
    • 空いている窓側の席を検索したい
    • 喫煙ルームに近い車両から席を検索したい
    • トイレに近い車両から席を検索したい
  • 予約
    • 出発地点〜到着地点までの
      • 指定席or自由席を予約したい
      • 並びの座席で複数同時予約したい
  • 変更
    • 予約中の席から他の席に変更したい
  • キャンセル
    • 予約中の席をキャンセルしたい
  • 予約の確認
    • 自分の予約済みの席一覧を確認したい
    • 過去に予約した席を確認したい
  • 通知
    • 予約完了したらメールで知らせてほしい

ドメインモデルv1(用語集)を作成する

ICONIXの文脈ではドメインモデルと表現しますが、DDDの文脈のドメインモデルとは意味するところが違うので注意が必要

要件から名詞を抜き出す

  • 空席
  • 指定席
  • 自由席
  • 窓側の席
  • 喫煙ルーム
  • 車両
  • トイレ
  • 予約
  • 出発地点
  • 到着地点
  • 並びの座席
  • 複数同時予約
  • 予約中の席
  • 予約済みの席一覧
  • 過去に予約した席
  • 通知
  • メール

PlantUMLで[card]で定義し、まとまり事にざっくりと段落で区切る

個人的にICONIXのこの工程はPlantUMLやmarmaid.jsなどの、テキストでモデリングできるツールが相性良さげ。

plantumlのプレビューはIntellijを使う。マルチカーソルが使えるエディタなら、一気にTypeを書き加えられるので便利。

後の工程でcard同士を関連づけていくのですが、一旦段落でまとまりを整理しておくと楽です。

段落で整理していく中で気づいた、「運賃」や「席の種類・状態」といった概念もこの時点で追加しておきます。

@startuml
card 新幹線
    card 系
    card 車両
        card 号
        card 席一覧
            card 席
                card 種類
                    card 指定席
                    card 自由席
                card 状態
                    card 予約済み
                    card 空席
                card 配置
                    card 窓際の席
                    card 喫煙ルームに近い
                    card トイレに近い
card 履歴
card 予約一覧
    card 予約
        card 出発地点
        card 到着地点
        card 運賃
            card 指定席料金
            card 自由席料金
@enduml

image

card同士を関連づけていく

汎化と集約関係を使って、cardを関連づけていく。

この時、関係性がよくわからないものは、一旦棒線でつなげておけばよい。(関係性は後の工程で明らかになる可能性が高く、最初はそこに悩む時間がもったいない為。)

@startuml
card 新幹線
    card 系
    card 車両
        card 号
        card 席一覧
            card 席
                card 種類
                    card 指定席
                    card 自由席
                card 状態
                    card 予約済み
                    card 空席
                card 配置
                    card 窓際の席
                    card 喫煙ルームに近い
                    card トイレに近い
新幹線 --* 系
新幹線 --* 車両
    車両 --* 号
    車両 --* 席一覧
        席一覧 --* 席
            席 --* 種類
                種類 <|-- 指定席
                種類 <|-- 自由席
            席 --* 配置
                配置 <|-- 窓際の席
                配置 <|-- 喫煙ルームに近い
                配置 <|-- トイレに近い
            席 --* 状態
                状態 <|-- 予約済み
                状態 <|-- 空席

card 履歴
履歴 --* 席

card 予約一覧
    card 予約
        card 区間
            card 出発地点
            card 到着地点
        card 運賃
            card 指定席料金
            card 自由席料金
予約一覧 --* 予約
    予約 --* 区間
        区間 --* 出発地点
        区間 --* 到着地点
    予約 --* 運賃
        運賃 <|-- 指定席料金
        運賃 <|-- 自由席料金
    予約 --* 席
@enduml

image

ユースケースを洗い出す

「要件」からユースケースを考える。「メールで完了通知」は「予約完了」のドメインイベントを発行して...などややこしそうな気がしたので一旦無視する
指定席とか、自由席とか、喫煙席に近い、とかは座席予約時に絞り込み機能などで提供すべきなのかな?ICONIXでは「ドメインモデル 」や「ユースケース記述」「ロバストネス図」は常に更新し続ける前提なので、細かい事は一旦気にしない

ユースケース一覧

  • 列車を検索する
  • 席を予約する
  • 予約中の席を他の席に変更する
  • 予約中の席をキャンセルする
  • 予約履歴を確認する

ユースケース図

image

ユースケース毎にユースケース記述を書く

  • ユースケース記述は、ユーザーとシステムとの対話を記述する
  • 使用する用語は「ドメインモデル 」で洗い出したものを使用する
    • ユースケース記述を書く中で用語に違和感がある場合は、用語を修正し、ドメインモデルに反映する
  • 新たな用語が登場する場合は、ドメインモデルに追加する
    • これが目的でもある(多分)
    • 全部書き切って、最後でも良い(多分)
  • 個人的に箇条書きでユーザーのアクションとシステムの振る舞いを段落で分けるのが好み
  • GUIの場合は、システムがある画面を表示するところから書き始めるので、最初の行は*にしてみた
    • 段落を他と揃えたいだけなので特に意味はない
  • 今回は他のユースケースへの参照を(hogehoge) で表現してみた

列車を検索する

晴れの日

  • *
    • システムは時刻・運賃検索画面を表示する
  • 予約者出発駅到着駅出発日時を入力し、検索ボタンをクリックする
    • システムは条件に沿った列車列車一覧から検索し、検索結果画面を表示する

雨の日

  • 出発駅/到着駅が未入力
    • エラー表示し、再度時刻・運賃検索画面を表示する
  • 出発日時が未入力
    • エラー表示し、再度時刻・運賃検索画面を表示する

席を予約する

晴れの日

  • (列車を検索する)
  • 予約者検索結果画面から任意の予約ボタンをクリックする
    • システムは指定された列車運行情報から、検索条件区間に絞り込んだ時刻表を一覧表示する
    • 各時刻の行には、席の種類毎の運賃(指定席料金, 自由席料金, グリーン席料金 )を表示する。
  • 予約者は希望する席の種類をクリックする
    • システムは選択された席の種類から、空席状態の席を一覧表示する
  • 予約者絞り込みタブをクリックする
    • システムはドロップダウンで窓際, 喫煙ルームに近い, トイレに近い, 2人並び席, 3人並び席を表示する
  • 予約者は任意の絞り込みをクリックする
    • 窓際が選択された場合
      • システムは空席状態C席またはE席を一覧表示する
    • 喫煙ルームに近いが選択された
    • システムは喫煙ルームを挟む号車から、空席状態を一覧表示する
    • トイレに近いが選択された場合
    • システムはトイレを挟む号車から、空席状態を一覧表示する
    • 2人並び席が選択された場合
    • システムは隣同士で空席状態を一覧表示する
    • 3人並び席が選択された場合
    • システムは3席続けて空席状態を一覧表示する
  • 予約者は任意のをクリックする
    • システムは乗車日, , 出発時刻/到着時刻, 席の種類, 料金を表示する
  • 予約者は予約ボタンをクリックする
    • システムは選択された予約する

雨の日

  • 希望した席の種類で空席が0だった
    • エラー表示し、再度席の種類毎の運賃(指定席料金, 自由席料金, グリーン席料金 )を表示する。
  • 予約された席が既に予約済みだった
    • エラー表示し、再度空席状態の席を一覧表示する

予約履歴を確認する

晴れの日

  • *
    • システムは予約者のマイページを表示する
  • 予約者予約履歴ボタンをクリックする
    • システムは予約履歴一覧画面を表示する

予約中の席を他の席に変更する

晴れの日

  • (予約履歴を確認する)
  • 予約者は変更したい予約詳細ボタンをクリックする
    • システムは予約詳細を表示する
  • 予約者変更するボタンをクリックする
    • システムは予約済みであった列車の区間に絞り込んだ時刻表を一覧表示する
    • 各時刻の行には、席の種類毎の運賃(指定席料金, 自由席料金, グリーン席料金 )を表示する。
  • 以下(席を予約する)[予約者は希望する席の種類をクリックする]に同じ

雨の日

  • 予約していた電車の運行時間を過ぎていた
    • エラー表示し、予約詳細画面を表示

予約中の席をキャンセルする

晴れの日

  • (予約履歴を確認する)
  • 予約者は変更したい予約詳細ボタンをクリックする
    • システムは予約詳細を表示する
  • 予約者キャンセルするボタンをクリックする
    • システムは予約のキャンセル画面を表示する
  • 予約者OKボタンをクリックする
    • システムは予約者予約のキャンセルする

雨の日

  • 予約していた電車の運行時間を過ぎていた
    • エラー表示し、予約詳細画面を表示

ユースケース記述の内容をドメインモデルに反映する

  • ユースケース記述に出てきた用語を初期のドメインモデルに反映する
  • 更新/追加した項目は色を変えてみた
  • 予約詳細が出発時刻到着時刻を持つのは実際にそうなるかはまだわからない
    • 乗車する列車の区間が決まれば、↑の値は一意に定まるはず
  • 席の種類席の料金が離れているのが気になる
    • 予約詳細は実は料金を持たなくて良い?

image

ロバストネス図

  • 席のキャンセルからは、特に新しいオブジェクトを発見できそうになかったので割愛
  • 改めて「ユースケース駆動開発実践ガイド」を読むと、コントローラには「アクターの振る舞い」と「システムの振る舞い」がある事に気付いた
    • その内の「システムの振る舞い」が、おそらくDomain層のモデルのメソッドになったり、Domain Serviceになったりするのでは?
  • コントローラーの振る舞いを遂行する為にどんなオブジェクトが必要か?と考えると、新たなオブジェクトが発見しやすい事がわかった
    • 例えば、「料金を算出する」コントローラーがあれば、料金を算出する為には「料金表」とか「基本料金」とかが必要では?ってなる

予約中の席を他の席に変更する

image

列車を検索する

image

予約履歴を確認数

image

席を予約する

image

ドメインモデル v3

  • ロバストネス図を書いてる途中に発見した概念や、その後ドメインモデルを整理する中で連鎖的に発見した概念を追加
    • ロバストネス図から発見したオブジェクトを追加しようとすると、既存のオブジェクトの今の関係性おかしくない?とか連鎖的に発生した
    • 多分これはエヴァンス本で言う「ブレークスルー」に繋がる話かも?
      • なんか微妙な感じだったけど隠れていた「暗黙な概念」を発見する事がきっかけで「リファクタ」→「より深いモデルの発見」みたいな話に通じそう
    • とはいえ今はドメインエキスパートとかいなくて1人でやってる(予約システムのドメイン知識も皆無)ので、実際はどんな感じなんだろう
  • 追加したcardは青にした
  • 料金は予約対象の席から導かれるもので、料金オブジェクトと汎化関係で指定席料金、自由席料金、グリーン席料金が存在するのは違和感を感じたので削除

image

クラス図 v1

  • 雰囲気で整理してみたversion1
  • テストから書き始めてみて、使い勝手を確認するのが良さそう
  • おそらくこのモデルは実装の過程で細かい部分はどんどん変わっていく
    image
作成者以外のコメントは許可されていません