本開発の目標アーキテクチャ(補足)

提案書 §3〜§6 の図と設定値をまとめたものです。Phase-1 の構成(Workers 1本+D1)を土台に、Access、R2、Cron Triggers、実 API 版のアダプタを足します。

§1〜§3 と §5 の図は、draw.io 形式の PHASE2_ARCHITECTURE.drawio にも同じ内容で入っています。draw.io(diagrams.net)で開くと、全体構成・認証の流れ・定期同期の流れ・追加する表の4ページを編集できます。

1. 全体構成

統合営業台帳 本開発の目標アーキテクチャ(全体構成)
社内利用者 約20名
ブラウザ
管理者 / 閲覧者
Cloudflare(先方名義アカウント)
Cloudflare Access
Entra ID SSO・MFA
Workers シークレット
Worker(Hono)
API・画面配信・Cron
D1
台帳の正本・マスタ
同期履歴・監査ログ
R2
Excel原本・月次資料
過去15年分・ログ
Workers AI
(B案のみ)
Microsoft 365 テナント
Entra ID
Dynamics 365
Dataverse / BC API
Microsoft Graph
SharePoint(Teams のファイル)
Knowledge Suite
API または CSV
凡例
実線:主案の通信(読み取り)
点線:認証・シークレット・任意
破線枠:B案(オプション)
サインイン
任意
トークン取得
前月売上(読み取り)
見通し(読み取り)
案件(読み取り)

2. 認証の流れ

Phase-1 の HMAC Cookie(src/auth.ts L19–L38)とログイン API(src/app.ts L29)は使いません。

認証の流れ(Phase-1 の HMAC Cookie とログイン API は使わない)
利用者
Cloudflare Access
Entra ID
Worker
D1
alt
[登録済み・有効]
[未登録]
台帳にアクセス
SSO(MFA は条件付きアクセス)
ID トークン
Access の Cookie を発行
API 呼び出し(Cf-Access-Jwt-Assertion 付き)
JWT を公開鍵と aud で検証
users 表でメールからロールを取得
access_logs に閲覧を記録
データ
403

3. 定期同期の流れ

入口は executeSync(src/adapters.ts L204)のまま残し、scheduled ハンドラからも呼べるようにします。

定期同期の流れ(入口は executeSync のまま、Cron から起動)
Cron Trigger
Worker(executeSync)
トークン取得
外部 API
D1
loop
[ページごと]
alt
[429 / 5xx]
alt
[成功]
[失敗]
定時起動(UTC 指定)
sync_runs に running を記録(trigger=cron)
クライアント資格情報でトークン取得(キャッシュあり)
差分取得(sync_cursors の位置から)
Retry-After
最大3回まで再試行
レコード
master_mappings で変換し、外部 ID で upsert(db.batch)
sync_runs を success、sync_cursors を更新
sync_runs を failed(error_code 付き)
2回連続なら管理者へ通知

4. 同期スケジュール(案)

Cron Triggers は UTC で指定するので、JST から9時間引いて設定します。

連携 実行時刻(JST) Cron(UTC) 内容
Dynamics 365 毎日 06:00 0 21 * * * 前月分と当月分を差分取得。月初の確定日に前月分を確定し、月次資料を生成
Teams / Graph 平日 08:00〜20:00 の毎時 設計時に確定(UTC では日付をまたぐため2本に分ける) eTag が変わったときだけ解析して取り込む
Knowledge Suite(API の場合) 毎日 02:00 0 17 * * * 案件を差分取得し、品質を判定。補完済みはスキップ
月次資料 毎月1日と15日 07:00 0 22 * * * 毎日起動し、JST の日付が1日か15日のときだけまとめ資料を生成して R2 に保存
ログの書き出し 毎月1日 03:00 0 18 * * * 毎日起動し、JST の1日だけ、13か月を過ぎた access_logs と audit_logs を R2 に書き出す

JST の日付は UTC とずれるので、月初・月中の判定は Cron 式ではなくコード側で行います。

5. 追加する表(マイグレーション 0002)

既存の表(migrations/0001_init.sql)との関係だけを示します。

追加・変更する表(マイグレーション 0002)
users(変更)
PK email TEXT
role TEXT
active INTEGER
last_seen_at TEXT
password 列は削除
access_logs(新規)
PK id INTEGER
FK user_email TEXT
entity_type TEXT
entity_id TEXT
created_at TEXT
import_profiles(新規)
PK id TEXT
template_key TEXT
name TEXT
mapping_json TEXT
sheet_name TEXT
header_row INTEGER
import_jobs(列を追加)
PK id TEXT
FK profile_id TEXT
r2_key TEXT
sha256 TEXT
source_type TEXT
employees(新規)
PK id TEXT
name TEXT
email TEXT
dynamics_owner_id TEXT
ks_user_id TEXT
master_mappings(新規)
system TEXT
kind TEXT
external_key TEXT
ledger_id TEXT
updated_by TEXT
sync_runs(列を追加)
PK id TEXT
system TEXT
trigger TEXT
attempt INTEGER
FK retry_of TEXT
error_code TEXT
sync_cursors(新規)
system TEXT
cursor_key TEXT
cursor_value TEXT
archive_files(新規)
PK r2_key TEXT
fiscal_year INTEGER
category TEXT
original_path TEXT
size INTEGER
sha256 TEXT
report_files(新規)
PK id TEXT
year_month TEXT
kind TEXT
r2_key TEXT
settings(新規)
PK key TEXT
value TEXT
凡例
■ 新規の表 ■ 既存の表を変更

変更しない既存の表(0001_init.sql)
customers / products / sales_plans / forecasts / deals / prior_month_sales / import_row_errors / audit_logs

mock_external_records は本番では使わない(テスト用フィクスチャへ移す)
retry_of

6. R2 のキー設計

接頭辞 中身 削除
imports/{年月}/{import_job_id}/{元ファイル名} 取込した Excel / CSV の原本 アプリからは不可
reports/{年月}/{種別}.xlsx 定期生成した月次資料 保持期間を過ぎたらライフサイクルで削除
archive/{年度}/{種別}/{元ファイル名} 過去15年分の原本(目録は archive_files) アプリからは不可
logs/{年}/{月}/access.jsonl、audit.jsonl 13か月を過ぎたログ 社内規程の保存年数に従う
backups/d1/{日付}.sql 週次の D1 エクスポート 12週を残して削除

7. 設定値とシークレット

名前 種別 用途
ACCESS_TEAM_DOMAIN 変数 Access の公開鍵の取得先
ACCESS_AUD 変数 JWT の aud 検証
ENTRA_TENANT_ID 変数 トークン取得
D365_BASE_URL 変数 Dataverse / BC の環境 URL
D365_CLIENT_ID、D365_CLIENT_SECRET シークレット Dynamics 365 用のアプリ
GRAPH_CLIENT_ID、GRAPH_CLIENT_SECRET シークレット Graph 用のアプリ(Sites.Selected)
TEAMS_SITE_ID、TEAMS_ITEM_ID(または TEAMS_LIST_ID) 変数 見通しファイルの場所
KS_API_KEY シークレット Knowledge Suite(API を使う場合)
ADAPTER_MODE 変数 live / mock。テストと開発は mock

DEMO_SECRET(Phase-1 の wrangler.toml L9)は廃止します。