Appendix D: 記入例(ランニング例の完成形)
Appendix B のテンプレを、ランニング例(小規模タスク管理)に対して記入した例です。テンプレ番号(B-1..B-8)に対応する順で掲載しています。
対象(ケース):
- ケース1: 「タスク割り当て + 通知(メール)」(D-1..D-8)
- ケース2: 「期限超過の表示 + 通知(メール)」(D-9..D-16)
| テンプレ |
記入例 |
ねらい |
| B-1 |
D-1 |
要求(なぜやるか)を KPI で固定する |
| B-2 |
D-2 |
要件(Shall)を実装から分離して列挙する |
| B-3 |
D-3 |
仕様(外部観測の振る舞い)を曖昧さなく定義する |
| B-5 |
D-4 |
設計(内部構造)を最小でアウトライン化する |
| B-4 |
D-5 |
S/D/V で依存の痛みを半定量化する |
| B-6 |
D-6 |
変更理由(Change Drivers)で「なぜ今」を揃える |
| B-7 |
D-7 |
ADR で重大な分岐を最小記録する |
| B-8 |
D-8 |
テスト配分(単体/統合/E2E)を合意する |
| テンプレ |
記入例(ケース2) |
ねらい |
| B-1 |
D-9 |
要求(なぜやるか)を KPI で固定する |
| B-2 |
D-10 |
要件(Shall)を実装から分離して列挙する |
| B-3 |
D-11 |
仕様(外部観測の振る舞い)を曖昧さなく定義する |
| B-5 |
D-12 |
設計(内部構造)を最小でアウトライン化する |
| B-4 |
D-13 |
S/D/V で依存の痛みを半定量化する |
| B-6 |
D-14 |
変更理由(Change Drivers)で「なぜ今」を揃える |
| B-7 |
D-15 |
ADR で重大な分岐を最小記録する |
| B-8 |
D-16 |
テスト配分(単体/統合/E2E)を合意する |
追加(任意):
| テンプレ |
記入例 |
ねらい |
| B-9 |
D-17 |
相互作用を焦点化し、設計の介入点を作る |
| B-10 |
D-18 |
失敗理由(code)を契約として固定する |
| B-11 |
D-19 |
要件→仕様→設計→テストの対応を最小で維持する |
| B-12 |
D-20 |
受け入れ条件(AC)をID化し、会話コストを下げる |
| B-13 |
D-21 |
認可ルールを表形式で合意し、仕様/テストの前提を揃える |
| B-14 |
D-22 |
API の入出力・エラー・冪等性を契約として固定する |
| B-15 |
D-23 |
整合性制約と同時更新の方針を合意する |
| B-16 |
D-24 |
外部I/Fの失敗時期待(継続/再送/検知)を仕様として固定する |
D-1. 要求(Needs / Goals)の記入例(B-1)
テーマ: 「タスク割り当て + 通知(メール)」
背景/課題(As-Is)
- 何が起きているか(事実): タスクが割り当てられても担当者が気づかず、着手が遅れる
- 誰が困っているか: 管理者(進捗管理・調整コスト)、担当者(見落としによる期限超過)
- 放置するとどうなるか(損失/機会損失): 期限超過の増加、リマインド/再割り当ての手間、監査(誰が割り当てたか)の不備
目的(Goals / To-Be)
- 目標状態(業務/体験): 管理者の割り当てが担当者に伝わり、期限内に着手/完了できる
- 期限(いつまでに): 次のリリースまで(例)
KPI(測定)
| 指標 |
現状 |
目標 |
測定方法 |
備考 |
| 割り当て後 24h 未着手率 |
30%(例) |
10%(例) |
assignedAt→in_progress までの差分を集計 |
タスク種別で分ける余地 |
| 期限超過率 |
15%(例) |
5%(例) |
dueAt と doneAt を集計 |
期限ルール変更で変動 |
| 通知失敗の検知遅延 |
2日(例) |
当日(例) |
失敗ログ/アラートの検知時間を集計 |
運用体制に依存 |
スコープ/非ゴール
- 含む:
- 管理者による担当者割り当て
- 担当者への通知(メール)
- 失敗時の検知と再送方針の明文化
- 含まない(非ゴール):
- 通知チャネルの多重化(Slack 等)
- 高度な配信保証(厳密な exactly-once)
- 既読・承認フロー(通知に対するリアクション管理)
制約/前提
- 体制: 単一チーム、単一リポジトリ
- 外部I/F: メール送信はネットワーク越しの外部サービス
- 運用: 通知失敗は業務影響を持つため、検知手段が必要
D-2. 要件(Requirements / Shall)の記入例(B-2)
スコープ
- 含む:
- タスクに担当者を割り当てできる
- 割り当て時に担当者へ通知できる
- 割り当て操作の監査ができる
- 含まない(非ゴール):
- 通知チャネルの多重化(Slack 等)
- 高度な配信保証(厳密な exactly-once)
機能要件(Shall)
| ID |
Shall(要件) |
根拠(要求/KPI) |
優先度 |
備考 |
| R-1 |
システムは管理者がタスクに担当者を割り当てできること |
未着手率の低減 |
Must |
|
| R-2 |
システムは権限により割り当て操作を制御すること(管理者のみ) |
セキュリティ/監査 |
Must |
|
| R-3 |
システムは割り当て(誰が/いつ/誰に/どのタスク)を記録できること |
監査 |
Should |
監査要件次第で Must |
| R-4 |
システムは割り当て時に担当者へ通知できること |
未着手率の低減 |
Must |
通知チャネルは仕様で固定 |
| R-5 |
システムは通知が失敗しても割り当ての確定を妨げないこと |
業務継続 |
Must |
失敗は検知可能であること |
| R-6 |
システムは通知失敗を運用で検知できること |
検知遅延の短縮 |
Must |
ログ/メトリクス/アラート |
| R-7 |
システムは存在しないタスクへの割り当てを失敗として扱うこと |
品質 |
Must |
エラー形式は仕様で定義 |
非機能(最小合意)
- 性能: 割り当て操作は通常時 1 秒以内(例: p95)で完了する
- 可用性: メンテ時間帯を除き稼働(SLA/SLO は将来導入なら「未定」でもよい)
- セキュリティ/権限: 管理者/一般ユーザーを識別し、割り当ては管理者のみに制限する
- 監査/ログ: 割り当て(誰が/いつ/誰に/どのタスク)を検索可能な形で残す
- 運用(アラート/リトライ/バックアップ): 通知失敗が検知でき、再送方針(手動/自動)が明文化されている
未決/リスク
- 通知失敗時の再送方式(手動/自動、再送回数、間隔)
- 監査ログの保存期間・閲覧権限
- 同時更新(競合)の扱い(後勝ち/排他/409 など)
D-3. 仕様(Specification / Behavior)の記入例(B-3)
参照(要求/要件)
- 要求: D-1(割り当ての見落としを減らす)
- 要件: R-1〜R-7
外部I/F(観測できる面)
- UI:
- 管理者はタスク詳細で担当者を選択し「割り当て」を実行できる
- 成功時に「割り当て完了」を表示し、担当者表示が更新される
- API(例):
POST /api/tasks/{taskId}/assignment body: { "assigneeId": "user-123" }
- 成功時は
200 を返す
- 外部連携(通知):
- 割り当て時に担当者へ通知(メール)を送信する
- メール送信の失敗/遅延が発生し得るため、失敗時の期待(業務継続/検知/再送)を定義する
受け入れ条件(Given/When/Then)
- Given: 管理者がログインしており、タスクが
todo 状態で存在する
- When: 管理者が担当者
user-123 を割り当てる
- Then:
- タスクの
assigneeId が user-123 に更新される
- 画面に「割り当て完了」が表示される
- 担当者に「タスクが割り当てられた」旨の通知(メール)が送信される
- Given: 管理者がログインしており、対象タスクが存在しない
- When: 管理者が割り当て操作を行う
- Then:
- API は
404 を返す
- UI は「対象タスクが見つからない」旨を表示する
- 監査/ログに「対象なし」が記録される(必要なら)
- Given: メール送信サービスが一時的に失敗する
- When: 管理者が割り当て操作を行う
- Then:
- タスクの割り当て自体は成功する(業務継続を優先)
- 送信失敗が運用で検知できる(ログ、メトリクス、アラートのいずれか)
- 再送の方針(手動/自動)が明文化されている
例外系/失敗時の振る舞い(Behavior)
- 認可失敗:
- 管理者以外が割り当て API を呼ぶと
403(または同等)を返す
- 存在しない/不正入力:
assigneeId が不正な形式なら 400(または同等)を返す
- 状態/業務ルール違反:
- タスクが割り当て可能な状態でない場合(例:
done)は 409(または同等)を返す
- 競合(同時更新):
- 更新競合が発生した場合の扱いを固定する(例:
409、または後勝ち。どちらでも良いが曖昧にしない)
- 外部I/F失敗(通知):
- 割り当て確定の合否と、通知送信の合否を混同しない(R-5)
- 失敗の検知方法と再送の起点を固定する(ログ/キュー/手順)
- 冪等性(二重送信):
- 同一タスクに同一担当者を再割り当てしても、結果が重複しない(少なくとも監査ログや通知が増殖しない)
- 時間/期限:
- 期限が過去の場合の扱い(許可/拒否/警告)を固定する(例: 割り当ては許可するが UI で警告する)
- 監査/ログ:
403/404/400/409 等の失敗も、原因追跡できる粒度で記録する(ただし個人情報をログに出さない)
観測点(何をもって合否判定するか)
- 画面表示: 担当者の表示が更新され、成功/失敗が表示できる
- API レスポンス: ステータスコード(200/403/404 等)とエラー形式(最低限
code と message)が一定
- 監査/ログ: 割り当てイベント(actor/task/assignee/時刻)と通知失敗が検索できる
テスト観点(仕様から導出)
- 単体テストで守る振る舞い: 権限判定、状態遷移、期限ルールなど(純粋ロジック)
- 統合テストで守る契約(境界): API+DB の永続化、認可接続、通知I/Fの失敗時の扱い
- E2E で守る導線: 管理者が割り当て、担当者表示が更新される(メール到達そのものはE2Eに含めない)
D-4. 設計(Design / Structure)アウトラインの記入例(B-5)
参照(要求/要件/仕様)
- 要求: D-1
- 要件: R-1〜R-7
- 仕様: D-3
構造(境界と責務)
- domain:
- 権限ルール(管理者のみ割り当て可)
- 状態遷移(
todo → in_progress → done)と割り当て可否
- 期限判定(表示用ステータス等)
- usecases:
assignTask(割り当ての確定、通知要求、監査の記録)
- 依存(DB/通知/時刻/認可)を port として引数で受ける
- adapters:
- HTTP(ルーティング、入力検証、エラー形式)
- DB(永続化、監査ログ)
- mailer(外部メールI/F)
依存方向(ルール)
- domain は I/O やフレームワークに依存しない
- usecases は domain と port にのみ依存する
- adapters が port を実装し、外部 I/F(DB/メール等)を扱う
データモデル
- 論理(概念/エンティティ):
- Task:
id/title/status/dueAt/assigneeId/assignedAt
- AssignmentAudit:
taskId/actorId/assigneeId/assignedAt
- 物理(DB テーブル例):
tasks: id, title, status, due_at, assignee_id, assigned_at
audit_logs: id, action, task_id, actor_id, payload, created_at
重要アルゴリズム/ルール
- 管理者のみ割り当て可能(認可は境界で enforce し、domain ルールとしても保持する)
- 完了済み(
done)への割り当ては不可(例。運用要件に合わせて固定する)
トレードオフ
- 得るもの: 割り当て確定と通知を分離でき、外部I/Fの失敗で業務確定が止まりにくい
- 失うもの: 通知が即時に届く保証は弱くなるため、検知と再送の運用が必要
テスト戦略への影響
- domain は単体を厚く、adapters 境界は統合で守る
- E2E は価値導線に限定し、外部メール到達は含めない(フレーク源になりやすい)
D-5. S/D/V 採点(依存関係トップ)の記入例(B-4)
例として、依存関係トップ 10 のうち代表的なものを記入します。
| # |
依存元 |
依存先 |
依存の内容 |
S |
D |
V |
コメント(観測/根拠) |
手当案 |
| 1 |
UI |
API |
割り当て操作の入出力・エラー形式 |
4 |
3 |
3 |
UI/API で入力検証・権限表示がズレると破綻 |
仕様(D-3)を固定 + 統合テスト |
| 2 |
usecases |
authz |
権限判定(管理者のみ割り当て可) |
4 |
3 |
3 |
認可ルール変更が UI/API に波及しやすい |
判定を domain 側へ寄せ、単体で守る |
| 3 |
usecases |
db |
割り当ての永続化と監査ログ |
4 |
3 |
2 |
同時変更は必須、ただし変動性は低め |
schema/DAO を境界に固定 + 統合テスト |
| 4 |
usecases |
mailer |
割り当て通知の送信 |
2 |
4 |
3 |
外部I/Fで失敗が起きる、運用要件が増えやすい |
adapter 化 + 失敗時の扱いを ADR 化 |
採点の狙い:
- 「今すぐ手当すべき」ものは、S と D と V が同時に悪い領域になりやすい
- ただし S が強い領域は「分離」より「一体で扱う」ほうがコストが低い場合がある(章 03 の判断原則)
D-6. Change Drivers(記入例)(B-6)
対象: 「通知を境界として強める」
背景(何が起きているか)
- 事象(観測): 通知失敗が顕在化し、問い合わせが発生した
- 影響(価値/運用/コスト): 担当者が気づかず、期限超過が増える
- 期限(いつまでに): 次のリリースまでに検知手段を用意したい
ドライバー(何が変更を必要にしているか)
| ドライバー |
内容 |
強さ(低/中/高) |
根拠 |
| 変動性(V) |
通知要件が増える |
中 |
追加要望(再送/テンプレ)が出ている |
| 距離(D) |
外部メール I/F |
高 |
失敗/遅延が発生する |
| 統合強度(S) |
割り当て確定は必須 |
高 |
業務確定のため |
| 運用要件 |
失敗検知が必要 |
高 |
問い合わせが発生した |
| チーム/組織 |
単一チーム |
低 |
現時点では分割不要 |
対応方針(案)
- 何を変える: 通知を adapter として明確化し、失敗を検知できる形にする
- 変えないこと(非ゴール): 高度な配信保証、複数チャネル対応
- テスト戦略への影響: 通知は統合テスト中心、E2E には入れない
- ADR が必要か: 必要(通知の扱いは重大な分岐)
D-7. ADR(記入例)(B-7)
ADR: 通知(メール)失敗時の扱いを「業務継続優先」にする
- Status: accepted
- Date: 2026-01-12
- Deciders: 開発チーム
- Scope: タスク割り当て/通知
Context
- 背景: 割り当て時に通知(メール)が必要になった
- 問題: メール送信は外部 I/F であり、失敗/遅延が発生する
- 制約: 割り当ては業務上の確定であり、通知失敗で割り当て自体を失敗させたくない
Decision
- 採用する決定: 割り当ての永続化は成功させ、通知失敗は「検知 + 再送方針」で扱う
- 理由: D が大きい外部 I/F である一方、割り当ての S は強い(同時変更が必須)。境界(adapter)で失敗を吸収し、ユースケースは業務確定を優先する
Consequences
- 得られる効果: 業務フローが外部障害に引きずられにくい
- 失うもの/トレードオフ: 「通知が届いていない」可能性が残るため、運用で検知する必要がある
- 後続タスク: 失敗ログ/メトリクス整備、再送手順の明文化
- 見直し条件: 通知が SLA 対象になった、または失敗が頻発した場合
D-8. テスト配分(記入例)(B-8)
対象: 「タスク割り当て + 通知(メール)」
方針(ねらい)
- 価値導線: 管理者が割り当て、担当者が着手/完了できること
- 主要リスク: 権限漏れ、割り当て不整合、通知失敗の検知漏れ
- 許容できない障害: 権限逸脱、データ不整合(割り当てが失われる)
配分(例)
| レベル |
ねらい |
対象 |
備考 |
| 単体 |
コアの振る舞い保護 |
権限判定、状態遷移、期限判定 |
時刻は注入して固定 |
| 統合 |
境界の契約保護 |
API+DB、認可、通知 I/F(失敗時の扱い) |
外部メールは stub/spy で契約固定 |
| E2E |
価値導線の保護 |
管理者が割り当て、担当者表示が更新される |
メール到達は含めない |
対象別の分類(例)
| 対象 |
単体 |
統合 |
E2E |
補足 |
| 権限判定 |
○ |
○ |
△ |
主要導線で E2E も可 |
| DB 永続化 |
- |
○ |
△ |
schema/クエリの契約を守る |
| 通知(メール) |
- |
○ |
- |
外部 I/F は統合で stub 化 |
| 主要導線(割り当て) |
- |
△ |
○ |
E2E は少数精鋭 |
配分の目安(例):
D-9. 要求(Needs / Goals)の記入例(B-1)
テーマ: 「期限超過の表示 + 通知(メール)」
背景/課題(As-Is)(期限超過)
- 何が起きているか(事実): 期限が近い/超過したタスクが見落とされ、着手や調整が遅れる
- 誰が困っているか: 担当者(見落としによる期限超過)、管理者(期限超過の後追い・調整コスト)
- 放置するとどうなるか(損失/機会損失): 期限超過率の上昇、手動リマインドの増加、説明責任(いつ気づけたか)が曖昧
目的(Goals / To-Be)(期限超過)
- 目標状態(業務/体験): 期限が近い/超過したタスクが UI で即座に判別でき、期限超過時に担当者へ通知される
- 期限(いつまでに): 次のリリースまで(例)
KPI(測定)(期限超過)
| 指標 |
現状 |
目標 |
測定方法 |
備考 |
| 期限超過率 |
15%(例) |
8%(例) |
dueAt と doneAt の差分を集計 |
タスク種別で分ける余地 |
| 平均期限超過日数 |
3日(例) |
1日(例) |
doneAt - dueAt の平均 |
外れ値に注意 |
| 期限超過の検知遅延 |
2日(例) |
当日(例) |
dueAt と通知/表示のタイミングを集計 |
運用体制に依存 |
スコープ/非ゴール(期限超過)
- 含む:
- 期限(
dueAt)を基にした「期限が近い/超過」の表示(UI)
- 期限超過時の担当者への通知(メール)
- 二重通知を避けるための最小の冪等性
- 含まない(非ゴール):
- 複数チャネル通知(Slack 等)
- 複雑なスケジューリング(営業時間、祝日カレンダー、リマインド頻度の多段階)
- 期限計算の高度化(営業日換算、タイムゾーン個別対応)
制約/前提(期限超過)
- タイムゾーン: 初期は単一タイムゾーン(例: JST)で運用する(個別対応は将来課題)
- 期限判定:
dueAt と「現在時刻」の比較で決める(判定ロジックは domain に集約する)
- 外部I/F: メール送信はネットワーク越しの外部サービス
D-10. 要件(Requirements / Shall)の記入例(B-2)
スコープ(期限超過)
- 含む:
- 期限ステータス(期限が近い/超過)を判定できる
- 期限ステータスを UI で表示できる
- 期限超過時に担当者へ通知できる
- 含まない(非ゴール):
- 複数チャネル通知、複雑なスケジューリング、タイムゾーン個別対応
機能要件(Shall)(期限超過)
| ID |
Shall(要件) |
根拠(要求/KPI) |
優先度 |
備考 |
| DUE-1 |
システムは dueAt と現在時刻から期限ステータス(例: ok/due_soon/overdue)を判定できること |
期限超過率の低減 |
Must |
判定ロジックは重複させない |
| DUE-2 |
システムは期限ステータスをタスク一覧/詳細で表示できること |
見落とし低減 |
Must |
UI の観測点を仕様で固定 |
| DUE-3 |
システムはタスクが期限超過になった場合、担当者へ通知できること |
検知遅延の短縮 |
Should |
初期はメールのみ |
| DUE-4 |
システムは期限超過通知を重複して送らないこと(冪等性) |
運用負債の抑制 |
Must |
二重実行を前提にする |
| DUE-5 |
システムは期限超過通知の送信結果(成功/失敗)を運用で検知できること |
検知遅延の短縮 |
Must |
ログ/メトリクス/アラート |
非機能(最小合意)(期限超過)
- 性能: 期限ステータス表示により、一覧表示が体感で劣化しないこと(測定対象を合意する)
- 可用性: 主要導線(一覧/詳細表示)が営業時間内に利用できること
- セキュリティ/権限: 期限情報は閲覧権限に従うこと(権限逸脱を許容しない)
- 監査/ログ: 期限超過通知の実行(対象/時刻/結果)が追跡できること
未決/リスク(期限超過)
due_soon の閾値(例: 2日)と見直し条件
- 期限超過の検知タイミング(即時/定期)と許容遅延
- タイムゾーン個別対応が必要になった場合の方針
D-11. 仕様(Specification / Behavior)の記入例(B-3)
参照(要求/要件)(期限超過)
外部I/F(観測できる面)(期限超過)
- UI:
- 一覧で期限ステータス(
ok/due_soon/overdue)が表示される
- 期限超過は強い視認性(例: バッジ)で判別できる
- API(例):
GET /api/tasks は dueAt と期限ステータス(または表示に必要な情報)を返す
- 外部連携(通知):
- 期限超過時に担当者へ通知(メール)を送信する
- 外部I/Fの失敗/遅延が起き得るため、失敗時の期待(検知/再送)を定義する
受け入れ条件(Given/When/Then)(期限超過)
- Given: タスクに
dueAt=2026-01-10 が設定されている(JST)
- When: 現在日が
2026-01-09 で一覧を表示する
- Then:
- 期限ステータスは
due_soon として表示される
- Given: タスクに
dueAt=2026-01-10 が設定されている(JST)
- When: 現在日が
2026-01-11 で一覧を表示する
- Then:
- 期限ステータスは
overdue として表示される
- Given: タスクが期限超過になった(
dueAt < now)
- When: 期限超過の検知が実行される(例: 定期ジョブ)
- Then:
- 担当者へ期限超過通知(メール)が送信される
- 通知の実行結果(成功/失敗)が追跡できる
例外系/失敗時の振る舞い(Behavior)(期限超過)
- 入力/データ不備:
dueAt が未設定の場合は ok として扱う(表示は「期限なし」等)
- 時刻/タイムゾーン:
- 期限判定のタイムゾーンを固定し、UI/API/DB で解釈がズレないようにする(曖昧にしない)
- 冪等性(二重実行):
- 同一タスクに対する期限超過通知は重複しない(少なくとも同一の通知が増殖しない)
- 外部I/F失敗(通知):
- 送信失敗は運用で検知でき、再送の起点が残る(再送方式の詳細は要件化してから決める)
観測点(何をもって合否判定するか)(期限超過)
- UI:
overdue が視認でき、一覧で取りこぼさない
- API: 期限ステータス(または表示に必要な情報)が一定の形式で取得できる
- 運用: 通知実行の成功/失敗が追跡できる(ログ/メトリクス/監査ログ)
テスト観点(仕様から導出)(期限超過)
- 単体テストで守る振る舞い: 期限判定ロジック(
ok/due_soon/overdue)、閾値の扱い、タイムゾーン解釈
- 統合テストで守る契約(境界): 期限表示の入出力、通知実行の記録、失敗時の検知/再送起点
- E2E で守る導線: 一覧で期限超過が視認でき、重要導線(作成/割り当て/完了)の妨げにならない
D-12. 設計(Design / Structure)アウトラインの記入例(B-5)
参照(要求/要件/仕様)(期限超過)
- 要求: D-9
- 要件: DUE-1〜DUE-5
- 仕様: D-11
構造(境界と責務)(期限超過)
- domain:
- 期限判定(
now と dueAt からステータスを返す)
- タイムゾーン解釈の前提(固定)を持つ
- usecases:
- 一覧表示用の DTO 生成(期限ステータスの付与)
- 期限超過通知の実行(対象抽出、通知要求、実行結果の記録)
- adapters:
- HTTP(一覧取得、表示用データの返却)
- scheduler(定期実行の起点。詳細は運用と合意して決める)
- DB(タスク、通知実行ログ/監査ログ)
- mailer(外部メールI/F)
依存方向(ルール)(期限超過)
- 期限判定は domain に集約し、UI/API が独自に判定しない
- scheduler は「起点」であり、判定ロジックを持たない(usecases を呼ぶ)
トレードオフ(期限超過)
- 通知を「いつ送るか」を厳密にし過ぎると、実装/運用コストが増える
- まずは表示(UI)を確実にし、通知は最小の契約(重複しない/失敗が追える)から始める
D-13. S/D/V 採点(依存関係トップ)の記入例(B-4)
| 依存 |
S |
D |
V |
観測(根拠) |
手当(方向性) |
| UI と API が期限判定を重複実装 |
3 |
2 |
4 |
ルール変更時に両方同時変更が必要 |
判定を domain に集約し、UI/API は結果を利用 |
| 期限超過通知(usecases → mailer) |
2 |
4 |
3 |
外部I/Fの失敗・遅延が現実に起きる |
通知要求と送信を分離、失敗検知/再送起点を固定 |
| scheduler(定期起点)と期限判定 |
2 |
3 |
3 |
実行タイミングの議論が運用に波及 |
起点を最小化し、要件化した時点で強化 |
D-14. Change Drivers(記入例)(B-6)
対象: 「期限超過の見落としを減らす」
背景(何が起きているか)(期限超過)
- 期限超過が一定割合で発生し、管理者の調整コストが上がっている
- 期限が近いタスクを一覧で拾えず、手動リマインドが増えている
ドライバー(何が変更を必要にしているか)(期限超過)
- 期限判定ルールが変更される可能性(V)と、UI/API の重複実装による手戻り
- 通知の外部I/F(D)が大きく、失敗時の運用が未整備
対応方針(案)(期限超過)
- 期限判定を domain に集約し、表示と判定のズレを無くす
- 通知は最小契約(重複しない/失敗が追える)で開始し、必要に応じて再送を強化する
D-15. ADR(記入例)(B-7)
ADR: 期限超過通知は「重複しない最小契約」で開始する
Context(期限超過通知)
- 期限超過の見落としが課題だが、複雑なスケジューリングを初期から導入すると過剰設計になりやすい
- 外部メールI/Fは失敗し得るため、到達性をE2Eで直接保証しにくい
Decision(期限超過通知)
- 期限超過通知は「同一タスクで重複しない」ことを契約として固定し、送信失敗は検知可能にする
- 通知の送信タイミングは運用と合意できる最小単位(例: 定期ジョブ)で開始し、要件化した時点で強化する
Consequences(期限超過通知)
- 良いこと: 早期に価値(見落とし低減)を出しつつ、後から運用要件に合わせて強化できる
- 失うもの/トレードオフ: “即時通知”は保証しないため、通知遅延を許容する前提が必要
- 見直し条件: 通知が SLA 対象になった、または遅延が業務影響になった場合
D-16. テスト配分(記入例)(B-8)
対象: 「期限超過の表示 + 通知(メール)」
方針(ねらい)(期限超過)
- 価値導線: 期限超過を見落とさず、着手/完了に繋げる
- 主要リスク: 期限判定のズレ、二重通知、通知失敗の検知漏れ
- 許容できない障害: 期限超過が表示されない、権限逸脱、データ不整合
配分(例)(期限超過)
| レベル |
ねらい |
対象 |
備考 |
| 単体 |
コアの振る舞い保護 |
期限判定(時刻注入)、閾値、タイムゾーン前提 |
now() を注入して固定 |
| 統合 |
境界の契約保護 |
一覧の入出力、通知実行ログ、失敗時の検知 |
外部メールは stub/spy で契約固定 |
| E2E |
価値導線の保護 |
一覧で overdue が視認できる |
メール到達は含めない |
配分の目安(例):
D-17. 相互作用マップ(記入例)(B-9)
対象: ケース2(期限超過の表示 + 通知)
| 変更シナリオ |
波及先(例) |
観測点(例) |
制約/最小手当(例) |
次アクション(接続) |
due_soon の閾値変更(2日→3日) |
UI 表示、API 出力、並び替え、テスト期待値 |
UI のバッジ、API の dueStatus、単体テストの境界値 |
期限判定を domain に集約し、UI/API の重複実装を禁止(時刻は注入) |
依存(UI↔API)の S/D/V 採点→テスト配分(単体/統合/E2E) |
| タイムゾーンの解釈変更(JST固定→将来拡張) |
期限判定、保存形式、表示、運用手順 |
判定結果の差分、ログの時刻、監査 |
タイムゾーンの前提を仕様として固定し、入出力形式を境界で統一する |
仕様(観測点)を再定義→統合テストで契約を固定 |
| 期限超過通知の実行タイミング変更(定期→準リアルタイム) |
scheduler、通知I/F、運用監視、再送 |
実行ログ、失敗検知、遅延の測定 |
起点(scheduler)を薄く保ち、通知要求の記録を契約にする(到達性は外部要因) |
Change Drivers→ADR(必要なら)で進化条件を明文化 |
D-18. エラーコードカタログ(記入例)(B-10)
対象: ケース1(割り当て API: POST /api/tasks/{taskId}/assignment)
| code |
message(利用者向け) |
起点(境界) |
HTTP |
再試行可否 |
監査/ログ |
備考 |
| forbidden |
権限がありません |
認可 |
403 |
No |
actor/taskId/code/相関ID |
管理者以外の割り当ては拒否 |
| not_found |
対象タスクが見つかりません |
参照 |
404 |
No |
actor/taskId/code/相関ID |
存在しないID参照 |
| invalid |
入力が不正です |
入力検証 |
400 |
No |
actor/code/相関ID |
個人情報はログに出さない |
| conflict |
競合が発生しました |
同時更新 |
409 |
Yes/No |
actor/taskId/code/相関ID |
楽観ロック等(方針を固定) |
補足:
- 通知(メール)送信失敗は、割り当ての合否と混同しない(ケース1 D-3/D-5)。API の失敗として返すのではなく、運用で検知できる記録(ログ/メトリクス/監査)に落とす。
D-19. トレーサビリティマップ(記入例)(B-11)
本例では、仕様(受け入れ条件/例外系)の参照を「D-3/D-11 のセクション名」で行います。受け入れ条件に ID(例: AC-1)を振る例は D-20 を参照してください。
ケース1(AC-ID): タスク割り当て + 通知
| 要件ID |
Shall(要約) |
仕様(受け入れ条件/観測点) |
設計(境界/ADR/エラーcode等) |
テスト(単体/統合/E2E) |
状態 |
備考 |
| R-1 |
管理者が担当者を割り当てできる |
D-3「受け入れ条件」/「観測点」 |
D-4(assignTask) |
D-8(統合/E2E) |
完了 |
主要導線 |
| R-2 |
管理者のみ割り当て可能(認可) |
D-3「例外系: 認可失敗」 |
D-4(domain ルール), D-18(forbidden) |
D-8(単体/統合) |
完了 |
権限逸脱は許容しない |
| R-3 |
割り当ての監査記録が残る |
D-3「観測点: 監査/ログ」 |
D-4(AssignmentAudit), D-7(監査の扱い) |
D-8(統合) |
完了 |
保存期間は運用要件次第 |
| R-4 |
割り当て時に通知できる |
D-3「外部連携(通知)」 |
D-4(mailer adapter) |
D-8(統合: stub/spy) |
完了 |
到達性は外部要因 |
| R-5 |
通知失敗でも割り当て確定は成立 |
D-3「外部I/F失敗」 |
D-4(分離方針), D-7(意思決定) |
D-8(統合) |
完了 |
再送は運用で扱う |
| R-6 |
通知失敗を運用で検知できる |
D-3「観測点: 監査/ログ」 |
D-4(ログ/アラート前提) |
D-8(統合) |
完了 |
相関ID があると解析が短い |
| R-7 |
存在しないタスクは失敗 |
D-3「例外系: Not Found」 |
D-18(not_found) |
D-8(統合) |
完了 |
|
ケース2(AC-ID): 期限超過の表示 + 通知
| 要件ID |
Shall(要約) |
仕様(受け入れ条件/観測点) |
設計(境界/ADR/エラーcode等) |
テスト(単体/統合/E2E) |
状態 |
備考 |
| DUE-1 |
期限ステータスを判定できる |
D-11「受け入れ条件」/「時刻/タイムゾーン」 |
D-12(期限判定を domain へ) |
D-16(単体) |
完了 |
時刻は注入して固定 |
| DUE-2 |
期限ステータスを表示できる |
D-11「外部I/F: UI」/「観測点」 |
D-12(DTO に必要情報のみ) |
D-16(統合/E2E) |
完了 |
表示要件は変動しやすい |
| DUE-3 |
期限超過時に通知できる |
D-11「外部連携(通知)」 |
D-12(通知 I/F を境界化) |
D-16(統合) |
完了 |
|
| DUE-4 |
期限超過通知が重複しない |
D-11「冪等性(二重実行)」 |
D-12(冪等キー/記録) |
D-16(単体/統合) |
完了 |
二重実行を前提にする |
| DUE-5 |
通知結果を運用で検知できる |
D-11「観測点」 |
D-12(ログ/メトリクス) |
D-16(統合) |
完了 |
|
D-20. 受け入れ条件(AC)ID 管理(記入例)(B-12)
対象: ケース1(タスク割り当て + 通知)
| AC ID |
要件ID |
Given(前提) |
When(操作/イベント) |
Then(期待結果/観測点) |
例外系/エラーcode |
推奨テスト |
備考 |
| AC-1 |
R-1, R-4 |
管理者がログインしており、タスクが存在する |
担当者を割り当てる |
担当者が更新され、成功が表示される |
- |
E2E/統合 |
導線の最小セット |
| AC-2 |
R-2 |
一般ユーザーがログインしている |
割り当てを実行する |
forbidden として失敗する |
forbidden |
統合/単体 |
仕様は D-3、code は D-18 |
| AC-3 |
R-7 |
管理者がログインしている |
存在しないタスクに割り当てる |
not_found として失敗する |
not_found |
統合 |
|
| AC-4 |
R-5, R-6 |
管理者がログインしている |
割り当て時に通知が失敗する |
割り当ては成立し、失敗が追跡できる |
- |
統合 |
外部I/F到達性は分離する |
対象: ケース2(期限超過の表示 + 通知)
| AC ID |
要件ID |
Given(前提) |
When(操作/イベント) |
Then(期待結果/観測点) |
例外系/エラーcode |
推奨テスト |
備考 |
| AC-5 |
DUE-1, DUE-2 |
dueAt=2026-01-10(JST) |
now=2026-01-09 で一覧を表示 |
due_soon として表示される |
- |
単体/統合 |
判定ロジックは domain に集約 |
| AC-6 |
DUE-1, DUE-2 |
dueAt=2026-01-10(JST) |
now=2026-01-11 で一覧を表示 |
overdue として表示される |
- |
単体/統合 |
|
| AC-7 |
DUE-3〜DUE-5 |
期限超過タスクが存在する |
検知(例: 定期ジョブ)が実行される |
通知が実行され、結果が追跡できる |
- |
統合 |
重複通知は DUE-4 で抑止 |
D-21. 認可(Authorization)ルール表(記入例)(B-13)
前提(例):
- 役割:
admin / user
- 属性: タスクには
assigneeId があり、担当者は assigneeId == actorId
- 失敗時の振る舞い: 認可失敗は
forbidden/403(not_found と混同しない)
| 操作ID |
操作(Action) |
対象(Resource) |
許可条件(Role/属性) |
失敗時(code/HTTP) |
監査/ログ |
推奨テスト |
備考 |
| AUTHZ-1 |
assign_task |
task |
admin のみ |
forbidden/403 |
actor/taskId/code/相関ID |
統合/単体 |
UI の表示制御に依存しない |
| AUTHZ-2 |
update_status |
task |
assigneeId == actorId |
forbidden/403 |
actor/taskId/code/相関ID |
統合/単体 |
状態遷移ルールは D-3/D-12 を参照 |
| AUTHZ-3 |
view_task |
task |
admin または assigneeId == actorId |
forbidden/403 |
actor/taskId/code/相関ID |
統合 |
一覧/詳細の両方に適用 |
D-22. API 契約(記入例)(B-14)
対象: ケース1(割り当て API: POST /api/tasks/{taskId}/assignment)
関連(要求/要件/受け入れ条件)
- 要件ID: R-1, R-2, R-4, R-5, R-7
- AC ID: AC-1, AC-2, AC-3, AC-4
- 認可ルール(参照): D-21(AUTHZ-1)
エンドポイント
- method/path:
POST /api/tasks/{taskId}/assignment
- 認証/認可: 認証必須、認可は
admin のみ
Request
- headers(任意):
Idempotency-Key: 二重送信対策(クライアントが送れる場合)
- params:
taskId(path): 割り当て対象タスクID
- body:
assigneeId(必須): 割り当て先のユーザーID
Response(Success)
- status:
200
- body(例):
taskId: "task-123"
assigneeId: "user-123"
assignedAt: "2026-01-10T12:34:56Z"(例: ISO 8601)
Response(Error)
最小形式(例):
{ "code": "<reason>", "message": "<user-facing>" }
| code |
HTTP |
条件(いつ起きるか) |
再試行可否 |
備考 |
| forbidden |
403 |
認可失敗(admin 以外) |
No |
D-18 |
| not_found |
404 |
taskId が存在しない |
No |
D-18 |
| invalid |
400 |
assigneeId の形式不正等 |
No |
D-18 |
| conflict |
409 |
同時更新/状態ルール違反 |
Yes/No |
D-18 |
冪等性(二重送信)/同時更新(競合)
- 冪等性の方針(例):
Idempotency-Key が同一なら、同一結果(同一レスポンス)に収束させる
Idempotency-Key が無い場合でも、同一 taskId に同一 assigneeId の再割り当ては no-op とし、通知や監査ログが増殖しない(R-5 の運用負債を抑制)
- 同時更新の方針(例):
- 競合時は
conflict/409 を返す(後勝ちにする場合も、方針を曖昧にしない)
ログ/監査(最小)
- 監査/ログ: actor、taskId、assigneeId、code(失敗時)、相関ID
- 個人情報はログに出さない(ID と code を中心にする)
D-23. データ整合性/同時更新(記入例)(B-15)
対象: ケース1(割り当て)+ ケース2(期限超過通知の重複抑止)
不変条件(Invariant)
| ID |
不変条件(守るべきこと) |
破ると何が起きるか |
守る場所(domain/DB/両方) |
備考 |
| I-1 |
存在しないタスクへ割り当てできない |
監査/通知/表示が破綻する |
両方 |
API は not_found/404(D-18) |
| I-2 |
done のタスクへ割り当てできない(例) |
状態遷移が破綻する |
domain |
conflict/409(D-18) |
| I-3 |
割り当て変更は監査ログに記録される |
説明責任が満たせない |
両方 |
保存期間は運用要件次第 |
| I-4 |
期限超過通知は重複しない(DUE-4) |
運用負債(メール増殖) |
両方 |
二重実行を前提にする |
整合性境界(トランザクション)
- 同一トランザクションで成立させるもの(例):
- タスクの
assigneeId/assignedAt 更新
- 割り当て監査ログの追記
- 通知要求の記録(送信の実行は別扱い)
- 結果整合でよいもの(例):
- メール到達(外部I/Fのため。失敗時の検知/再送は別途設計)
同時更新(競合)方針
- 方針(例): 楽観ロック
tasks に version(または updatedAt)を持ち、更新時に一致確認する
- 競合時は
conflict/409 を返す(後勝ちにする場合も、方針を曖昧にしない)
- 再試行(例):
- API: クライアント側で再試行するかは、業務影響とUXを見て決める(上限/間隔を固定)
- 定期ジョブ(期限超過通知): 冪等性が担保できるなら再試行可(重複抑止が前提)
DB 物理(任意: 例)
- UNIQUE 制約(例):
- 期限超過通知の重複抑止:
(task_id, notification_type) の UNIQUE
- バージョン列(例):
tasks.version(整数のインクリメント)
D-24. 外部I/F失敗時設計(記入例)(B-16)
対象: ケース1(割り当て通知: メール送信)
前提/非ゴール
- 外部I/F: メール送信サービス(ネットワーク越し)
- 失敗が業務に与える影響:
- 通知失敗でも割り当ては成立させる(R-5)
- 通知失敗は運用で検知できる(R-6)
- 非ゴール:
- 厳密な exactly-once は目指さない(重複抑止は「自系の範囲」で行う)
- 外部サービスの SLA を自系で保証しない(遅延/障害は起こり得る)
失敗分類(観測できること)
| code(運用) |
失敗種別 |
例 |
再試行 |
期待する挙動(仕様) |
観測点(ログ/メトリクス) |
備考 |
| NOTIFY-1 |
TIMEOUT |
タイムアウト |
Yes |
通知要求は失わず、再送起点(outbox)に残る |
code/相関ID/attempt |
一時障害 |
| NOTIFY-2 |
5XX |
500/503 |
Yes |
自動再試行(上限あり)。上限超過はアラート |
failed/backlog |
無限リトライ禁止 |
| NOTIFY-3 |
RATE_LIMIT |
429 |
Yes |
バックオフして再試行 |
retry_scheduled |
レート制限 |
| NOTIFY-4 |
4XX |
400/401/403 |
No |
恒久失敗として保管し、手動対応 |
failed |
設定ミス/宛先不正 |
失敗時の業務継続(同期/非同期)
- 分離: する(割り当て確定とメール送信を混同しない)
- 外部挙動(例: 割り当て API):
- 割り当てが成立したら
200 を返す(メール送信の成功は待たない)
- 「通知は遅延する可能性がある」を利用者へ明示(UI/ヘルプ/運用)
- 停止させる条件:
再試行/再送方針
- 自動再試行: Yes
- 上限: 3回(例)
- 間隔: 1分→10分→1時間(例: バックオフ)
- 実行主体: 定期ジョブ(ワーカー)が outbox をポーリングして送信
- 手動再送: Yes
- 起点: 失敗一覧(管理画面)または運用スクリプト
- 権限: admin のみ
- 手順(例):
failed を pending に戻し再送(相関IDで追跡)
- 冪等性/重複抑止(例):
dedupeKey = assignment:{taskId}:{assignedAt}
dedupeKey を UNIQUE にし、二重登録を防ぐ
- 外部サービスが冪等性キーを受け付ける場合は header に付与(可能なら)
永続化/キュー(任意: 例)
- outbox テーブル(例):
notification_outbox
- columns(例):
id, type, taskId, recipientId, dedupeKey, status, attempts, nextAttemptAt, lastErrorCode, lastErrorAt, createdAt
- 状態(例):
pending → sending → sent / failed
- DLQ/保管先(例):
failed 行を保持し、手動再送の対象にする
- 保持期間(例):
sent: 30日(監査要件に応じて調整)
failed: 90日(復旧/説明責任のため)
監視/アラート(最小: 例)
- 失敗: 直近 1 時間で
failed が 1 件以上 → アラート
- 滞留:
pending が一定件数を超える、または最古の pending が 15 分を超える → アラート
- 相関ID: API リクエスト → outbox → 送信ログまで伝播させ、原因追跡を短縮する
テスト観点(最小)
- 単体: 失敗種別→再試行可否の判定、バックオフ計算
- 統合:
- 通知 I/F が失敗しても割り当ては成功し、outbox に再送起点が残る
dedupeKey により二重送信が増殖しない(再実行/再試行に耐える)