JTBD-012 証憑管理 SaaS アーキテクチャ(暫定版)
ステータス: 暫定・設計中(2026-06-08 時点) 本ドキュメントは確定仕様ではない。各モジュールは ADR / spike で順次確定する。 確定した決定だけが正典で、その出典は本文の ADR / 塊(D 番号)/ RQ 番号で示す。
このドキュメントは何か
JTBD-012「証憑管理」を B2B SaaS の本番アーキで構築し、自社を最初のテナント(tenant-0)として dogfood する方針(ADR-0125)に基づく、アーキテクチャの全体像。 個々の決定(記録正本・認証・取込・保存…)は塊 D1〜D10 に分割して Decision Pipeline で審査する。本書はそれらを 1 枚に合成した俯瞰図。
凡例:
| マーク | 意味 |
|---|---|
| ✅ | 方針確定(ADR 決定済 or 本セッションで確定) |
| 🔴 | spike 待ち(該当 RQ の調査結果が前提=DoR ゲート) |
| ⬜ | 未起案(実装 ADR / EDD で詳細化) |
スコープと非スコープ
| 区分 | 内容 |
|---|---|
| スコープ | 汎用 証憑プロダクトを本番実アーキで構築。電帳法③電子取引を最初の機能スライス。自社=tenant-0 |
| 非スコープ(後回し) | 建設業特化・②スキャナ保存・①優良電子帳簿(機能の深掘り)。アーキは汎用版で一度決め切る |
| dogfood の前提 | 自社 IdP = M365(Entra)→ day-1 認証は Entra。Google 等は顧客対応要件として外部テナントで検証 |
層構成図(レイヤード)
縦軸=責務スタック(上=ユーザーに最も近い UI → 下=保存・基盤)。外部システム(顧客テナント/第三者)はスタックの脇に置き、認証・取込・出力の各点で越境する。横軸=層内のサブ責務でグルーピング。図は D2(diagram-as-code)で生成。
- 自社スタック(上→下): ① プレゼンテーション(UI) → ② インタフェース・認証 → ③ アプリケーション/ドメイン処理(取込・証憑処理)→ ④ 永続化・正本 → ⑤ 基盤
- 外部: 顧客IdP(Entra/Google)・証憑ソース(Outlook/OneDrive・Gmail/Drive)・会計ソフト(弥生/freee/MF)
- ソース:
docs/architecture/arch_jtbd012_voucher_saas.d2/ 再生成:d2 --layout=elk docs/architecture/arch_jtbd012_voucher_saas.d2 docs/architecture/arch_jtbd012_voucher_saas.svg - 色: 青帯=自社レイヤー / 桃=外部 / 橙=モジュール群 / 白=モジュール / 黄=横断。状態: ✅確定 / 🔴spike待ち / ⬜未起案。
信頼境界・配置(デプロイ)図(Mermaid)
誰が管理・ホストするかの境界を示す。正本は自社(案B)に集約し、認証と取込は顧客テナントへ越境、会計ソフトは顧客側に残す。
flowchart TB
classDef cust fill:#fff0f6,stroke:#c2255c,color:#7d1a3b;
classDef vendor fill:#d3f9d8,stroke:#2f9e44,color:#1b4332;
classDef hyper fill:#fff3bf,stroke:#f08c00,color:#663c00;
classDef third fill:#e7f5ff,stroke:#1c7ed6,color:#0b4a8b;
classDef internal fill:#f1f3f5,stroke:#868e96,color:#343a40;
user(["👤 顧客ユーザー"])
subgraph CUST["🏢 顧客テナント(顧客が管理)"]
cidp["顧客IdP
Entra ID(M365) / Google Workspace"]
csrc["証憑ソース
Outlook・OneDrive / Gmail・Drive"]
acc["会計ソフト
弥生 / freee / MF(顧問税理士)"]
cmirror["(将来)ミラー先"]
end
subgraph VENDOR["🟢 自社 SaaS = 案B 正本(CF Workers・dogfood確定)"]
app["証憑プロダクト本体
L1 認証 〜 L3 処理 〜 L5 連携"]
sor["RecordStore 正本
R2 / D1 / KV + KeyManager"]
end
subgraph HYPER["🟡 ハイパースケーラ(条件付き・🔴RQ-098)"]
tee["ConfidentialCompute
TEE: GCP / Azure / AWS"]
end
subgraph THIRD["🔵 第三者 SaaS(🔴RQ-099)"]
broker["IdPBroker
WorkOS / GCP Identity Platform 等"]
end
subgraph SELF["⚪ 自社内部(別系)"]
gas["既存 GAS 会計
自社 会計 SSoT"]
end
user -->|"SSO"| app
user -. 所属 .-> cidp
app -->|"federation"| broker
broker -->|"OIDC / SAML"| cidp
app -->|"Graph / Google API で取込"| csrc
app -->|"仕訳export(証憑は動かさない)"| acc
app --> sor
app -. TEE要件時のみ .-> tee
app -->|"連携境界"| gas
sor -. 将来:複製・正本は動かさない .-> cmirror
class user cust
class CUST,cidp,csrc,acc,cmirror cust
class VENDOR,app,sor vendor
class HYPER,tee hyper
class THIRD,broker third
class SELF,gas internal
境界の要点: 正本(RecordStore)と鍵(KeyManager)は自社 SaaS 側。顧客テナントには正本を置かない(案B)。認証は顧客 IdP へ、取込は顧客ソースへ、出力は顧客の会計ソフトへ越境する。TEE を採るときだけ自社の計算をハイパースケーラへ寄せる(🔴RQ-098)。
証憑が「入って検索できるまで」の流れ(Mermaid)
Write(取込〜保存)は 非同期(Cloudflare Queues)、Read(検索・閲覧)は 同期ショートカットに分ける(CQRS 視点)。
sequenceDiagram participant U as 顧客ユーザー participant IdP as 顧客IdP (Entra/Google) participant App as SaaS API (CF Workers) participant Q as Queue (CF Queues) participant W as 背景 Worker participant SoR as 正本ストレージ (R2/D1) participant Acc as 会計ソフト (弥生等) Note over U,Acc: 【Write 経路:取込〜保存〜出力(非同期)】 U->>IdP: SSO IdP-->>App: OIDC トークン → テナント解決 (L1) App->>App: 受領イベント取込 (L2) = TS起点 App->>Q: ジョブ投入して即 200 OK(冪等キー=受領ID) Q->>W: デキュー W->>W: OCR・3要素抽出 → 信頼度ゲート (L3) W->>SoR: ハッシュ付き正本を暗号化保存+台帳登録 (L4) Note over W,SoR: 監査ログ記帳(横断・改ざん検知) W->>Acc: 仕訳エクスポート(証憑は動かさず・リンクのみ)(L5) Note over U,SoR: 【Read 経路:検索・閲覧(同期・CQRS)】 U->>App: 3要素検索 / 閲覧 App->>SoR: L1認証 → L3 SearchIndex → L4 RecordStore(L2 を通らない) SoR-->>U: 証憑・台帳を返す
各層のモジュール(役割のやさしい説明)
モジュール名は参照用の暫定識別子(実装 ADR / EDD で確定)。状態: ✅確定 / 🔴spike待ち / ⬜未起案。図の ①〜⑤+外部+横断 と対応。
① プレゼンテーション層(UI)— ユーザーが直接さわる画面
| モジュール名 | やさしい説明 | 塊・状態 |
|---|---|---|
| 証憑管理アプリ | 証憑をアップロードし、一覧・確認する “主な作業画面”。 | ⬜ |
| 検索ビュー | 取引年月日・金額・取引先で証憑を探す画面。電帳法の検索要件をユーザーに見せる窓口。 | ⬜ |
| 管理コンソール | テナント設定・ユーザー・連携などの管理画面。 | ⬜ |
② インタフェース・認証層 — 外と中をつなぐ入口。誰が入ってよいかを判定し、API を出す
| モジュール名 | やさしい説明 | 塊・状態 |
|---|---|---|
| AuthGateway | 顧客が “自社の M365/Google アカウントのまま” ログインできるようにする認証の入口。day-1 は Entra OIDC、IdP 非依存の pluggable 抽象。 | D4b ✅ ADR-0127 |
| IdPBroker | Entra・Google など IdP ごとの違いを吸収する仲介役。自前実装か外部サービスかは調査中。 | D4b 🔴RQ-099 |
| RealmResolver | 「この人はどの会社(テナント)・どの IdP か」をメールドメイン等から振り分ける。 | D4b ⬜ |
| SearchIndex(API) | 検索ビューの裏側。3要素で証憑を引く検索 API。 | D6 ⬜ |
| JournalExporter | 仕訳データを弥生/freee/MF へ書き出す。証憑そのものは動かさずリンクだけ渡す。 | D3 ⬜ |
| AccountingBridge | 自社の既存 GAS 会計(会計の正=SSoT)とつなぐ境界。 | D3 ⬜ |
③ アプリケーション/ドメイン処理層 — 証憑を取り込み、中身を理解し、台帳に仕立てる中核
取込(外部ソースから証憑を集める)
| モジュール名 | やさしい説明 | 塊・状態 |
|---|---|---|
| IngestAdapter | 取込口の共通インターフェイス。Google でも M365 でも “同じ扱い” にする。ソース非依存の抽象方針は ADR-0127 で確定。 | D5 ✅ ADR-0127 |
| GoogleAdapter / M365Adapter | それぞれ Gmail/Drive、Outlook/OneDrive から証憑を取りに行く実装。day-1 は M365Adapter(Graph)、Google は将来の後付け。 | D5 ✅ ADR-0127(day-1=M365) |
| ReceiptIntake | 受け取った証憑をそろえ、重複を除く。受領時刻=電帳法のタイムスタンプ起点。 | D5 ⬜ |
| OcrExtractor | 証憑の画像から文字・項目(3要素など)を読み取る(OCR/LLM)。 | D8 ⬜ |
証憑処理(読み取り結果を確定し、台帳化する)
| モジュール名 | やさしい説明 | 塊・状態 |
|---|---|---|
| AutomationGate | 読み取りの信頼度で「自動で確定」か「人の確認待ち」に振り分ける。工数を最小化する仕組み。 | D8 ⬜ |
| VoucherLedger | 証憑の台帳。3要素の検索キーや、会計仕訳との対応を持つデータの形。 | D6 ⬜ |
| IntegrityGuard | 改ざんされていないことを保証(ハッシュ・版数・監査ログ)。真実性は保存層と一体。 | D7 🔴RQ-088 |
④ 永続化・正本層 — 証憑の “法的な原本” を安全に保管する
| モジュール名 | やさしい説明 | 塊・状態 |
|---|---|---|
| RecordStore | 正本の集約先。証憑本体=R2、台帳/メタ=D1、索引=KV。電帳法の保存場所そのもの。 | D2 ✅ ADR-0126 |
| ImmutableStore | 訂正・削除できない保存(WORM 相当)。これで “真実性” を製品が独立に保証できる。 | D7 🔴 |
| KeyManager | 暗号化と鍵の管理。テナントごとに鍵を分け、紛失対策(エスクロー)も持つ。 | D10 🔴RQ-098 |
| RetentionManager | 法定の保存年限を管理し、期限後の廃棄を監査証跡つきで行う。 | D9 🔴RQ-091 |
⑤ 基盤層(Infrastructure)— 上の全層が動く土台
| モジュール名 | やさしい説明 | 塊・状態 |
|---|---|---|
| Platform | Cloudflare Workers + R2/D1/KV。dogfood はこの構成で確定。 | D3 ✅ |
| ConfidentialCompute | 運営者でも中身を読めない処理(TEE)が要る場合だけ使う、ハイパースケーラ上の計算層。 | D10 🔴RQ-098 |
外部(顧客テナント / 第三者)— 自社システムの外。連携先
| モジュール名 | やさしい説明 |
|---|---|
| 顧客IdP | 顧客が普段使う Entra(M365)/Google。ログインの認証元。 |
| 証憑ソース | 証憑が届く場所(Outlook/OneDrive・Gmail/Drive)。取込が読みに行く。 |
| 会計ソフト | 顧客/顧問税理士が記帳に使う弥生/freee/MF。仕訳の受け渡し先。 |
| 既存 GAS 会計(自社内部) | 自社の会計の正(SSoT)。AccountingBridge でつなぐ。 |
横断(全層に適用)
| モジュール名 | やさしい説明 | 塊・状態 |
|---|---|---|
| AuditLog | 誰がいつ何をしたかの記録。改ざん検知とコンプラ(JIIMA/ISMS)の土台。 | — |
| TenantContext | すべての処理に「どのテナントか」を持たせ、他社データへの越境を防ぐ。 | D4a 🔴RQ-092 |
| Telemetry | 稼働の見える化(取込率・自動確定率・SSO 失敗率など)。 | — |
補足: MirrorExporter(将来) — 顧客テナントへ証憑の複製を出すオプション。正本は動かさず、タイムスタンプ付き形式で書き出す。初期スコープ外(決定⑤)。
設計の要点(討議で確定した前提)
- 真実性は保管場所に属し移送できない。案B のイミュータブル保存(電帳法 真実性ルート(3)=訂正削除できないシステム)で製品が独立に法的充足を担保する。タイムスタンプは必須でなく代替可。エクスポート/ミラー時のみ TS 付き形式へ変換する。これが「正本を自社に集約する(案A 顧客テナント保存を採らない)」根拠。
- 受領(授受)がタイムスタンプ起点。取引発生日でも記帳日でもない。受領イベントをシステムが制御できるので、保存期限超過リスクをアーキで構造的に潰せる。
- 記帳は顧客の既存会計ソフトに任せ、証憑は動かさない。L5 は仕訳だけをエクスポートし、証憑正本は L4 に残してリンクで紐付ける。
- 認証は IdP 非依存。単一 IdP(Entra)固定にしない。day-1 は自社 M365=Entra で動かし、Google 等は pluggable アダプタで後付けする(ロックインしない設計が要件、全 IdP の day-1 実装は不要)。
- 機密性ゼロトラストはスペクトラム。dogfood 基線は Lv2(テナント別エンベロープ暗号化)。純粋ゼロ知識(顧客のみ鍵)は OCR・検索・鍵紛失リスクで dogfood 不適。TEE(機密計算)は CF Workers 不可・ハイパースケーラのみ=基盤選定と one-way 結合(RQ-098 で判定)。
実装フェーズの設計原則(このベースラインを劣化させないために)
層構成は正典(ベースライン)として確定。実装(EDD・コーディング)で次の3点をチームで合意・明文化する(アーキ評価レビュー反映 2026-06-08)。
1. 依存方向は L3(ドメイン)へ集約 — DIP / Ports & Adapters
- データは L2→L3→L4→L5 と流れるが、ソースコードの import 方向はすべて L3 に向ける(依存関係逆転)。
- L3 は
IVoucherStore/IIngestSource/IJournalSinkのような Port(インターフェイス)だけを定義し、L2 取込・L4 保存・L5 出力が Adapter として実装する。 - 効果: M365/Google/会計ソフトの API 仕様変更が L3 コアに波及しない(腐敗防止層)。L3 の単体テストが DB・外部なしで高速に回る。
- NG例: L3 が R2/D1 の SDK を直接呼ぶ。 OK例: L3 は
IVoucherStoreだけ知り、L4 がそれを実装。
2. 非同期(Cloudflare Queues)境界を明示
- L2〜L5 を 1 HTTP リクエストで同期処理すると、OCR 遅延・外部通信で Workers のタイムアウト/CPU 制限に抵触する。
- L2 で受領イベントを受けたら即 200 OK → 以降(OCR・保存・出力)は Cloudflare Queues 経由のバックグラウンド Worker で処理する(シーケンス図の Write 経路)。
- 冪等性キー=受領イベントID で再投入・重複を安全に吸収する(電帳法の全件保存に対する欠損防止)。
3. Read と Write を分離 — CQRS 視点
- L1〜L5 は Write(取込→保存→出力)パイプラインとして最適化されている。
- Read(検索・閲覧・手動確認)は L2 取込口を通らず、
L1 認証 → L3 SearchIndex/UI向けAPI → L4 RecordStoreのショートカット経路にする(シーケンス図の Read 経路)。 - この Read/Write の非対称(CQRS 的アプローチ)をフロントエンドとの境界として明記する。
塊(ADR)と層の対応
| 層 | 主な塊 | 対応 ADR |
|---|---|---|
| ② インタフェース・認証 | D4a テナント分離 / D4b 認証 / D3 連携境界 | ADR-0127(認証=M365ネイティブ pluggable・取込=Graph day-1・受理済、旧 ADR-β)/ D4a 分離方式は RQ-092 / D3 |
| ③ ドメイン処理 | D5 取込抽象 / D6 データモデル / D7 真実性 / D8 自動化 | D7 は初手層・ADR-α と整合設計/他は実装 ADR・EDD |
| ④ 永続化・正本 | D2 正本 / D9 年限 / D10 暗号化 | ADR-0126(正本=案B・受理済、旧 ADR-α)/D10 は独立 ADR |
| ⑤ 基盤 | D3 基盤 | D3(ADR-0121 継承) |
| 横断 | D4a テナント分離(スコープ貫通) | — |
ADR-0118(共通基盤)は塊が大きすぎたため取下げ、RQ-097 粒度原則で 2 本に分解した。法的正本は ADR-0126(正本=自社集約 案B・受理済、旧 ADR-α)、顧客認証・取込は ADR-0127(M365 ネイティブ pluggable・受理済、旧 ADR-β)で確定。 これで JTBD-012 基盤の 2 本(正本+認証)が揃った。
ADR-0126 が決めたのは「正本をどこに置くか(案B)」だけ。 審査で挙がった次の 3 つの信頼性リスクは、置き場所の決定では解決しない別問題なので下流の塊が引き取る:
- 長時間障害で全顧客が同時に提示できなくなる → 可用性・DR 設計(④ RecordStore / ⑤ Platform)
- 証憑と仕訳をつなぐ索引が壊れて検索できなくなる → データモデル(③ D6 VoucherLedger / SearchIndex)
- 第三者タイムスタンプ局が止まって証憑が滞留する → 真実性(③ D7 IntegrityGuard・RQ-088)
未解決(spike=DoR ゲート)
| RQ | 論点 | ブロックする層/塊 |
|---|---|---|
| RQ-088 | 版数+監査ログ+規程で③真実性を法的に満たせるか | L3 D7 |
| RQ-091 | 保存年限の自動管理・廃棄判定 | L4 D9 |
| RQ-092 | マルチテナント分離方式・規模実測 | L1 D4a |
| RQ-098 | 機密性脅威モデル × TEE × {AWS/GCP/Azure} × コスト | L4 D10 / L0 基盤 |
| RQ-099 | マルチ IdP broker vs 直接 OIDC | L1 D4b |
改訂履歴
| 日付 (JST) | 変更 |
|---|---|
| 2026-06-08 | 初版(暫定)。証憑保存・タイムスタンプ・真実性・機密性ゼロトラスト・マルチ IdP の討議結果を合成 |
| 2026-06-08 | 各モジュールに参照用のモジュール名(AuthGateway / RecordStore 等)を付与。Mermaid ノード・各層表・横断表に反映 |
| 2026-06-08 | 層構成図をステータス色分け+ノード3行統一で整理。信頼境界・配置(デプロイ)図を追加(顧客 / 自社 / ハイパースケーラ / 第三者の境界を明示) |
| 2026-06-08 | 層構成図を D2(diagram-as-code)生成のレイヤー型 SVG に差し替え(横帯レイヤー+入れ子モジュール群+上向きフロー)。ソース .d2 を同梱、build に SVG/PNG コピーを追加 |
| 2026-06-08 | 図の微調整: 処理・保存層を「証憑処理/正本ストレージ/外部会計連携」の3群横並びに整え、正本↔会計連携の横同期を水平化。横断帯を整理 |
| 2026-06-08 | 縦軸を B(古典的レイヤード)に変更: 上=UI/ユーザー → 下=保存・基盤の責務スタック(grid で固定)、外部システムを脇に分離。①〜⑤+外部+横断の構成に |
| 2026-06-08 | 各層モジュールに「やさしい説明」を付与し、一覧を図の ①〜⑤+外部+横断 に整列。塊↔層の対応表も更新 |
| 2026-06-08 | アーキ評価レビューを反映: 実装フェーズの設計原則3点(DIP/Ports&Adapters・非同期Queue境界・CQRS Read/Write分離)を追記。シーケンス図を Write(非同期)/Read(同期) の2経路に再構成 |
| 2026-06-08 | ADR-0118 取下げ→分解の結果を反映: 法的正本を ADR-0126(正本=案B・受理済) で確定(D2/RecordStore・塊対応表・related を更新)。審査で挙がった 3 信頼性リスク(可用性/索引整合/TSA停止)を下流の塊(DR設計・D6・D7)へ再ホームと明記 |
| 2026-06-08 | 顧客認証・取込を ADR-0127(M365 ネイティブ pluggable・受理済、旧 ADR-β) で確定(AuthGateway D4b/IngestAdapter・M365Adapter D5/②塊対応表/related を更新)。これで基盤 2 本(正本+認証)が揃った。D4a 分離方式は RQ-092・IdPBroker は RQ-099 で spike 継続 |