flow引当と発送 v1examples/fulfillment/temporal/fulfillment.ja.flow · dandori doc
注文の明細ごとに在庫を引き当て、配送を手配し、倉庫の梱包を待ってから、お客さまに知らせる。明細は並べて引き当て、足りない明細があれば、引き当てたぶんを戻す。Temporal 向けの版:倉庫の呼び出しは dandori が Connect で生成するアクティビティ。配送チームのワークフローが専用のタスクキューで配送を手配し(子ワークフロー、arrange_delivery.ja.flow)、翌日便の車がなければ通常便で頼み直す。梱包の依頼、お知らせ、記録は自分で書くアクティビティで、梱包の担当者は、生成したクライアントが送る Update でコールバックに応答する
flow| 行 | 呼び出し | 呼ぶもの | リトライ | タイムアウト | 失敗したとき |
|---|---|---|---|---|---|
| 73 | 判定 = 急ぎ(…) | 規則 出荷の急ぎ.rule | 1 秒後と 2 秒後の 2 回(failure) | — | timeout・failure → ワークフローが失敗する |
| 75 | 答え = 在庫を引き当てる(…) | connect warehouse StockService/Reserve・key | 1 秒おきに 2 回(混雑) | — | 混雑・timeout・failure → そのイテレーションが失敗し、ワークフローも失敗する |
| 86 | 引当を戻す(…) | connect warehouse StockService/Release・idempotent | — | — | timeout・failure → そのイテレーションが失敗し、ワークフローも失敗する |
| 95 | 記録する(…) | 自分で書くタスク・idempotent | — | — | timeout・failure → ワークフローが失敗する |
| 98 | 配送 = 配送を手配する(…) | flow arrange_delivery.ja.flow | — | — | 翌日便の空きなし → 99 行目timeout・failure → 102 行目 |
| 100 | 配送 = 配送を手配する(…) | flow arrange_delivery.ja.flow | — | — | 翌日便の空きなし・timeout・failure → 101 行目 |
| 103 | 箱 = 梱包を待つ(…) | 自分で書くタスク(応答はコールバック) | — | 2 日 | timeout → 104 行目failure → ワークフローが失敗する |
| 105 | 知らせる(…) | 自分で書くタスク | — | — | 宛先なし → 106 行目timeout・failure → ワークフローが失敗する |
| 行 | 終わり方 |
|---|---|
| 88 | fail 在庫不足 "注文 {注文.id} には在庫の足りない明細があります" |
| 101 | fail 配送の手配の失敗 "注文 {注文.id} の配送を手配できませんでした" |
| 102 | fail 配送の手配の失敗 "注文 {注文.id} の配送を手配できませんでした" |
| 104 | fail 梱包の遅れ "二日たっても梱包の知らせがありません" |
| 107 | succeed 引当 = 結果, 追跡番号 = 配送.追跡番号 |
ステップを選ぶと、そこについて検査で分かったことが出ます。左のシナリオを選ぶと、その実行が通るところが光ります。ステップの横の数は、その実行が通った回数です。
引当と発送 v1注文の明細ごとに在庫を引き当て、配送を手配し、倉庫の梱包を待ってから、お客さまに知らせる。明細は並べて引き当て、足りない明細があれば、引き当てたぶんを戻す。Temporal 向けの版:倉庫の呼び出しは dandori が Connect で生成するアクティビティ。配送チームのワークフローが専用のタスクキューで配送を手配し(子ワークフロー、arrange_delivery.ja.flow)、翌日便の車がなければ通常便で頼み直す。梱包の依頼、お知らせ、記録は自分で書くアクティビティで、梱包の担当者は、生成したクライアントが送る Update でコールバックに応答する
注文: 注文引当: list[warehouse.ReserveResponse]・追跡番号: stringshop.ja.v1.FulfillmentService (../specs/fulfillment.ja.proto)Fulfill: 実行を始めます。失敗の名前は 在庫不足・配送の手配の失敗・梱包の遅れ ですAnswerPacking: コールバックに応答します (梱包を待つ)examples/fulfillment/temporal/fulfillment.ja.flow判定 = 急ぎ(…) 73 行目 73 let 判定 = 急ぎ(会員: 注文.会員, 金額: 注文.金額)
出荷の急ぎ.ruletimeout・failure → ワークフローが失敗する 4 use rule 急ぎ from "../../order/rules/出荷の急ぎ.rule"
答え = 在庫を引き当てる(…) 75 行目 75 let 答え = 在庫を引き当てる(sku: 明細.sku, quantity: 明細.数)
connect warehouse StockService/Reserve・key混雑・timeout・failure → そのイテレーションが失敗し、ワークフローも失敗する42 # 倉庫は、どの引当にも答える。在庫を押さえられたら secured とその ID、足りなければ short。 43 task 在庫を引き当てる(sku: string, quantity: int) -> warehouse.ReserveResponse 44 connect warehouse "StockService/Reserve" 45 errors 混雑 = resource_exhausted 46 key 47 retry 2 times every 1 second on 混雑
let 足りない = false 77 行目 77 let 足りない = false
match 一つ.stock 79 行目79 match 一つ.stock 80 short => let 足りない = true 81 secured => pass
let 足りない = true 80 行目 80 short => let 足りない = true
match 足りない 82 行目82 match 足りない 83 true => 89 false => pass
match 戻す.id 85 行目85 match 戻す.id 86 some id => 引当を戻す(id: id) 87 none => pass
引当を戻す(…) 86 行目 86 some id => 引当を戻す(id: id)
connect warehouse StockService/Release・idempotenttimeout・failure → そのイテレーションが失敗し、ワークフローも失敗する49 task 引当を戻す(id: string) 50 connect warehouse "StockService/Release" 51 idempotent
fail 在庫不足 88 行目 88 fail 在庫不足 "注文 {注文.id} には在庫の足りない明細があります"
let 宛名 = none 90 行目 90 let 宛名: string? = none
match 注文.贈り物 91 行目91 match 注文.贈り物 92 some 贈り物 => 97 none => pass
let 宛名 = 贈り物.宛名 93 行目 93 let 宛名 = 贈り物.宛名
match 贈り物.メッセージ 94 行目94 match 贈り物.メッセージ 95 some 一言 => 記録する(注文ID: 注文.id, 付帯: {メッセージ: 一言, 宛名: 贈り物.宛名}) 96 none => pass
記録する(…) 95 行目 95 some 一言 => 記録する(注文ID: 注文.id, 付帯: {メッセージ: 一言, 宛名: 贈り物.宛名})
idempotenttimeout・failure → ワークフローが失敗する69 task 記録する(注文ID: string, 付帯: json) 70 idempotent
配送 = 配送を手配する(…) 98 行目98 let 配送 = 配送を手配する(注文ID: 注文.id, 便: 判定.便, 宛名: 宛名, 付帯: 注文.付帯) 99 on 翌日便の空きなし => 102 on failure => fail 配送の手配の失敗 "注文 {注文.id} の配送を手配できませんでした"
flow arrange_delivery.ja.flow翌日便の空きなし → 99 行目timeout・failure → 102 行目53 # 配送チームのワークフローが、専用のタスクキューのワーカーで配送を手配する。それ自身の .flow で 54 # 書いてあり、検査はこのタスクを、その入力・出力・エラーと突き合わせる。 55 task 配送を手配する(注文ID: string, 便: 急ぎ.便, 宛名: string?, 付帯: json) -> 配送 56 flow "../arrange_delivery.ja.flow" 57 errors 翌日便の空きなし
配送 = 配送を手配する(…) 100 行目100 let 配送 = 配送を手配する(注文ID: 注文.id, 便: 通常便, 宛名: 宛名, 付帯: 注文.付帯) 101 on failure => fail 配送の手配の失敗 "注文 {注文.id} の配送を手配できませんでした"
flow arrange_delivery.ja.flow翌日便の空きなし・timeout・failure → 101 行目53 # 配送チームのワークフローが、専用のタスクキューのワーカーで配送を手配する。それ自身の .flow で 54 # 書いてあり、検査はこのタスクを、その入力・出力・エラーと突き合わせる。 55 task 配送を手配する(注文ID: string, 便: 急ぎ.便, 宛名: string?, 付帯: json) -> 配送 56 flow "../arrange_delivery.ja.flow" 57 errors 翌日便の空きなし
fail 配送の手配の失敗 101 行目 101 on failure => fail 配送の手配の失敗 "注文 {注文.id} の配送を手配できませんでした"
fail 配送の手配の失敗 102 行目 102 on failure => fail 配送の手配の失敗 "注文 {注文.id} の配送を手配できませんでした"
箱 = 梱包を待つ(…) 103 行目103 let 箱 = 梱包を待つ(注文ID: 注文.id, 引当: 結果) 104 on timeout => fail 梱包の遅れ "二日たっても梱包の知らせがありません"
timeout → 104 行目failure → ワークフローが失敗する59 # 梱包の担当者のアプリは、自分で書く関数から依頼とコールバックの ID を受け取り、箱詰めが 60 # 済むとコールバックに応答する。 61 task 梱包を待つ(注文ID: string, 引当: list[warehouse.ReserveResponse]) -> 梱包 62 callback 63 timeout 2 days
fail 梱包の遅れ 104 行目 104 on timeout => fail 梱包の遅れ "二日たっても梱包の知らせがありません"
知らせる(…) 105 行目105 知らせる(注文ID: 注文.id, 本文: "注文 {注文.id} を{判定.便}で出しました({箱.箱の数} 箱、追跡番号 {配送.追跡番号})") 106 on 宛先なし => pass
宛先なし → 106 行目timeout・failure → ワークフローが失敗する65 # お店の通知サービス。 66 task 知らせる(注文ID: string, 本文: string) 67 errors 宛先なし
succeed 引当 = 結果, 追跡番号 = 配送.追跡番号 107 行目 107 succeed 引当 = 結果, 追跡番号 = 配送.追跡番号
終わり(成功)flow が最後まで走り、ワークフローは成功します。
let 結果 = for 明細 in 注文.明細 at most 50 in parallel, 10 at a time 74 行目 74 let 結果 = for 明細 in 注文.明細 at most 50 in parallel, 10 at a time
for 一つ in 結果 at most 50 78 行目 78 for 一つ in 結果 at most 50
for 戻す in 結果 at most 50 in parallel 84 行目 84 for 戻す in 結果 at most 50 in parallel
判定 = 急ぎ(…) oklet 結果 = for 明細 in 注文.明細 at most 50 in parallel, 10 at a timelet 足りない = falsefor 一つ in 結果 at most 50match 足りない → falselet 宛名 = nonematch 注文.贈り物 → none配送 = 配送を手配する(…) ok箱 = 梱包を待つ(…) ok知らせる(…) oksucceed 引当 = 結果, 追跡番号 = 配送.追跡番号判定 = 急ぎ(…) failure failure failure → ここでは処理しない判定 = 急ぎ(…) ok判定 = 急ぎ(…) oklet 結果 = for 明細 in 注文.明細 at most 50 in parallel, 10 at a time答え = 在庫を引き当てる(…) oklet 足りない = falsefor 一つ in 結果 at most 50match 一つ.stock → securedmatch 足りない → falselet 宛名 = nonematch 注文.贈り物 → some 贈り物let 宛名 = 贈り物.宛名match 贈り物.メッセージ → none配送 = 配送を手配する(…) 翌日便の空きなし → on 翌日便の空きなし配送 = 配送を手配する(…) ok箱 = 梱包を待つ(…) timeout → on timeoutfail 梱包の遅れ判定 = 急ぎ(…) oklet 結果 = for 明細 in 注文.明細 at most 50 in parallel, 10 at a time答え = 在庫を引き当てる(…) failure → ここでは処理しない答え = 在庫を引き当てる(…) failure → ここでは処理しない判定 = 急ぎ(…) oklet 結果 = for 明細 in 注文.明細 at most 50 in parallel, 10 at a time判定 = 急ぎ(…) oklet 結果 = for 明細 in 注文.明細 at most 50 in parallel, 10 at a timelet 足りない = falsefor 一つ in 結果 at most 50match 足りない → falselet 宛名 = nonematch 注文.贈り物 → some 贈り物let 宛名 = 贈り物.宛名match 贈り物.メッセージ → some 一言記録する(…) ok配送 = 配送を手配する(…) failure → on failurefail 配送の手配の失敗判定 = 急ぎ(…) oklet 結果 = for 明細 in 注文.明細 at most 50 in parallel, 10 at a timelet 足りない = falsefor 一つ in 結果 at most 50match 足りない → falselet 宛名 = nonematch 注文.贈り物 → none配送 = 配送を手配する(…) 翌日便の空きなし → on 翌日便の空きなし配送 = 配送を手配する(…) failure → on failurefail 配送の手配の失敗判定 = 急ぎ(…) oklet 結果 = for 明細 in 注文.明細 at most 50 in parallel, 10 at a timelet 足りない = falsefor 一つ in 結果 at most 50match 足りない → falselet 宛名 = nonematch 注文.贈り物 → none配送 = 配送を手配する(…) ok判定 = 急ぎ(…) oklet 結果 = for 明細 in 注文.明細 at most 50 in parallel, 10 at a timelet 足りない = falsefor 一つ in 結果 at most 50match 足りない → falselet 宛名 = nonematch 注文.贈り物 → none配送 = 配送を手配する(…) ok箱 = 梱包を待つ(…) failure → ここでは処理しない判定 = 急ぎ(…) oklet 結果 = for 明細 in 注文.明細 at most 50 in parallel, 10 at a timelet 足りない = falsefor 一つ in 結果 at most 50match 足りない → falselet 宛名 = nonematch 注文.贈り物 → none配送 = 配送を手配する(…) ok箱 = 梱包を待つ(…) ok判定 = 急ぎ(…) oklet 結果 = for 明細 in 注文.明細 at most 50 in parallel, 10 at a timelet 足りない = falsefor 一つ in 結果 at most 50match 足りない → falselet 宛名 = nonematch 注文.贈り物 → none配送 = 配送を手配する(…) ok箱 = 梱包を待つ(…) ok知らせる(…) 宛先なし → on 宛先なしsucceed 引当 = 結果, 追跡番号 = 配送.追跡番号判定 = 急ぎ(…) oklet 結果 = for 明細 in 注文.明細 at most 50 in parallel, 10 at a timelet 足りない = falsefor 一つ in 結果 at most 50match 足りない → falselet 宛名 = nonematch 注文.贈り物 → none配送 = 配送を手配する(…) ok箱 = 梱包を待つ(…) ok知らせる(…) failure → ここでは処理しない判定 = 急ぎ(…) oklet 結果 = for 明細 in 注文.明細 at most 50 in parallel, 10 at a time答え = 在庫を引き当てる(…) 混雑 oklet 足りない = falsefor 一つ in 結果 at most 50match 一つ.stock → shortlet 足りない = truematch 足りない → truefor 戻す in 結果 at most 50 in parallelmatch 戻す.id → nonefail 在庫不足判定 = 急ぎ(…) oklet 結果 = for 明細 in 注文.明細 at most 50 in parallel, 10 at a time答え = 在庫を引き当てる(…) ok判定 = 急ぎ(…) oklet 結果 = for 明細 in 注文.明細 at most 50 in parallel, 10 at a time答え = 在庫を引き当てる(…) ok判定 = 急ぎ(…) oklet 結果 = for 明細 in 注文.明細 at most 50 in parallel, 10 at a time答え = 在庫を引き当てる(…) oklet 足りない = falsefor 一つ in 結果 at most 50match 一つ.stock → shortlet 足りない = truematch 足りない → truefor 戻す in 結果 at most 50 in parallelmatch 戻す.id → some id引当を戻す(…) okfail 在庫不足判定 = 急ぎ(…) oklet 結果 = for 明細 in 注文.明細 at most 50 in parallel, 10 at a time答え = 在庫を引き当てる(…) oklet 足りない = falsefor 一つ in 結果 at most 50match 一つ.stock → securedmatch 足りない → falselet 宛名 = nonematch 注文.贈り物 → some 贈り物let 宛名 = 贈り物.宛名match 贈り物.メッセージ → some 一言記録する(…) failure → ここでは処理しない判定 = 急ぎ(…) oklet 結果 = for 明細 in 注文.明細 at most 50 in parallel, 10 at a time答え = 在庫を引き当てる(…) oklet 足りない = falsefor 一つ in 結果 at most 50match 一つ.stock → securedmatch 足りない → falselet 宛名 = nonematch 注文.贈り物 → some 贈り物let 宛名 = 贈り物.宛名match 贈り物.メッセージ → none配送 = 配送を手配する(…) failure → on failurefail 配送の手配の失敗判定 = 急ぎ(…) oklet 結果 = for 明細 in 注文.明細 at most 50 in parallel, 10 at a time答え = 在庫を引き当てる(…) oklet 足りない = falsefor 一つ in 結果 at most 50match 一つ.stock → securedmatch 足りない → falselet 宛名 = nonematch 注文.贈り物 → some 贈り物let 宛名 = 贈り物.宛名match 贈り物.メッセージ → none配送 = 配送を手配する(…) 翌日便の空きなし → on 翌日便の空きなし配送 = 配送を手配する(…) failure → on failurefail 配送の手配の失敗判定 = 急ぎ(…) oklet 結果 = for 明細 in 注文.明細 at most 50 in parallel, 10 at a time答え = 在庫を引き当てる(…) oklet 足りない = falsefor 一つ in 結果 at most 50match 一つ.stock → securedmatch 足りない → falselet 宛名 = nonematch 注文.贈り物 → some 贈り物let 宛名 = 贈り物.宛名match 贈り物.メッセージ → none配送 = 配送を手配する(…) 翌日便の空きなし → on 翌日便の空きなし配送 = 配送を手配する(…) ok判定 = 急ぎ(…) oklet 結果 = for 明細 in 注文.明細 at most 50 in parallel, 10 at a time答え = 在庫を引き当てる(…) oklet 足りない = falsefor 一つ in 結果 at most 50match 一つ.stock → securedmatch 足りない → falselet 宛名 = nonematch 注文.贈り物 → some 贈り物let 宛名 = 贈り物.宛名match 贈り物.メッセージ → none配送 = 配送を手配する(…) 翌日便の空きなし → on 翌日便の空きなし配送 = 配送を手配する(…) ok箱 = 梱包を待つ(…) ok知らせる(…) oksucceed 引当 = 結果, 追跡番号 = 配送.追跡番号判定 = 急ぎ(…) oklet 結果 = for 明細 in 注文.明細 at most 50 in parallel, 10 at a time答え = 在庫を引き当てる(…) ok答え = 在庫を引き当てる(…) oklet 足りない = falsefor 一つ in 結果 at most 50match 一つ.stock → shortlet 足りない = truematch 一つ.stock → shortlet 足りない = truematch 足りない → truefor 戻す in 結果 at most 50 in parallelmatch 戻す.id → nonematch 戻す.id → nonefail 在庫不足判定 = 急ぎ(…) oklet 結果 = for 明細 in 注文.明細 at most 50 in parallel, 10 at a time答え = 在庫を引き当てる(…) ok答え = 在庫を引き当てる(…) oklet 足りない = falsefor 一つ in 結果 at most 50match 一つ.stock → shortlet 足りない = truematch 一つ.stock → shortlet 足りない = truematch 足りない → truefor 戻す in 結果 at most 50 in parallelmatch 戻す.id → some id引当を戻す(…) failure → ここでは処理しないmatch 戻す.id → some id引当を戻す(…) failure → ここでは処理しない判定 = 急ぎ(…) oklet 結果 = for 明細 in 注文.明細 at most 50 in parallel, 10 at a time答え = 在庫を引き当てる(…) ok答え = 在庫を引き当てる(…) oklet 足りない = falsefor 一つ in 結果 at most 50match 一つ.stock → securedmatch 一つ.stock → securedmatch 足りない → falselet 宛名 = nonematch 注文.贈り物 → some 贈り物let 宛名 = 贈り物.宛名match 贈り物.メッセージ → some 一言記録する(…) ok配送 = 配送を手配する(…) failure → on failurefail 配送の手配の失敗判定 = 急ぎ(…) oklet 結果 = for 明細 in 注文.明細 at most 50 in parallel, 10 at a time答え = 在庫を引き当てる(…) ok答え = 在庫を引き当てる(…) oklet 足りない = falsefor 一つ in 結果 at most 50match 一つ.stock → securedmatch 一つ.stock → securedmatch 足りない → falselet 宛名 = nonematch 注文.贈り物 → some 贈り物let 宛名 = 贈り物.宛名match 贈り物.メッセージ → none配送 = 配送を手配する(…) ok箱 = 梱包を待つ(…) ok知らせる(…) oksucceed 引当 = 結果, 追跡番号 = 配送.追跡番号判定 = 急ぎ(…) oklet 結果 = for 明細 in 注文.明細 at most 50 in parallel, 10 at a time答え = 在庫を引き当てる(…) ok答え = 在庫を引き当てる(…) oklet 足りない = falsefor 一つ in 結果 at most 50match 一つ.stock → securedmatch 一つ.stock → securedmatch 足りない → falselet 宛名 = nonematch 注文.贈り物 → some 贈り物let 宛名 = 贈り物.宛名match 贈り物.メッセージ → some 一言記録する(…) ok配送 = 配送を手配する(…) 翌日便の空きなし → on 翌日便の空きなし配送 = 配送を手配する(…) ok箱 = 梱包を待つ(…) timeout → on timeoutfail 梱包の遅れ判定 = 急ぎ(…) oklet 結果 = for 明細 in 注文.明細 at most 50 in parallel, 10 at a time答え = 在庫を引き当てる(…) ok答え = 在庫を引き当てる(…) oklet 足りない = falsefor 一つ in 結果 at most 50match 一つ.stock → securedmatch 一つ.stock → securedmatch 足りない → falselet 宛名 = nonematch 注文.贈り物 → some 贈り物let 宛名 = 贈り物.宛名match 贈り物.メッセージ → none配送 = 配送を手配する(…) 翌日便の空きなし → on 翌日便の空きなし配送 = 配送を手配する(…) failure → on failurefail 配送の手配の失敗判定 = 急ぎ(…) oklet 結果 = for 明細 in 注文.明細 at most 50 in parallel, 10 at a time答え = 在庫を引き当てる(…) ok答え = 在庫を引き当てる(…) oklet 足りない = falsefor 一つ in 結果 at most 50match 一つ.stock → securedmatch 一つ.stock → securedmatch 足りない → falselet 宛名 = nonematch 注文.贈り物 → some 贈り物let 宛名 = 贈り物.宛名match 贈り物.メッセージ → some 一言記録する(…) ok配送 = 配送を手配する(…) 翌日便の空きなし → on 翌日便の空きなし配送 = 配送を手配する(…) ok箱 = 梱包を待つ(…) timeout → on timeoutfail 梱包の遅れ