QRコードからスマホで注文するシステムの要件定義をRDRA使ってやってみる-その４

https://qiita.com/s3imamura/items/e169b7507d59053117e5
の続き

## お客様との MTG

RDRA のモデルを説明しながら、お客様から業務のお話を聞いたことにする。
ネット上にあるメニュー表を探していたところ、「びっくりドンキー」のメニューがちょうど良さそうだったので拝借した。

```markdown
◆ メニュー
・メニューは以下 URL
└ <https://www.bikkuri-donkey.com/menu/>
・時間帯によって提供するメニューが変わる（モーニング／ランチ）
・店舗によらず基本的には同じだが、一部の店舗で提供していないメニューもある

◆ 料金
・22 時以降は深夜料金で 10% 割増となる

◆ 店舗、ユーザー情報
・店舗は複数件あり、それぞれ価格設定が異なる。
・店舗には店長がいるがメニューを作っているわけではない。ただ、メニューの更新などは店舗にいる社員が行いたい。
・店長含め社員は社用のメールアドレス持っているけど、店員はアルバイトも多く、メールアドレス発行されていない。
・パスワード救済はメール通知で行う。
・店員のプライベート端末であっても、制約を設けたうえでアクセス・操作できるようにしてほしい。
└ アルバイトによる不正利用防止のため。
・店員はホールスタッフと調理スタッフで、とくに区別しているわけではない。

◆ 注文カート
・「注文カート」という用語は使っていないけど、他に該当する用語もないから、そのままでよいとのこと。
・客の端末を超えて注文カートは共有しないで OK。

◆ 店員オペレーション
・注文提供や、注文取消、売り切れ対応は店員のオペレーションで OK とのこと。

◆ 他機能
・売り切れからもとに戻す機能は必要。
・座席の統合はいらない。
・他の機能も付けたいけど、まずは動くものを作ってほしい。
```

店舗が複数あるのかぁ。
メニューも見てみないと何も言えないな。
割増料金もあるし、データ構造変わるなぁ。。。

ひとまず、メニュー確認してみる。

### メニューの確認

Web サイトにメニューが載っているので確認してみると、バリエーションが色々ありそう。
・ハンバーグの量を選択できる（150g or 300g）
・トッピングも追加できる（エッグ、チーズ、パイン、おろしそ）
・ライスの量を選択できる（大盛 or 普通 or 小盛）
・ライスの種類を変更できる（ライス or カリフラワーライス）
・サラダを追加できる（なし or 追加）※サラダ単品の販売もあり
・ランチは 11 ～ 17 時の間に注文可能
・モーニングは開店～ 11 時の間に注文可能
・他メニューは常時注文可能（モーニングの時間帯でも注文可能）
・ドリンクもサイズを選択できる（小 or 中 or 大）
・期間限定メニューもある

メニューはライス変更（択一選択）や追加トッピング等（複数選択可）が適用できるデータ形式にしてみる。
ランチタイム、モーニングタイムは同じらしい

### 社員アカウント

メールアドレスとパスワードで認証するような形式ができそう。
パスワードリセット機能もメールアドレスに送信する形で実現する。
管理社員用のアカウントと一般社員用のアカウントの区別をつける。

### メニューと店舗の関連

各店舗のバリエーションは料金と提供有無とのこと。
店舗配下にメニューを登録するというよりは、メニューの中から店舗で提供するものを選択して提供料金を編集できるような形式にしてみる。

### モデルの更新

まずは、変更エネルギーコストの低そうなシステムコンテキスト図あたりから更新してく。

#### 変更後のモデル（システムコンテキスト）

```plantuml
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentBorderColor #000000
skinparam shadowing false
hide footbox

left to right direction
file porpose [
【システム化の目的】

・注文業務を自動化することで、
  店員の負担を減らし業務効率化すること
・注文情報をデジタルデータとして蓄積・分析し
  店舗経営の指針とすること
]


storage "システム利用者" #line.dotted {
    actor "客" as customer
    actor "店舗ユーザー" as store_user
    actor "社員" as employee
    actor "一般社員" as general_employee
    actor "管理社員" as manager_employee
    actor "アルバイト" as part_store_user

    general_employee --|> employee
    manager_employee --|> employee
    employee --|> store_user
    part_store_user --|> store_user
}
component "QRコード注文システム" as system {
    component "QR注文サイト" as qr_order_system
    component "店舗サイト" as store_system
    database "メニュー\n注文データ" as store_database
    qr_order_system -- store_database
    store_system -- store_database
}
storage "外部システム"  as outer_system #line.dotted{
    component QRコード印刷機 as qr_printer
}

' 関連
customer -- qr_order_system
store_user -- store_system
store_system --- qr_printer

porpose --[hidden] store_database

@enduml
```

店長ではなく、社員とアルバイトという分類にした。
そして社員はさらに、一般社員と管理社員という区分にした。
これに伴いユースケースや他モデル内の用語もすべて置換していく。

#### 参考）前のモデル（システムコンテキスト）

```plantuml
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentBorderColor #000000
skinparam shadowing false
hide footbox
scale 0.6

left to right direction

storage "システム利用者" #line.dotted {
    actor "お客様" as customer

    actor "店員" as store_user
    actor "調理スタッフ" as store_chef
    actor "ホールスタッフ" as store_waiter
    actor "店長" as store_manager

    store_manager --|> store_user
    store_chef --|> store_user
    store_waiter --|> store_user
}
component "QRコード注文システム" as system {
    component "QR注文サイト" as qr_order_system
    component "店舗サイト" as store_system
    database "メニュー\n注文データ" as store_database
    qr_order_system -- store_database
    store_system -- store_database
}
storage "外部システム"  as outer_system #line.dotted{
    component QRコード印刷機 as qr_printer
}

' 関連
customer -- qr_order_system
store_user -- store_system
store_system --- qr_printer

@enduml
```

---

次に新しい用語がいくつか出てきたので、情報モデルも更新する。

#### 変更後のモデル（情報モデル／用語集）

```plantuml
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentBorderColor #000000
skinparam shadowing false
hide footbox

storage "アカウント関連" #line.dotted {
    card 社員アカウント
    card メールアドレス
    ' 内部関連
    社員アカウント -- メールアドレス
}
storage "店舗関連" #line.dotted {
    card 店舗
    card テーブル
    ' 内部関連
    店舗 -- テーブル
    ' 外部関連
    社員アカウント -- 店舗
}

storage "メニュー関連" #line.dotted {
    card メニュー表
    card モーニングメニュー
    card ランチメニュー
    card カテゴリー
    card メニュー
    card メニューオプション
    card ドリンク
    card サイズ
    card 期間限定メニュー
    card トッピング
    card 料金
    card 深夜料金

    ' 内部関連
    メニュー表 -- モーニングメニュー
    メニュー表 -- ランチメニュー
    メニュー表 -- カテゴリー
    カテゴリー -- メニュー
    メニュー -- 期間限定メニュー
    メニュー -- ドリンク
    ドリンク -- サイズ
    メニュー -- トッピング
    メニュー -- 料金
    メニュー -- メニューオプション
    料金 -- 深夜料金
    ' 外部関連
    店舗 -- メニュー表
}

storage "注文関連" #line.dotted {
    card 客
    card QRコード
    card QRコード印刷
    card 注文
    card 注文カート
    card 食事
    card 注文履歴
    card 食事料金合計
    card 売り切れ

    ' 内部関連
    QRコード -- 客
    QRコード -- QRコード印刷
    客 -- 注文
    メニュー -- 注文
    注文 -- 注文カート
    注文 -- 売り切れ
    注文 -- 食事
    注文 -- 注文履歴
    注文履歴 -- 食事料金合計

    ' 外部関連
    テーブル -- QRコード
}

@enduml
```

情報モデルも用語が増えてきたので、分類した。
だんだんそれっぽくなってきた。

#### 参考）前のモデル（情報モデル／用語集）

```plantuml
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentBorderColor #000000
skinparam shadowing false
hide footbox
scale 0.6

card 店舗
card テーブル
card QRコード
card 客
card 料理注文
card 食事
card QRコード発行
card 注文伝票
card 注文履歴
card 食事料金合計
card メニュー
card メニュー表
card 料金
card カテゴリー

' 関連
店舗 -- テーブル
QRコード発行 -- QRコード
客 -- QRコード
テーブル -- 食事
客 -- 食事
食事 -- 料理注文
テーブル -- QRコード発行
店舗 -- メニュー表
メニュー表 -- カテゴリー
カテゴリー -- メニュー
食事 -- 注文伝票
メニュー -- 料金
メニュー -- 料理注文
料理注文 -- 注文履歴
食事料金合計 -- 注文履歴

@enduml
```

---

大きいところから進めるということで、ビジネスコンテキスト図を更新する。
このあたりから、ユースケースやデータモデルを更新しつつ何度も更新し直す。

#### 変更後のモデル（ビジネスコンテキスト）

```plantuml
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentBorderColor #000000
skinparam shadowing false
hide footbox

left to right direction

' アクター
actor 客
node 株式会社〇〇 #line.dotted {
    node 店舗 as store #line.dotted {
        actor 社員
        actor 店舗ユーザー
    }
    actor 管理社員
}

' もの
entity 社員アカウント
entity 店舗
entity メニュー
entity 店舗メニュー
entity テーブル
entity 料理
entity 食事合計金額

' 業務
storage "全体管理業務" #line.dotted {
    storage 社員アカウント管理
    storage 店舗管理
    storage メニュー管理

    ' 業務 - アクター関連
    社員アカウント管理 -- 管理社員
    メニュー管理 -- 管理社員
    店舗管理 -- 管理社員
    ' 業務 - もの関連
    社員アカウント -- 社員アカウント管理
    店舗 -- 店舗管理
    メニュー -- メニュー管理
}

storage "店舗管理業務" #line.dotted {
    storage 店舗情報設定
    storage 店舗メニュー設定

    ' 業務 - アクター関連
    店舗情報設定 -- 社員
    店舗メニュー設定 -- 社員
    ' 業務 - もの関連
    店舗 -- 店舗情報設定
    メニュー -- 店舗メニュー設定
    店舗メニュー -- 店舗メニュー設定
}

storage "店舗運用業務" #line.dotted {
    storage 開店閉店作業
    storage テーブル準備
    storage 料理提供
    storage 会計

    ' 業務 - アクター関連
    テーブル準備 -- 店舗ユーザー
    料理提供 -- 店舗ユーザー
    会計 -- 店舗ユーザー
    開店閉店作業 -- 店舗ユーザー

    ' 業務 - もの関連
    テーブル -- テーブル準備
    料理 -- 料理提供
    食事合計金額 -- 会計
}

storage "客業務" #line.dotted {
    storage 料理注文
    storage 食事

    ' 業務 - アクター関連
    客 -- 料理注文
    客 -- 食事
    客 -- 会計

    ' 業務 - もの関連
    料理注文 -- 店舗メニュー
    料理注文 -- 料理

    食事 -- テーブル
    食事 -- 料理
}

' 関連

@enduml
```

システムコンテキストに合わせてアクターの名称を更新。
業務を大きく全体管理業務、店舗管理業務、店舗運用業務、客業務に分類した。
それぞれの業務フローとそこに現れるユースケースを確認していく。

#### 参考）元のモデル（ビジネスコンテキスト図）

```plantuml
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentBorderColor #000000
skinparam shadowing false
hide footbox
scale 0.6

left to right direction

' アクター
actor 客
node "店舗" as store #line.dotted {
    actor 店長
    actor 店員
}

' 業務
storage 店舗情報設定
storage メニュー表作成
storage 料理注文
storage 料理提供
storage 食事
storage 会計

' もの
entity 店舗
entity メニュー
entity 料理注文履歴
entity 料理

' 関連
店舗 -- 店舗情報設定
店舗情報設定 -- 店長
メニュー表作成 -- 店長
メニュー -- メニュー表作成

料理注文 -- メニュー
客 -- 料理注文

料理注文履歴 -- 料理提供
料理提供 -- 店員
料理 -- 料理提供

客 -- 食事
食事 -- 料理

料理注文 -- 料理注文履歴
' 料理注文履歴 -- 会計
客 -- 会計
会計 -- 店員

@enduml
```

---

ここからは、ビジネスコンテキスト図で表した業務のフローを細かく確認していく。
システムに関わる業務を新たに発見したら、ビジネスコンテキスト図側に反映してく。

#### 業務フローユースケース関連

##### 社員アカウント管理

```plantuml
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentBorderColor #000000
skinparam shadowing false
hide footbox

actor 一般社員
actor 管理社員

' 社員アカウント管理 - 新規アカウント登録
group 新規アカウント登録
    一般社員 --> 管理社員: 新規登録依頼
    管理社員 --> 一般社員: アカウント登録\n（メール通知）
    note right
            - 社員アカウントを登録する
            - 社員を店舗に所属させる
            - 社員アカウントを参照する

            <操作を誤った場合>
            - 社員アカウントを更新する
            - 社員アカウントを削除する
    end note
    一般社員 --> 一般社員: ログイン確認
    note left
            - ログインする
            - ログイン中の社員アカウント情報を参照する
            - 店舗情報を参照する
            - ログアウトする
    end note
    一般社員 --> 管理社員: ログイン確認報告
end

' 社員アカウント管理 - アカウント情報更新
group アカウント情報更新
    一般社員 --> 一般社員: アカウント更新
    note left
            - ログイン中の社員アカウントを更新する
    end note
    一般社員 --> 一般社員: アカウント更新確認
    note left
            - ログイン中の社員アカウント情報を参照する
    end note
end

' 社員アカウント管理 - アカウント停止
group アカウント停止
    一般社員 --> 管理社員: アカウント停止依頼\n（退社・不正発覚等）
    note right
            - 社員を店舗の所属から解除する
            - 社員アカウントを停止する
    end note
end


@enduml
```

##### 店舗管理

```plantuml
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentBorderColor #000000
skinparam shadowing false
hide footbox

actor 一般社員
actor 管理社員

' 店舗管理 - 新規店舗登録
group 新規店舗登録
    管理社員 --> 管理社員: 新規店舗登録
    note right
            - 店舗を登録する
            - 店舗を参照する
            - 社員を店舗に所属させる

            <操作を誤った場合>
            - 店舗を更新する
            - 店舗を削除する
            - 社員を店舗の所属から解除する
    end note

    管理社員 --> 一般社員: 所属追加連絡
    一般社員 --> 一般社員: 店舗所属確認
    note left
            - ログインする
            - 店舗情報を参照する
            - ログアウトする
    end note
    一般社員 --> 管理社員: 店舗所属確認報告
end

' 店舗管理 - 店舗情報設定
group 店舗情報設定
    一般社員 --> 一般社員: 店舗情報設定
    note left
            - 店舗情報を更新する
            - 店舗情報を参照する
            - 店舗メニューを設定する
            - 店舗メニューオプションを設定する
    end note
end

' 店舗管理 - 閉店
group 店舗営業を閉じる
    管理社員 --> 管理社員: 店舗情報を更新する
    note right
            - 社員を店舗の所属から解除する
            - 店舗情報を更新する（ステータス無効）
    end note
end


@enduml
```

##### メニュー管理

```plantuml
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentBorderColor #000000
skinparam shadowing false
hide footbox

' actor "客\n（管理社員）" as customer
actor 一般社員
actor 管理社員
' メニュー管理 - メニュー登録
group 初期メニュー登録
    管理社員 --> 管理社員: 初期メニュー登録
    note right
        - カテゴリーを登録する
        - カテゴリーを参照する
        - メニューを登録する
        - メニューを参照する
        - メニューオプションを登録する
        - メニューオプションを参照する

        <操作を誤った場合>
        - カテゴリーを更新する
        - カテゴリーを削除する
        - メニューを更新する
        - メニューを削除する
    end note

    管理社員 --> 一般社員: メニュー登録連絡
    一般社員 --> 一般社員: メニュー確認
    note left
        - 店舗メニューを設定する
        - 店舗メニューオプションを設定する
    end note
    一般社員 --> 管理社員: メニュー確認報告
end

' メニュー管理 - 新メニュー追加
group 新メニュー追加
    管理社員 --> 管理社員: 新メニュー追加
    note right
        - カテゴリーを登録する
        - カテゴリーを参照する
        - メニューを登録する
        - メニューを参照する
        - メニューオプションを登録する
        - メニューオプションを参照する

        <操作を誤った場合>
        - カテゴリーを更新する
        - カテゴリーを削除する
        - メニューを更新する
        - メニューを削除する
    end note

    管理社員 --> 一般社員: 新メニュー追加連絡
    一般社員 --> 一般社員: 新メニュー追加確認
    note left
        - 店舗メニューを設定する
        - 店舗メニューオプションを設定する
    end note
    一般社員 --> 管理社員: 新メニュー追加確認報告
end

' メニュー管理 - メニュー値段変更
group メニュー値段変更
    管理社員 --> 管理社員: メニュー値段変更
    note right
        - メニューを更新する
    end note

    管理社員 --> 一般社員: メニュー値段変更連絡
    一般社員 --> 一般社員: メニュー値段変更確認
    note left
        - 店舗メニューを設定する
        - 店舗メニューオプションを設定する
    end note
    一般社員 --> 管理社員: メニュー値段変更確認報告
end

' メニュー表作成 - メニュー終了
group メニュー終了
    管理社員 --> 管理社員: メニュー終了
    note right
        - メニューを更新する（ステータス更新）
    end note
end
@enduml
```

#### ユースケース一覧

```plantuml
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentBorderColor #000000
skinparam shadowing false
hide footbox

left to right direction

storage "管理社員ユースケース" #line.dotted {
    actor 管理社員 as manager_employee
    storage manager_usecase_list #line.dotted [
        - 社員アカウントを登録する
        - 社員アカウントを参照する
        - 社員アカウントを更新する
        - 社員アカウントを停止する
        - 社員アカウントを削除する

        - 店舗を登録する
        - 店舗を参照する
        - 店舗を更新する
        - 店舗を削除する

        - 社員を店舗に所属させる
        - 社員を店舗の所属から解除させる

        - カテゴリーを登録する
        - カテゴリーを参照する
        - カテゴリーを更新する
        - カテゴリーを削除する

        - メニューを登録する
        - メニューを参照する
        - メニューを更新する
        - メニューを削除する

        - メニューオプションを登録する
        - メニューオプションを参照する
        - メニューオプションを更新する
        - メニューオプションを削除する
    ]

    storage "一般社員ユースケース" #line.dotted {
        actor 一般社員 as general_employee
        storage general_usecase_list #line.dotted [
            - ログインする
            - ログアウトする

            - 店舗を参照する
            - 店舗を更新する

            - 店舗メニューを設定する
            - 店舗メニューオプションを設定する

            - テーブルを登録する
            - テーブルを参照する
            - テーブルを更新する
            - テーブルを削除する

            - アルバイトアクセス用QRコードを印刷する
        ]
        general_employee - general_usecase_list

        storage "アルバイトユースケース" #line.dotted {
            actor アルバイト as part_store_user
            storage part_usecase_list #line.dotted [
                - QR コードを印刷する
                - QR コードを再印刷する

                - メニュー一覧を見る
                - 料理を注文カートに追加する
                - 料理を注文カートから削除する
                - 料理注文を確定する

                - 料理の注文状況を見る
                - メニューを売り切れにする
                - メニューを売り切れから戻す
                - 料理注文を取消する
                - 料理を提供済みにする

                - テーブルの注文履歴を見る
                - テーブルの食事合計金額を見る
                - テーブルの食事を終了する
            ]
            part_store_user -- part_usecase_list
        }
        general_employee -- アルバイトユースケース
    }
    manager_employee - manager_usecase_list
    manager_employee -- 一般社員ユースケース
}


actor "客" as customer
storage end_usecase #line.dotted [
    - メニュー一覧を見る
    - 料理を注文カートに追加する
    - 料理を注文カートから削除する
    - 料理注文を確定する
    - 料理の注文履歴を見る
    - テーブルの食事合計金額を見る
    - テーブルの食事を終了する
]
customer -- end_usecase

actor "システム（バッチ）" as cron_user
storage cron_usecase #line.dotted [
    - オーダーストップする
    - 開店処理する
    - 閉店処理する
]
cron_user -- cron_usecase

@enduml
```

期間終了したメニューとかは、データとしては削除しないけど、表示したくないケースが多いのかな。

---

### 状態モデル

今度は状態モデルを更新する。

```plantuml
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentBorderColor #000000
skinparam shadowing false
hide footbox



storage "テーブルステータス" as table_status #line.dotted {
    ' 開始-終了
    circle 開始 as table_status_start
    circle 終了 as table_status_end #black
    ' ステータス
    storage 待機中 as standby_on_table
    storage 食事中 as meal_while_on_table
    ' ユースケース
    usecase 開店処理する as open_on_table
    usecase 料理を注文する as order_food_on_table
    usecase 食事を終了する as finish_meal_on_table
    usecase 閉店処理する as close_on_table


    table_status_start --> open_on_table
    open_on_table --> standby_on_table

    standby_on_table -> order_food_on_table
    order_food_on_table --> meal_while_on_table
    order_food_on_table <-- meal_while_on_table

    meal_while_on_table -> finish_meal_on_table
    standby_on_table <-- finish_meal_on_table
    standby_on_table --> close_on_table
    close_on_table --> table_status_end
}

storage "メニューステータス" as menu_status #line.dotted {

    ' 開始-終了
    circle 開始 as menu_status_start
    circle 終了 as menu_status_end #black
    ' ステータス
    storage 注文可能 as menu_orderable
    storage 注文不可 as menu_unorderable {
        storage 売り切れ as menu_soldout
        storage オーダーストップ as menu_order_stop
    }
    ' ユースケース
    usecase メニューを売り切れにする
    usecase メニューを売り切れから戻す
    usecase オーダーストップする

    menu_status_start --> menu_orderable

    menu_orderable --> メニューを売り切れにする
    メニューを売り切れにする --> menu_soldout
    menu_orderable <- メニューを売り切れから戻す
    メニューを売り切れから戻す <--- menu_soldout
    menu_soldout --> menu_status_end

    menu_orderable --> オーダーストップする
    オーダーストップする --> menu_order_stop
    menu_order_stop --> menu_status_end
}

storage "注文カートステータス" as order_cart_status #line.dotted {
    ' 開始-終了
    circle 開始 as order_cart_status_start
    circle 終了 as order_cart_status_end #black
    ' ステータス
    storage 空 as order_cart_empty
    storage 未注文 as order_cart_contain_unorderd
    ' ユースケース
    usecase 料理を注文カートに追加する
    usecase 料理を注文カートから削除する
    usecase 注文を確定する

    order_cart_status_start --> order_cart_empty
    order_cart_empty -> 料理を注文カートに追加する
    料理を注文カートに追加する --> order_cart_contain_unorderd
    料理を注文カートに追加する <-- order_cart_contain_unorderd

    order_cart_contain_unorderd --> 注文を確定する
    order_cart_empty <-- 注文を確定する
    order_cart_contain_unorderd --> 料理を注文カートから削除する
    order_cart_empty <-- 料理を注文カートから削除する
    order_cart_empty --> order_cart_status_end
}

storage "料理注文ステータス" as food_order_status #line.dotted {
    ' 開始-終了
    circle 開始 as food_order_status_start
    circle 終了 as food_order_status_end #black
    ' ステータス
    storage 注文済み as food_ordered
    storage 提供済み as food_served
    storage 取消済み as food_order_canceled
    ' ユースケース
    usecase 料理を注文する
    usecase 料理を提供済みにする
    usecase 料理注文を取消する

    food_order_status_start --> 料理を注文する
    料理を注文する --> food_ordered

    food_ordered -> 料理を提供済みにする
    料理を提供済みにする --> food_served
    food_served --> food_order_status_end

    food_ordered --> 料理注文を取消する
    料理注文を取消する --> food_order_canceled
    food_order_canceled --> food_order_status_end
}

' レイアウト
table_status ------[hidden]- order_cart_status
table_status -----[hidden]- food_order_status

@enduml
```

閉店、開店によって状態が変わるケースも想定される。

---

### データモデル

各ユースケースを満たすように調整していく

#### 変更後のモデル

```plantuml
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentBorderColor #000000
skinparam linetype ortho
skinparam shadowing false
hide footbox
hide circle

storage "アカウント関連" #line.dotted {

    entity 社員アカウント {
        アカウントID
        --
        アカウント種別
        メールアドレス
        パスワード
        アカウントステータス
    }
    note right
    ◆アカウント種別
    ・一般社員アカウント
    ・管理社員アカウント

    ◆アカウントステータス
    ・有効
    ・停止
    end note

    entity パスワードリセット {
        パスワードリセットID
        --
        メールアドレス
        トークン
        有効期限
        有効フラグ
    }
    entity 所属店舗 {
        社員アカウントID
        店舗ID
        --
    }
    ' 内部関連
    社員アカウント ||--o{ 所属店舗
}

storage "メニューマスタ関連" #line.dotted {
    ' エンティティ
    entity カテゴリー {
        カテゴリーID
        --
        カテゴリー名
    }

    entity メニュー {
        メニューID
        --
        カテゴリーID <<FK>>
        メニュー種別
        メニュー名
        メニュー標準料金
        写真URL
        メニューステータス
    }
    note right
    ◆メニュー種別
    ・グランドメニュー
    ・ランチメニュー
    ・モーニングメニュー

    ◆メニューステータス
    ・提供中
    ・提供終了
    end note

    entity メニューオプション {
        メニューオプションID
        --
        メニューID<<FK>>
        メニューオプショングループ
        メニューオプション種別
        メニューオプション名
        メニューオプション標準料金
    }
    note right
    ◆メニューオプション種別
    ・択一型
    ・重複選択型

    ◆メニューオプショングループ
    同一選択肢グループは同一の数値を入力する想定
    end note

    ' 関連
    カテゴリー ||--o{ メニュー
    メニュー ||--o{ メニューオプション
}

storage "店舗情報関連" #line.dotted {
    ' エンティティ
    entity 店舗 {
        店舗ID
        --
        店舗名
        営業開始時刻
        営業終了時刻
        最終注文時刻
        店舗ステータス
        店舗アクセス用日次トークン
    }
    note right
    ◆店舗ステータス
    ・有効
    ・無効

    ◆店舗アクセス用日次トークン
    アルバイトは店舗用の QR コードで
    店舗サイトにアクセスする
    end note

    entity 店舗メニュー {
        メニューID<<FK>>
        店舗ID<<FK>>
        --
        提供フラグ
        メニュー料金
        メニューステータス
    }
    note right
    ◆メニューステータス
    ・提供可能
    ・売り切れ
    end note

    entity 店舗メニューオプション {
        店舗メニューID <<FK>>
        メニューオプションID <<FK>>
        --
        提供フラグ
        メニューオプション料金
    }
    entity テーブル {
        テーブルID
        --
        テーブル名
    }

    ' 内部関連
    店舗 ||--o{ 所属店舗
    店舗 ||--o{ 店舗メニュー
    店舗メニュー ||--o{ 店舗メニューオプション
    店舗 ||--o{ テーブル
    ' 外部関連
    メニュー ||--o{ 店舗メニュー
    メニューオプション ||--o{ 店舗メニューオプション
}

storage "食事関連" #line.dotted {
    ' エンティティ
    entity テーブルステータス {
        テーブルステータスID
        --
        テーブルID <<FK>>
        テーブルステータス
        食事中食事テーブルID
        トークン
        トークン発行日時
        トークン有効期限
    }
    note right
    ◆テーブルステータス
    ・待機中
    ・食事中
    end note

    entity 食事テーブル {
        食事テーブルID
        --
        （店舗ID）
        テーブルID<<FK>>
        トークン
        食事開始日時
        食事終了日時
        食事合計金額
    }

    entity 料理注文 {
        料理注文ID
        --
        （店舗ID）
        （テーブルID）
        食事テーブルID <<FK>>
        店舗メニューID <<FK>>
        数量
        注文日時
        料理注文ステータス
    }

    entity 料理注文オプション {
        料理注文オプションID
        --
        （店舗ID）
        （テーブルID）
        料理注文ID <<FK>>
        店舗メニューオプションID <<FK>>
    }

    entity 料理注文追加料金 {
        料理注文追加料金
        --
        （店舗ID）
        （テーブルID）
        料理注文ID <<FK>>
        追加料金
        事由種別
    }
    note right
    ◆事由種別
    ・深夜料金（10%増し）
    end note

    ' 内部関連
    食事テーブル |o..o| テーブルステータス
    食事テーブル ||--o{ 料理注文
    料理注文 ||--o{ 料理注文オプション
    料理注文 ||--o{ 料理注文追加料金

    ' 外部関連
    テーブル ||--|| テーブルステータス
    テーブル ||---o{ 食事テーブル
    店舗メニュー ||..o{ 料理注文
    店舗メニューオプション ||..o{ 料理注文オプション
}

@enduml
```

テーブルが増えた。
トークン発行あたりの流れはスッキリした気がするけど、名前がまだしっくりこない。
メニューオプションもこれで実現できるかどうかちょっと不安。
料金周りはどこに情報を持つのがよいのかイマイチわからない。

#### 参考）元のモデル（ERD）

```plantuml
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentBorderColor #000000
skinparam linetype ortho
skinparam shadowing false
hide footbox
hide circle
scale 0.6

entity ログインアカウント {
  アカウントID
  --
  パスワード
}

entity 営業時間 {
  営業時間ID
  --
  営業開始時刻
  営業終了時刻
  最終注文時刻
}

entity カテゴリー {
  カテゴリーID
  --
  カテゴリー名
}

entity メニュー {
  メニューID
  --
  カテゴリーID <<FK>>
  メニュー名
  メニュー料金
  写真URL
  メニューステータス
}

entity テーブル {
  テーブルID
  --
  テーブル名
  テーブルステータス
}

entity トークン発行 {
  トークン発行ID
  --
  テーブルID<<FK>>
  トークン
  発行日時
  有効期限
  トークンステータス
}

entity 食事 {
  食事ID
  --
  トークン発行ID<<FK>>
  （テーブルID）<<FK>>
  食事ステータス
  合計金額
}

entity 料理注文 {
  料理注文ID
  --
  食事ID <<FK>>
  メニューID <<FK>>
  数量
  注文日時
  料理注文ステータス
  （テーブルID） <<FK>>
}

カテゴリー ||--o{ メニュー

テーブル ||--o{ 食事
トークン発行 ||..o| 食事
テーブル ||..o{ トークン発行

食事 ||--o{ 料理注文
メニュー ||--o{ 料理注文

@enduml
```

この後の流れは、
・業務フローを網羅する
・各ユースケースごとにデータモデル＋状態遷移上の整合性をチェックし、不整合が発生する場合は練り直す。
・業務フロー上から例外フローを洗い出す。
・例外フローに対応できるデータモデル＋状態遷移となるように練り直す。
・要求を満たすための画面と画面遷移を考える。
・画面とユースケース、データ同士の不整合がないかチェックする
・異常系のユースケース時の画面遷移を練り直す。

以上で機能に関する要件の整理はひとまず完了の予定。

---

良かった点
・plantuml でも意外と色々表現できることがわかった。（とくに業務フロー）
・最初のモデルが全然ダメでも、プロセスを経て徐々に整合性が出てくる感じがわかった。

反省点
・全体として UX やユーザー価値などの検討が不足しており、実際の要件定義ではもっと手前の段階でシステム価値を考えなくちゃいけないと思う。
・用語集の作成（英名、説明の記述）は面倒くさかったので端折った。
・今回はひとりでモデルの作成を行ったが、本来はお客様やチームメンバー含めて時間を割いて一緒に実施すべきだった。
・ひとりでもそこそこ時間はかかった（今回の範囲までだと 4 人日くらい ※慣れてないと 10 人日くらいかかりそう）

注意点
・非機能要件の検討やインフラ構成については別途実施する必要あり。
・実際にプロジェクトの開発対象とするスコープはお客様と一緒に決める。
・plantuml はたしかに便利だが、整合性のチェックなどは人間が手動で行う必要があるので、規模が大きいシステムでは専用ツールを使うのもよいと思う。