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


## 背景

最近食事にいくと、テーブルに置かれた QRコードを読み取ってそこからスマホで注文する形式の飲食店が増えている気がする。
もし、このシステムを構築してくれと頼まれたときを想定して、RDRA で要件定義してみる。
※本来は、Saas のテナント型の仕組みになっていそうだけど、今回は無視して考える。 

## コンテキストモデル

まずは、登場人物とシステム、外部システムを洗い出す。


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

left to right direction

storage "ユーザー" #line.dotted {
    actor "客" as customer
    actor "店員" as store_user
    actor "料理人" as chef
    actor "店長" as store_manager

    chef --|> store_user
    store_manager --|> store_user
}
component "システム" as system {
    component "メニュー注文表" as menu_system
    component "店舗サイト" as store_system
    database "メニュー\n注文データ" as store_database
    menu_system -- store_database
    store_system -- store_database
}
component "外部システム" as outer_system {
    component QRコード印刷機 as qr_printer
}

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

@enduml
```

使ったことは無いが、おそらく店舗側の管理サイトがあるだろう。
QRコードの印刷機も連携して毎回印刷していると思う。
システム構築の目的については、整理した後で記述する。

## システム要求

システムへの要求事項を以下にリストアップした。

### 要求一覧

- QR コードを読み取って、スマホからメニューを注文できるようにしたい
- 注文したメニューは、各スマホから履歴を確認したい
- 注文する際は、いくつかのメニューをまとめて注文したい
- キッチンでは、何が注文されたかリアルタイムで把握したい
- メニューは店舗オーナーが登録できるようにしたい
- 売り切れメニューは注文できないようにしたい
- スマホから会計（注文終了）したい
- 決済自体はシステム上で行わずに、店舗の既存システム上で行う

一旦このままにしておく。
あとで図示する。

## ビジネスコンテキスト

主要な業務を洗い出す。


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

left to right direction

' アクター
actor 客
actor 店長
actor 店員

' 業務
storage メニュー表作成
storage 注文
storage 調理
storage 食事
storage 会計

' もの
node メニュー表
node 注文履歴
node 料理


' 関連
メニュー表作成 -- 店長
メニュー表 -- メニュー表作成

注文 -- メニュー表
客 -- 注文

調理 -- 店員
料理 -- 調理

客 -- 食事
食事 -- 料理

注文 -- 注文履歴
注文履歴 -- 会計
客 -- 会計
会計 -- 店員

@enduml
```

飲食店の業務はなんとなく想像で書いてみる。
おそらく、店長がメニュー作成してたりするんじゃなかろうか。。。
この辺の業務は、MTG 時に詳しく確認したい。

## 情報モデル

システムの中で使われていそうな用語（概念）を関連づける。
とりあえず最初のモデルとして、用語の洗い出しと関連付けをしてみた。

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

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

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

@enduml
```

QRコード発行のあたりが怪しい気がする。
後からどんどん増えると思うが、いったん置いておく。

## 状態モデル

状態が変化していく情報があれば、ここで洗い出す。

### テーブル

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


storage 食事中
storage 食事終了

usecase 着席する
usecase 会計する

circle 開始
circle 終了

開始 - 着席する
着席する -- 食事中
食事中 - 会計する
会計する -- 食事終了
食事終了 -- 終了

@enduml
```

### 注文伝票

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

storage 注文予定
storage 注文済み
usecase 注文を追加する
usecase 注文を確定する
circle 開始
circle 終了

開始 - 注文を追加する
注文を追加する -- 注文予定

注文予定 - 注文を確定する
注文を確定する -- 注文済み
注文済み -- 終了
@enduml
```

### メニュー

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

storage 注文可能
storage 売り切れ

usecase 売り切れにする

circle 開始
circle 終了

開始 -- 注文可能
注文可能 - 売り切れにする
売り切れにする -- 売り切れ
売り切れ -- 終了

@enduml
```

以外といくつか出てきた。

---

## ユースケース

ひとまず一覧で書き出す

```plantuml
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentBorderColor #000000
skinparam shadowing false
hide footbox
actor 店員 as store_user
storage store_usecase #line.dotted [
    - メニューを登録・更新・削除する
    - カテゴリーを登録・更新・削除する
    - QR コードを印刷する
    - 未提供の注文された料理一覧を見る
    - 提供した料理のステータスを提供済みにする
    - メニューのステータスを売り切れにする
    - テーブルの注文履歴を見る
    - テーブルの注文合計金額を見る
    - テーブルのステータスを更新する
]
store_user - store_usecase

actor "客" as end_user
storage end_usecase #line.dotted [
    - メニュー一覧を見る
    - 注文を追加する
    - 注文を確定する
    - 注文をキャンセルする
    - 注文履歴を見る
    - テーブルのステータスを食事終了にする
]
end_user - end_usecase
@enduml
```

---

**思ったこと**

- おそらく、メニューやカテゴリーは事前にマスターデータの登録が必要。
- QR コードの印刷タイミングは、お客さんがテーブルに着いた後なのか、事前なのかは確認しておきたい。
- お客さんから注文された料理はキッチン側でリアルタイムに確認できないといけないし、調理し終わったらステータスを変更できるようにしなきゃいけないだろう。（操作端末はタブレットとか？）
- 料理のステータスとして「調理済み」と「提供済み」は分ける必要があるだろうか？（調理して完成しているが、まだテーブルに運んでいないというステータスは重要？）
- 注文の追加（未確定）の状態は、客同士の端末で共有する必要があるか？

**今回のシステムにおいて**
→ QR コードはお客さんが来店する前から印刷して、テーブルに用意しておくことにする。
→「調理済み」というステータスは考えないことにする。
→ キッチンには、タブレット端末を 2 台、モニターを 1 台設置する設定。
→ 客同士の端末で、注文未確定の状態も共有することにする。

---

業務フローからユースケースを書き出す

### メニュー表作成

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

actor 店員

店員 --> 店員: マスターデータを登録
note right
・カテゴリーを登録する
・カテゴリーを参照する
・カテゴリーを更新する
・カテゴリーを削除する
・メニューを登録する
・メニューを参照する
・メニューを更新する
・メニューを削除する
end note

@enduml
```

### 注文～食事～会計

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

actor 客
actor 店員

店員 --> 店員: テーブルの用意をする
note right
・QRコードを印刷する
end note

客 --> 店員: 来店する
客 <-- 店員: テーブルに案内する
客 --> 客: QRコードを読み取る

loop
    loop
        客 --> 客: 料理を選ぶ
        note left
        ・メニューを見る
        ・注文を追加する
        ・注文をキャンセルする
        end note
    end

    客 --> 店員: 料理を注文する
    note left
    ・注文を確定する
    end note

    店員 --> 店員: 食材確認する
    note right
    ・注文された料理を見る
    ＜食材がなくなる場合＞
    ・メニューを売り切れにする
    end note

    店員 --> 店員: 調理する

    客 <-- 店員: 料理を提供する
    note right
    ・料理を提供済みにする
    end note

    客 --> 客: 料理を食べる
end


客 --> 客: 注文履歴を確認する
note left
・注文履歴を見る
・注文合計金額を見る
end note

客 --> 店員: 会計する
note left
・テーブルのステータスを食事終了にする
end note

note right
・注文履歴を見る
・注文合計金額を見る
end note

店員 --> 店員: テーブルを片付ける

@enduml
```

---

**思ったこと**

- ラストオーダーの概念が必要か？
- お客さんからシステム経由でなく直接注文された場合など、お客さん側の操作を店員側でも実施できる必要があるか？
- テーブルステータスは食事中にどのタイミングで更新すべきか？他にステータスは持たなくて良いか？
- 客「QR コード無くした」と言われたら、再発行必要？
- 客「間違って大量に注文した、キャンセルしてくれ」と言われたら、注文取消できる機能必要？

**今回のシステムにおいて**

→ ラストオーダーの概念は必要
→ 店員側でお客さんの操作ができる必要あり
→ 最初のメニューをお客さんが注文した時点でテーブルのステータス変更とする
→ QR コードの再発行も必要とする
→ 確定済みの注文の取消も店員側でのみ可能とする

---

不足しているフローを追加する

### 注文代行

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

actor 客
actor 店員

客 --> 客: 料理を選ぶ
客 --> 店員: 料理を直接口頭で注文する

店員 --> 店員: 料理注文のシステム入力を代行する
note right
・メニューを見る
・注文を追加する
・注文をキャンセルする
・注文を確定する
end note
店員 --> 店員: 調理する
客 <-- 店員: 料理を提供する
客 --> 客: 料理を食べる


@enduml
```

### QR コード再発行

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

actor 客
actor 店員

客 --> 店員: QRコードの再発行を依頼する

店員 --> 店員: QRコードを再発行する
note right
・QRコードを再発行する
end note
店員 --> 客: QRコードを渡す



@enduml
```

### ラストオーダー

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

actor 客
actor 店員
actor システム

客 <-- 店員: ラストオーダー時刻を伝える

システム --> システム: オーダーストップする
activate システム
note right
・自動でオーダーストップ
（新規注文不可の状態に）する
end note

@enduml
```

### 注文取消

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

actor 客
actor 店員

客 --> 客: 料理を選ぶ
客 --> 店員: 料理を注文する
客 --> 店員: 料理を注文の取消依頼をする

店員 --> 店員: 料理の注文を取消する
note right
・料理の注文を取消する
end note

@enduml
```

---

フローを書き出せたので、今度は状態遷移を再検討する

### 料理注文の状態遷移

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

actor "客"

usecase 料理を注文伝票に追加する
usecase 注文伝票内の料理注文を確定する

客 .. 料理を注文伝票に追加する
客 .. 注文伝票内の料理注文を確定する

storage 注文検討中
storage 注文確定済み


usecase 料理の注文を取消する
usecase 料理を提供済みにする

店員 .. 料理の注文を取消する
店員 .. 料理を提供済みにする

storage 注文取消済み
storage 料理提供済み

circle 開始
circle 終了 as normal_end
circle 終了 as cancel_end

開始 --> 料理を注文伝票に追加する
料理を注文伝票に追加する --> 注文検討中

注文検討中 -> 注文伝票内の料理注文を確定する
注文伝票内の料理注文を確定する --> 注文確定済み

注文確定済み --> 料理の注文を取消する
料理の注文を取消する --> 注文取消済み
注文取消済み --> cancel_end

注文確定済み -> 料理を提供済みにする
料理を提供済みにする --> 料理提供済み
料理提供済み --> normal_end

@enduml
```

・ステータスを 4 種類用意した
・「注文を追加する」から「料理を注文伝票に追加する」に変更した
・「注文を確定する」から「注文伝票内の料理注文を確定する」に変更した

---

この時点のユースケースの一覧を確認する

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


actor 店員 as store_user
storage store_usecase #line.dotted [
    - カテゴリーを登録する
    - カテゴリーを参照する
    - カテゴリーを更新する
    - カテゴリーを削除する
    - メニューを登録する
    - メニューを参照する
    - メニューを更新する
    - メニューを削除する
    - QR コードを印刷する
    - QR コードを再発行する
    - 料理を注文伝票に追加する
    - 注文をキャンセルする
    - 注文伝票内の料理注文を確定する
    - 料理の注文を取消する
    - 注文された料理を見る
    - 料理を提供済みにする
    - メニューを売り切れにする
    - テーブルの注文履歴を見る
    - テーブルの注文金額合計を見る
    - テーブルを食事終了にする
]
store_user - store_usecase

actor "客" as end_user
storage end_usecase #line.dotted [
    - メニュー一覧を見る
    - 料理を注文伝票に追加する
    - 注文伝票内の料理注文を確定する
    - 注文をキャンセルする
    - 注文履歴を見る
    - テーブルのステータスを食事終了にする
]
end_user - end_usecase

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

@enduml
```

---

次は、情報との整合性を確認する

いったん終了。
この後、ユースケースと情報を洗練させていく感じで進める予定。（架空）


続き
https://qiita.com/s3imamura/items/bdc516e2234744855d20