跳至主要内容

專案結構說明

本文檔說明基於 AppFuse Server 的專案推薦結構,以 app-tenant-server 參考實作為範例。

標準專案結構

my-project/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/yourcompany/yourapp/
│ │ │ ├── config/ # 配置類別
│ │ │ ├── entity/ # 領域模型(Entity)
│ │ │ ├── repository/ # 資料存取層
│ │ │ ├── service/ # 業務邏輯層
│ │ │ ├── controller/ # REST API 控制器
│ │ │ └── Application.java # 主程式
│ │ └── resources/
│ │ ├── application.yml # Spring 內建屬性
│ │ └── {app-name}.yml # 應用程式自訂屬性
│ └── test/
│ └── java/
│ └── com/yourcompany/yourapp/
├── build.gradle.kts # Gradle 建置腳本
└── README.md # 專案說明

套件組織

config/ - 配置類別

存放 Spring Configuration 類別:

config/
├── SecurityConfig.java # 安全配置
├── CacheConfig.java # 快取配置
├── JpaAuditingConfig.java # JPA 審計配置
├── MailConfig.java # 郵件配置
├── TenantConfig.java # 多租戶配置
└── WebConfig.java # Web 配置

職責

  • 配置 Spring Beans
  • 整合框架元件
  • 環境特定設定

範例:參考 app-tenant-server/config

entity/ - 領域模型

按業務模組組織 Entity:

entity/
├── base/ # 基礎實體
│ ├── AuditableBase.java # 審計基底類別
│ ├── AuditableTenantEntity.java
│ ├── Country.java
│ └── Currency.java
├── tenant/ # 多租戶模組
│ └── Tenant.java
├── auth/ # 認證模組
│ ├── Account.java
│ ├── Role.java
│ └── Authority.java
├── sales/ # 銷售模組(業務範例)
│ ├── Product.java
│ ├── ProductCategory.java
│ └── ProductStatus.java
├── order/ # 訂單模組(業務範例)
│ ├── Order.java
│ ├── OrderItem.java
│ └── OrderStatus.java
└── customer/ # 客戶模組(業務範例)
├── Customer.java
├── Contact.java
└── Address.java

設計原則

  • 按業務領域劃分子套件
  • 基礎類別放在 base/
  • Entity 專注於資料結構定義

範例:參考 app-tenant-server/entity

repository/ - 資料存取層

按業務模組組織 Repository:

repository/
├── tenant/
│ └── TenantRepository.java
├── auth/
│ ├── AccountRepository.java
│ └── AuthorityRepository.java
├── sales/
│ ├── ProductRepository.java
│ └── ProductCategoryRepository.java
├── order/
│ └── OrderRepository.java
└── customer/
└── CustomerRepository.java

職責

  • 定義資料存取介面
  • 擴展 Spring Data JPA Repository
  • 自定義查詢方法

範例:參考 app-tenant-server/repository

service/ - 業務邏輯層

按業務模組組織 Service:

service/
├── auth/
│ └── AccountDetailsService.java
├── sales/
│ ├── ProductService.java
│ └── ProductCategoryService.java
├── order/
│ └── OrderService.java
├── customer/
│ └── CustomerService.java

職責

  • 實作業務邏輯
  • 協調多個 Repository 操作
  • 處理交易邊界

範例:參考 app-tenant-server/service

controller/ - REST API 控制器

按業務模組組織 REST Controllers:

controller/
├── auth/
│ └── AuthController.java
├── sales/
│ └── ProductController.java
├── order/
│ └── OrderController.java
├── customer/
│ └── CustomerController.java
├── file/
│ └── StagingFileController.java # 可重塑的 staging API reference adapter
└── base/
└── ReferenceDataController.java

職責

  • 處理 HTTP 請求/回應
  • 參數驗證
  • 呼叫 Service 執行業務邏輯
  • 建構 HATEOAS 連結

範例:參考 app-tenant-server/controller

配置檔案組織

AppFuse 採用配置檔分離策略,將 Spring 內建屬性與應用程式自訂屬性分開管理:

配置檔命名慣例

檔案內容說明
application.ymlSpring 內建屬性server、spring、management、logging 等
{app-name}.yml應用程式自訂屬性app.* 等自訂配置

檔名 {app-name} 對應 spring.application.name;本 workspace 的 app-tenant-server 來源模組以 app-server 作為部署服務身分,因此使用 app-server.yml

配置載入順序

appfuse-server 的 Environ 模組支援外部配置,載入順序如下(後者覆蓋前者):

1. classpath:application.yml ← Spring 內建屬性
2. classpath:{app-name}.yml ← 應用程式自訂屬性
3. ${app.home}/conf/{app-name}.yml ← 外部配置(部署時覆蓋)
4. ${app.home}/conf/{app-name}-{profile}.yml ← 外部配置的 profile 變體(僅該 profile 啟用時)

application.yml(Spring 內建屬性)

存放 Spring 框架相關配置:

server:
servlet:
context-path: /app-server

spring:
application:
name: app-tenant-server
datasource:
url: jdbc:h2:file:${user.home}/var/data/appdb
jpa:
hibernate:
ddl-auto: update

logging:
level:
io.leandev.app: DEBUG

{app-name}.yml(應用程式自訂屬性)

存放應用程式特定的配置:

app:
storage:
type: database
jwt:
expiration: 900000
cors:
allowed-origins: "http://localhost:*"

外部配置(部署環境)

部署時可在 ${app.home}/conf/ 放置外部配置檔覆蓋預設值:

# 設定 app.home
export APP_HOME=/opt/app-tenant-server

# 建立外部配置
mkdir -p $APP_HOME/conf
cat > $APP_HOME/conf/app-server.yml << EOF
app:
cors:
allowed-origins: "https://app.example.com"
EOF

外部配置優先於 classpath 內的配置,適合管理環境特定設定。

一台機器上並存多個環境

Environ 注入的 spring.config.additional-location 指向的是單一檔案,因此 Spring Boot 會自動解析它的 profile 變體。把「跨環境共用」與「隨環境而異」的設定拆成兩層,同一台開發機 即可在多個後端之間切換,不必改檔名:

$APP_HOME/conf/
├── app-server.yml # 共通層:恆生效(不覆蓋 datasource 即沿用 classpath 的內嵌 H2)
├── app-server-sit.yml # 環境層:僅 sit profile 啟用時疊上,優先於共通層
└── app-server-uat.yml
java -jar app.jar # 共通層 = 本地預設
java -jar app.jar --spring.profiles.active=sit # 共通層 + SIT 環境層

檔名分隔符是連字號 -{profile}(Spring 慣例);寫成 .{profile} 不會被辨識。未啟用的 profile 檔完全不載入。

Schema 管理

app-tenant-server 參考實作使用 Hibernate ddl-auto: update 自動管理 Schema:

spring:
jpa:
hibernate:
ddl-auto: update

ddl-auto 選項

說明適用環境
update自動新增欄位/表,不刪除開發環境
validate僅驗證 Schema 一致性生產環境(搭配遷移工具)
create-drop啟動時建立、關閉時刪除測試環境
none不做任何操作由遷移工具管理

:::warning 生產環境建議 生產環境建議使用 FlywayLiquibase 管理 Schema 遷移, 搭配 ddl-auto: validate 確保 Entity 與實際 Schema 一致。 :::

測試結構

測試目錄鏡射 main 的套件結構:

test/java/com/yourcompany/yourapp/
├── entity/
│ └── sales/
│ └── ProductTest.java # 單元測試
├── repository/
│ └── sales/
│ └── ProductRepositoryTest.java # Repository 測試
├── service/
│ └── sales/
│ └── ProductServiceTest.java
└── controller/
└── sales/
└── ProductControllerTest.java # API 整合測試

架構設計原則

分層架構

app-tenant-server 採用分層架構(Layered Architecture),將程式碼按技術職責分層:

┌─────────────────────────────────────┐
│ controller/ │ ← HTTP 請求處理
├─────────────────────────────────────┤
│ service/ │ ← 業務邏輯
├─────────────────────────────────────┤
│ repository/ │ ← 資料存取
├─────────────────────────────────────┤
│ entity/ │ ← 領域模型
└─────────────────────────────────────┘

優點

  • 職責清晰,易於理解
  • 各層可獨立測試
  • 符合 Spring 生態系慣例

業務模組劃分

在每一層內,按業務模組(如 sales/order/customer/)組織程式碼:

entity/
├── sales/ # 銷售相關 Entity
├── order/ # 訂單相關 Entity
└── customer/ # 客戶相關 Entity

service/
├── sales/ # 銷售相關 Service
├── order/ # 訂單相關 Service
└── customer/ # 客戶相關 Service

這種「分層 + 模組」的組織方式兼顧了:

  • 技術層面:清晰的依賴方向(controller → service → repository → entity)
  • 業務層面:相關功能集中在同一子套件

配置分離

  • Spring 內建屬性 → application.yml
  • 應用程式自訂屬性 → {app-name}.yml
  • 部署環境覆蓋 → ${app.home}/conf/{app-name}.yml
  • 敏感資訊 → 環境變數

下一步

參考資源