專案程式結構
本文說明花店管理系統參考實作的程式結構,包括後端 (app-server)、前端 (app-web / app-web-mockup) 的目錄組織與各層職責。
模組總覽
模組關係圖
各模組定位
| 模組 | 定位 | API 方式 | 技術棧 |
|---|---|---|---|
| app-web-mockup | Prototype 參考模板 | Mock (MSW) | React 19 + TypeScript |
| app-web | 生產級 SPA | Mock → 真實(漸進切換) | React 19 + TypeScript |
| app-server | 生產級 RESTful API | 真實 API | Spring Boot 3.5 + Java 25 |
| app-web-host | 前端托管層 | N/A | Spring Boot (WAR) |
| app-docs-host | 文檔托管層 | N/A | Spring Boot (WAR) |
| app-docs | 應用文檔站 | N/A | Docusaurus |
app-server 結構
Package 結構
io.leandev.app/
├── actuator/ # 健康檢查端點
├── config/ # 應用配置
│ ├── SecurityConfig # JWT + OAuth2 + Spring Security
│ ├── TenantConfig # 多租戶配置
│ ├── CacheConfig # 快取配置
│ ├── FileStorageConfig # 檔案儲存配置
│ └── ...
│
├── controller/ # REST API 層
│ ├── auth/ # 認證端點
│ ├── customer/ # 客戶管理
│ ├── order/ # 訂單管理
│ ├── sales/ # 產品管理
│ └── ...
│
├── dto/ # DTO 與請求物件
│ ├── auth/
│ ├── order/
│ └── ...
│
├── entity/ # JPA 實體
│ ├── auth/ # Account, Role, Authority
│ ├── base/ # 基礎類別(AuditableTenantEntity)
│ ├── customer/ # Customer
│ ├── order/ # Order, OrderItem
│ ├── sales/ # Product, ProductCategory
│ └── tenant/ # Tenant
│
├── exception/ # 自訂異常
├── handler/ # 全域異常處理(RFC 7807)
├── initializer/ # 資料初始化
├── mapper/ # MapStruct 映射器
├── repository/ # Spring Data JPA
├── scheduler/ # 排程任務
├── security/ # 安全相關
├── service/ # 業務邏輯層
│ ├── auth/
│ ├── cache/
│ ├── customer/
│ ├── file/
│ ├── mail/
│ ├── order/
│ └── sales/
└── util/ # 工具函數
各層職責
| 層級 | 職責 | 說明 |
|---|---|---|
| Controller | HTTP 請求處理 | 參數驗證、權限檢查(@PreAuthorize)、呼叫 Service |
| Service | 業務邏輯 | 事務管理(@Transactional)、業務規則、快取 |
| Repository | 資料持久化 | Spring Data JPA、自動多租戶過濾 |
| Entity | 資料模型 | JPA 實體、驗證註解、狀態管理 |
| Config | 應用配置 | 依賴注入、框架配置 |
核心設計特點
多租戶架構
- Entity 層:繼承
AuditableTenantEntity,自動注入 tenantId - Repository 層:繼承
TenantAwareRepository,自動過濾租戶資料 - 無需手動:開發者不需撰寫 WHERE tenant_id = ? 條件
Entity 繼承結構
TenantAwareEntity(框架提供)
↓
AuditableTenantEntity(應用層基類)
↓
具體業務實體(Order, Product, Customer)
DTO vs PropertyMap 策略
| 情境 | 使用 | 範例 |
|---|---|---|
| 輸入結構 ≠ Entity | DTO | CreateOrderRequest |
| 輸入結構 = Entity | PropertyMap | Product、Customer 的 CRUD |
| PATCH 部分更新 | PropertyMap | 天然支援部分欄位更新 |
開發規範摘要
應該做:
- 使用
TenantContext自動獲取租戶 ID - 利用 Hibernate Filter 自動過濾多租戶資料
- Service 層統一負責事務邊界
- 使用語義化異常(
NotFoundException、DuplicateException)
避免:
- Repository 層的讀取操作加
@Transactional - 在 Entity 中混合業務邏輯與映射邏輯
- 手動編寫租戶過濾 WHERE 子句
app-web / app-web-mockup 結構
目錄結構
src/
├── applets/ # 業務功能模組
│ ├── customer-applet/ # 客戶管理
│ ├── order-applet/ # 訂單管理
│ ├── product-applet/ # 產品管理
│ ├── dashboard-applet/ # 儀表板
│ ├── delivery-applet/ # 配送管理
│ └── shared/ # 跨模組共用
│
├── components/ # 全域應用組件
│ ├── applet-shell/ # Applet 外框
│ ├── application-launcher/ # 應用啟動器
│ ├── loading-fallback/ # 載入中 UI
│ └── ...
│
├── features/ # Redux Slices
│ ├── iam/ # 身份與授權
│ │ └── me-slice.ts # 使用者狀態
│ ├── crm/ # CRM 相關
│ ├── product/ # 產品相關
│ └── root-reducers.ts # 根 reducers
│
├── hooks/ # 全域自定義 Hooks
├── layouts/ # 佈局組件
├── mocks/ # MSW 配置(開發環境)
│ ├── browser.ts # MSW worker
│ ├── handlers/ # API 處理器
│ └── data/ # Mock 數據
│
├── nls/ # 國際化資源
├── pages/ # 系統頁面
├── routes/ # 路由配置
├── services/ # API 服務層
│ ├── api-client.ts # Axios HTTP 客戶端
│ ├── core/ # 核心服務
│ └── iam/ # 認證服務
│
├── store/ # Redux Store 配置
├── types/ # TypeScript 類型
├── utils/ # 工具函數
│
├── App.tsx # 應用主組件
├── main.tsx # 應用入口
└── tailwind.css # Tailwind 配置
各目錄職責
| 目錄 | 職責 | 說明 |
|---|---|---|
| applets/ | 業務功能模組 | 花店系統特定功能(客戶、訂單、產品等) |
| applets/shared/ | 跨模組共用 | 跨 Applet 共用的組件和 hooks |
| components/ | 全域應用組件 | 與框架庫無關的應用層組件 |
| features/ | Redux Slices | 全域狀態管理(用戶、授權、應用配置) |
| hooks/ | 自定義 Hooks | 跨組件共用的 hooks |
| layouts/ | 佈局組件 | 頁面頭部、菜單、側邊欄 |
| mocks/ | MSW 配置 | Mock API 端點、種子數據 |
| nls/ | 國際化資源 | 多語言翻譯文件 |
| routes/ | 路由配置 | 路由定義和路由保護 |
| services/ | API 服務層 | HTTP 客戶端、API 調用 |
| store/ | Redux Store | Store 配置、中間件 |
| types/ | 類型定義 | 全域 TypeScript 類型 |
Applet 架構模式
每個 Applet(業務模組)遵循統一結構:
xxx-applet/
├── components/ # 該模組特有的 UI 組件
├── hooks/ # 該模組的自定義 hooks
├── utils/ # 該模組的工具函數
├── types.ts # 該模組的類型定義
├── index.ts # 模組導出
├── xxx-applet.tsx # 模組主組件(路由入口)
├── xxx-finder.tsx # 搜尋/列表頁面
├── xxx-detail.tsx # 詳細頁面
├── xxx-editor.tsx # 編輯/新增頁面
└── xxx-form.tsx # 共用表單組件
| 組件 | 職責 |
|---|---|
| xxx-applet.tsx | 路由入口,定義模組級路由 |
| xxx-finder.tsx | 列表/搜尋頁面 |
| xxx-detail.tsx | 詳細頁面 |
| xxx-editor.tsx | 編輯/新增頁面 |
| xxx-form.tsx | 表單邏輯,可被 editor 和 detail 複用 |
開發規範摘要
色彩規範(必須遵守)
// ❌ 禁止硬編碼顏色
className="text-gray-900 bg-blue-500 dark:text-white"
// ✅ 使用 DaisyUI 語義化顏色
className="text-base-content bg-primary"
國際化(必須遵守)
// ❌ 禁止硬編碼中文
<button>創建客戶</button>
// ✅ 使用 useTranslation
import { useTranslation } from '@appfuse/appfuse-web'
const { t } = useTranslation()
<button>{t('Create Customer')}</button>
用戶通知(必須遵守)
// ❌ 禁止原生對話框
alert('成功')
confirm('確認?')
// ✅ 使用 prompt API
import { prompt } from '@appfuse/appfuse-web'
prompt.success('Customer created')
prompt.confirm('確認刪除?')
TypeScript(必須遵守)
- 禁止使用
any - 必須定義明確類型
app-web vs app-web-mockup 差異
| 項目 | app-web-mockup | app-web |
|---|---|---|
| 用途 | Prototype,需求確認 | 生產級應用 |
| API 方式 | 僅 Mock (MSW) | Mock → 真實(可切換) |
| 後端服務 | 無需 | 需要 app-server |
| 生產部署 | 不適用 | 需要 app-web-host(WAR 封裝) |
| 數據持久化 | 內存(刷新丟失) | 資料庫 |
| 主要受眾 | 產品經理、設計師 | 開發團隊、運維 |
程式碼差異:極小,MSW 控制方式相同:
- 開發環境(
npm run dev):MSW 自動啟用 - 生產環境(
npm run build):MSW 預設關閉
詳細說明請參閱「Mock → 真實 API 漸進切換」
技術棧總覽
後端 (app-server)
| 類別 | 技術 |
|---|---|
| 框架 | Spring Boot 3.5.6 |
| 語言 | Java 25 |
| 建構 | Gradle (Kotlin DSL) |
| ORM | Spring Data JPA + Hibernate |
| 安全 | Spring Security + OAuth2 Resource Server + JWT |
| 資料庫 | H2(開發)/ MySQL / PostgreSQL / Oracle / SQL Server |
| 快取 | Spring Cache + 自訂實作 |
| 檔案儲存 | Local / Database / S3 / Azure / SFTP |
前端 (app-web / app-web-mockup)
| 類別 | 技術 |
|---|---|
| 框架 | React 19 |
| 語言 | TypeScript 5.9 |
| 建構 | Vite 7 |
| 狀態管理 | Redux Toolkit + TanStack React Query |
| 路由 | React Router 7 |
| 表單 | React Hook Form |
| 樣式 | TailwindCSS 4 + DaisyUI 5 |
| HTTP | Axios |
| Mock | MSW (Mock Service Worker) |
| 測試 | Vitest + Playwright |
相關資源
- 快速開始 - 環境設定與啟動步驟
- 系統架構 - 整體架構說明
- 專案初始化指南 - 複製專案與開始開發
- AppFuse 框架文檔 - 框架使用指南