搜尋開發指南

試試「多租戶」、「啟動」或「outbox」。
搜尋僅使用本站文件,不會傳送至外部服務。

文件目錄

NEXUSPRO / HANDBOOK

技術架構

用一條請求路徑理解系統。先建立分層與資料所有權的共同語言,再討論模組和工具。

內容核對 2026-09-28·圖文指南

本章目標與前置條件

讀完後,你應能判斷一段邏輯應放在哪一層,以及長流程、單步副作用與查詢之間的差異。前置條件是能閱讀 Go package、HTTP request 與 PostgreSQL 的基本概念;不需要先啟動環境。

模組化單體與分層

模組化表示業務域有清楚的 package 邊界;單體表示不因業務分類就拆出一套微服務部署。API 與 worker 的不同執行入口,也不等於每個域是一個微服務。

架構流程圖 · Mermaid

正在繪製架構圖…

查看圖表原始碼
flowchart TD
  HTTP["Gin:身分解析與路由授權"] --> Service["Service/Facade:業務規則與編排"]
  Service --> Repo["Repository:持久化契約"]
  Repo --> Store["PostgreSQL Store + sqlc"]
  Store --> DB[("PostgreSQL/RLS")]
  DB -.->|"outbox 事件"| Dispatcher["worker:outbox dispatcher"]
  Jobs["worker:jobs.Runner 定時工作"] -->|"輪詢與 LISTEN 喚醒"| Dispatcher
  Jobs -.->|"逾期提醒、同步、清理"| Service
  Dispatcher -.->|"單步副作用"| Platform["internal/platform adapters"]
  Dispatcher -.->|"僅 JML:Orchestrator.Start"| Temporal["Temporal(僅 JML)"]
  Service -.->|"JML 決議:交易外 Signal"| Temporal
  Temporal -.->|"worker 內 activities 回寫投影"| DB
層負責不應承擔
Gin adapter身分、路由授權、輸入驗證與錯誤信封直接寫 SQL 或編排跨域交易
Service/Facade業務規則、交易、稽核及協作依賴外部系統 wire 欄位和供應商語意
Repository 介面定義業務需要的持久化操作揭露特定資料庫 driver 細節
Store/sqlc租戶資料存取、映射、SQL 執行創造第二份業務計算規則
PostgreSQL資料、約束、RLS、查詢投影與 outbox用 trigger 或 function 重做 service 邏輯

組裝與請求授權鏈

cmd/api 與 cmd/worker 是兩個執行入口,都以 internal/startup 的 Assembler 逐一建構模組(失敗時反向回滾),組裝時跑 RunReadiness,關閉時走 RunShutdownPhases。各模組的具體組裝在 internal/wiring,API 的 HTTP 端在 internal/wiring/apiruntime。worker 只提供 ops listener,不提供業務 HTTP API。

業務路由在 internal/api/v1 以 route.Point(resource, action)、route.AuthOnly() 或 route.Public() 宣告;後兩者必須列在 internal/route/whitelist.go,否則 registrar 在啟動時拒絕。所有路由與授權宣告快照在 internal/route/testdata/registry.golden;api/openapi.yaml 不含權限 metadata。

/v1 請求依序經過:CORS(有設定 origin 時)與 header 檢查 → IP 限流 → identityMiddleware(以 Keycloak JWKS 驗 Bearer token,Public 路由略過)→ 帳號限流 → authorizeMiddleware(只判 Point 路由,交給 internal/authz 決策)→ handler。

資料與租戶邊界

PostgreSQL 是業務資料、查詢投影及 outbox 的唯一事實來源。畫面需要狀態時應讀取本地投影,不把 Temporal 當業務查詢資料庫;Orchestrator port 刻意只有 Start 與 Signal,沒有讀方法。

租戶隔離不能只靠每個查詢記得加條件。規範要求新表預設同時 ENABLE 與 FORCE RLS,應用層的租戶上下文和資料庫防線一起工作。

service 透過 repository.UnitOfWork 的 WithinTenant 開租戶交易;底層是 internal/platform/postgres 的 WithTenantTx,它在交易開頭設定租戶 GUC,並拒絕巢狀交易(ErrNestedTransaction)。所以在 service 程式碼裡看到的是 WithinTenant,不是 WithTenantTx。

長流程與單步副作用

問題選擇理由
包含等待、人審、重試或補償的長流程Temporal(目前只有 JML 採用)需要持久化編排進度與恢復能力
單步、可重試且冪等的副作用outbox handler避免把一次性動作包成長流程
週期性掃描、提醒與同步worker 的 jobs.Runner定時觸發,不依賴 outbox 事件
讀取目前業務狀態或報表PostgreSQL 查詢投影不向編排引擎索取業務讀模型
後端呼叫外部系統internal/platform/ 適配層將供應商 wire contract 隔離在邊界

service 在 WithinTenant 的 callback 內以 Publish 寫 outbox,與本域變更同一交易提交,投遞在交易外發生:worker 的 jobs.Runner 每 30 秒輪詢,並由 PostgreSQL LISTEN 通知提前喚醒 dispatcher。跨域副作用不放進同一個租戶交易同步呼叫另一個 service。

Temporal 目前只承載 JML

  • 範圍:Temporal 只跑 JML 定義 hr.onboarding.v1 與 hr.offboarding.v1,對外是 /v1/workflow-runs。表單審批(/v1/forms/:formInstanceId/approve 等與 /v1/workflows/reviews)用 PostgreSQL 內的審批引擎 internal/service/workflow/engine,逾期提醒由 worker 的 form_overdue_reminder job 處理,都不經 Temporal。
  • 啟動:worker 處理 hr.employee.created,或轉為離職的 hr.employee.status_changed 時呼叫 StartJMLRun,在同一交易寫 run 與 workflow.run.start_requested;worker 再消費這個事件並呼叫 Orchestrator.Start。
  • 決議:API 在交易內記錄動作證據,交易外呼叫 Orchestrator.Signal;activities 在 cmd/worker 執行並回寫 PostgreSQL 投影,查詢端只讀投影。
  • 開關:WORKFLOW_JML_AUTO_START_ENABLED 預設 false,HR 狀態變更不會開 run;TEMPORAL_ENABLED 預設 false,API 與 worker 都載入 in-process fake orchestrator,流程在 process 記憶體中推進,沒有真正的長流程、重試、逾時或補償。需要真 Temporal 時設 TEMPORAL_ENABLED=true,並以 deploy/dev-local/compose.p4.yml 啟動 temporal 服務。

前後端資料流

業務前端採 Next.js App Router、React、TypeScript;現有 UI 生態包括 Ant Design、SWR、Jotai 與 Zod,不是參考網站的 Vue 技術選型。

以草稿預覽為例,畫面透過 features/forms/api.ts 的 hook 取得資料。SWR key 包含身分與草稿 revision,避免把舊 revision 的結果當成新狀態。wire payload 與 UI 物件之間的轉換必須經過 schema/adapter,而非直接信任回應。

text
WorkflowPanel
    ↑ schema / UI data
useGetFormWorkflowPreview
    ↓ 同源 /v1/**
proxy.ts → Gin → Service → Repository → PostgreSQL

/v1/** 之外,Next server 還有兩類自有路由:app/api/auth/* 處理 Keycloak OIDC/PKCE 的 login、callback、refresh、logout,以及 password-login 與 reset-password,token 只放在 httpOnly cookie;app/api/geo/current-context 先以 cookie 中的 token 呼叫後端 /v1/me 驗證身分並限流,再直連 Open-Meteo 與 Nominatim。瀏覽器另以 NEXT_PUBLIC_GOOGLE_MAPS_API_KEY 載入 Google Maps JS。上表「外部系統走 internal/platform/」只約束後端;前端新增外部呼叫時,身分、限流與逾時要在 review 中單獨確認。

以上是閱讀路徑,不是本輪網路 trace。完整案例見新增業務功能實戰。

如何在原始碼中驗證

  1. 從 internal/api/v1/form_runtime.go 找到路由與 permission policy。
  2. 沿 PreviewWorkflow 進入 internal/service/form/instance/preview.go。
  3. 確認狀態限制、可見性、時間取樣與錯誤是否在正確層處理。
  4. 對照 internal/domain/form/runtime_preview.go 的 wire 欄位及 OpenAPI。
  5. 回到前端 hook、schema 和測試,確認不是 fixture 模式的假陽性。
  6. 流程若牽涉 JML,對照 internal/wiring/workflow.go,確認目前設定載入的是真 Temporal adapter 還是 fake。

預期結果: 可以指出每個邊界的實際檔案,而不是只憑架構圖猜測程式碼。若檔案與文件不同,記錄 commit/工作區差異,重新核實。

常見誤解與功能狀態

  • 「採 Temporal」目前只指 JML(/v1/workflow-runs):表單審批不經 Temporal;TEMPORAL_ENABLED 預設 false 時,JML 也只由 in-process fake 推進。每條流程仍需核對實作與開關。
  • 「目錄存在」不等於端點已註冊、測試已通過或已部署。
  • 表單草稿預覽:已實作(原始碼層級) ,本輪已讀取契約、handler、service、前端 hook;未執行真實聯調。
  • 其餘業務模組:已有原始碼實作索引。見功能全景與各板塊章節;逐項區分後端、前端與測試入口。本輪未對業務服務執行聯調,仍不能據此判定已部署或可在所有環境啟用。

相關文件

接著閱讀後端開發規範,將架構判斷轉成可執行的開發與驗證流程。outbox、jobs 與 JML 的細節見 Outbox、Jobs 與 Temporal。

內容來源與核實範圍

以下路徑相對於所列業務倉庫;核實層級為「已讀原始碼」,不是本輪業務測試或部署驗收。

  • nexus-pro-be-plus/AGENTS.md
  • nexus-pro-be-plus/internal/api/v1/form_runtime.go
  • nexus-pro-be-plus/internal/service/form/instance/preview.go
  • nexus-pro-be-plus/internal/domain/form/runtime_preview.go
  • nexus-pro-be-plus/internal/api/v1/api.go
  • nexus-pro-be-plus/internal/api/v1/middleware.go
  • nexus-pro-be-plus/internal/api/v1/authorize.go
  • nexus-pro-be-plus/internal/route/registrar.go
  • nexus-pro-be-plus/internal/repository/uow.go
  • nexus-pro-be-plus/internal/platform/postgres/tx.go
  • nexus-pro-be-plus/internal/wiring/workflow.go
  • nexus-pro-be-plus/internal/worker/hr.go
  • nexus-pro-be-plus/internal/service/workflow/run_service.go
  • nexus-pro-be-plus/cmd/worker/assemble.go
  • nexus-pro-be-plus/api/config-reference.md
  • nexus-pro-web-plus/features/forms/api.ts
  • nexus-pro-web-plus/proxy.ts
  • nexus-pro-web-plus/app/api/geo/current-context/route.ts
  • nexus-pro-web-plus/README.md
NexusPro 開發指南以程式碼為準 · 以驗證為據