refactor(layout): consolidate backend codebase into backend/ package and clean root directory

- Moved cmd/, core/, plugins/, pkg/, downstream/, and main.go into backend/ directory
- Batch updated all Go source files to import github.com/Rain-kl/Wavelet/backend/...
- Updated Makefile, scripts/swagger.sh, architecture guards, and platform skills
- Passed all quality gates (100% tests, 0 lint issues, clean build)
This commit is contained in:
ryan
2026-08-28 12:56:02 +08:00
parent 33b38f8687
commit 43dc97e48c
319 changed files with 912 additions and 1031 deletions
+15 -14
View File
@@ -127,9 +127,10 @@ Wavelet 是面向未来 5 年生产级云原生与高并发业务中台的 **微
## 5. 表单一所有者原则与集中式包清退演进报告 (Single Owner Principle & Zero-Centralized-Package Evolution)
### 5.1 彻底根除集中式包 (Zero-Centralized-Package)
在过去的传统单体架构中,集中式的 `internal/model/`、`internal/repository/` 以及 `internal/` 目录往往成为大杂烩,随着团队扩展导致模块边界失控与隐式耦合。在本次 Cordis 架构重构中,我们实施了彻底的物理清退:
- **`internal/` 目录**:**100% 物理清除**。通用的无状态基础库平移至 `pkg/`,所有业务全部下沉至 `plugins/domain/`。
### 5.1 彻底根除集中式包与建立 backend/ 顶级总包
在过去的传统单体架构中,集中式的 `internal/model/`、`internal/repository/` 以及 `internal/` 目录往往成为大杂烩,随着团队扩展导致模块边界失控与隐式耦合。在本次 Cordis 架构重构中,我们实施了彻底的物理清退与顶级前后端分包:
- **`backend/` 顶级总包**:汇聚所有 Go 后端代码(`cmd/`、`core/`、`plugins/`、`pkg/`、`main.go`),根目录仅保留顶级功能域。
- **`internal/` 目录**:**100% 物理清除**。通用的无状态基础库平移至 `backend/pkg/`,所有业务全部下沉至 `backend/plugins/domain/`。
- **`pkg/model/` 目录**:**100% 物理清除**。消灭集中式数据模型。
- **`pkg/repository/` 目录**:**100% 物理清除**。消灭集中式仓储。
- **`pkg/listener/` 目录**:**100% 物理清除**。全面切换至微内核强类型 `EventBus` 广播订阅。
@@ -138,19 +139,19 @@ Wavelet 是面向未来 5 年生产级云原生与高并发业务中台的 **微
| 数据表 | 唯一所有者插件 | 数据结构与仓储位置 | 跨插件交互方式 |
| :--- | :--- | :--- | :--- |
| `w_users` | `plugins/domain/user` | `models.go`<br>`repository.go` | `core/contracts.UserService`<br>`contracts.UserDTO` |
| `w_auth_sources`<br>`w_external_accounts`<br>`w_access_tokens` | `plugins/domain/auth` | `models.go`<br>`service.go` | `core/contracts.AuthService`<br>`contracts.AuthRegistry` |
| `w_uploads`<br>`w_upload_stats` | `plugins/domain/upload` | `models/models.go`<br>`repository/repository.go` | `core/contracts.StorageService`<br>`upload.Ingest` 流水线 |
| `w_system_configs`<br>`w_templates` | `plugins/domain/admin` | `models.go`<br>`repository.go` | `ctx.Settings()` / `contracts.ConfigService`<br>Redis Pub/Sub 广播 |
| `w_message_channels`<br>`w_message_bindings`<br>`w_message_pairing_codes`<br>`w_push_events`<br>`w_push_channels`<br>`w_push_histories` | `plugins/domain/message_gateway` | `models.go`<br>`repository.go` | `EventBus` 强类型事件广播订阅 |
| `w_user_access_logs` | `plugins/domain/risk_control` | `logstore/` | `logstore` 门面<br>ClickHouse PG/SQLite 回落 |
| `w_schedules` | `plugins/drivers/driver_asynq_cron` | `schedule.go` | `ctx.Schedule()` 扩展点 |
| `w_task_executions` | `plugins/drivers/driver_asynq_worker` | `types.go`<br>`executor.go` | `ctx.Task()` 扩展点 |
| `w_schema_versions` | **系统内部** | `cmd/app.go` sharedStore | 自动管理,不归属于任何插件 |
| `w_users` | `backend/plugins/domain/user` | `models.go`<br>`repository.go` | `core/contracts.UserService`<br>`contracts.UserDTO` |
| `w_auth_sources`<br>`w_external_accounts`<br>`w_access_tokens` | `backend/plugins/domain/auth` | `models.go`<br>`service.go` | `core/contracts.AuthService`<br>`contracts.AuthRegistry` |
| `w_uploads`<br>`w_upload_stats` | `backend/plugins/domain/upload` | `models/models.go`<br>`repository/repository.go` | `core/contracts.StorageService`<br>`upload.Ingest` 流水线 |
| `w_system_configs`<br>`w_templates` | `backend/plugins/domain/admin` | `models.go`<br>`repository.go` | `ctx.Settings()` / `contracts.ConfigService`<br>Redis Pub/Sub 广播 |
| `w_message_channels`<br>`w_message_bindings`<br>`w_message_pairing_codes`<br>`w_push_events`<br>`w_push_channels`<br>`w_push_histories` | `backend/plugins/domain/message_gateway` | `models.go`<br>`repository.go` | `EventBus` 强类型事件广播订阅 |
| `w_user_access_logs` | `backend/plugins/domain/risk_control` | `logstore/` | `logstore` 门面<br>ClickHouse PG/SQLite 回落 |
| `w_schedules` | `backend/plugins/drivers/driver_asynq_cron` | `schedule.go` | `ctx.Schedule()` 扩展点 |
| `w_task_executions` | `backend/plugins/drivers/driver_asynq_worker` | `types.go`<br>`executor.go` | `ctx.Task()` 扩展点 |
| `w_schema_versions` | **系统内部** | `backend/cmd/app.go` sharedStore | 自动管理,不归属于任何插件 |
### 5.3 架构防线与单向依赖保障
1. **测试脚手架绝对解耦**:底层通用的 `pkg/testhelper` 严禁反向引用任何上层业务插件。`testhelper` 维护轻量自包含的测试表脚手架,彻底杜绝包导入循环(Import Cycle)。
1. **测试脚手架绝对解耦**:底层通用的 `backend/pkg/testhelper` 严禁反向引用任何上层业务插件。`testhelper` 维护轻量自包含的测试表脚手架,彻底杜绝包导入循环(Import Cycle)。
2. **Pub/Sub 并发安全防线**:在启动 Redis Pub/Sub 监听协程前,严格捕获局部客户端实例,彻底消除测试或重启期间对可变全局客户端的数据竞争(Data Race Free)。
3. **零旁路读写 (No Bypass)**:严禁插件 A 跨界旁路直接操作属于插件 B 的数据表,跨域调用一律面向 `core/contracts` 契约编程或发布事件。
3. **零旁路读写 (No Bypass)**:严禁插件 A 跨界旁路直接操作属于插件 B 的数据表,跨域调用一律面向 `backend/core/contracts` 契约编程或发布事件。
+91 -91
View File
@@ -57,7 +57,7 @@ const docTemplate = `{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/github_com_Rain-kl_Wavelet_plugins_domain_cap.ChallengeResponse"
"$ref": "#/definitions/cap.ChallengeResponse"
}
}
}
@@ -109,7 +109,7 @@ const docTemplate = `{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/github_com_Rain-kl_Wavelet_plugins_domain_cap.RedeemResponse"
"$ref": "#/definitions/cap.RedeemResponse"
}
}
}
@@ -2156,7 +2156,7 @@ const docTemplate = `{
"data": {
"type": "array",
"items": {
"$ref": "#/definitions/task.TaskMeta"
"$ref": "#/definitions/driver_asynq_worker.TaskMeta"
}
}
}
@@ -4954,6 +4954,49 @@ const docTemplate = `{
}
}
},
"cap.ChallengeResponse": {
"type": "object",
"properties": {
"challenge": {
"type": "object",
"properties": {
"c": {
"type": "integer"
},
"d": {
"type": "integer"
},
"s": {
"type": "integer"
}
}
},
"expires": {
"description": "ms timestamp",
"type": "integer"
},
"token": {
"type": "string"
}
}
},
"cap.RedeemResponse": {
"type": "object",
"properties": {
"error": {
"type": "string"
},
"expires": {
"type": "integer"
},
"success": {
"type": "boolean"
},
"token": {
"type": "string"
}
}
},
"cap.challengeRequest": {
"type": "object",
"properties": {
@@ -5006,45 +5049,67 @@ const docTemplate = `{
}
}
},
"github_com_Rain-kl_Wavelet_plugins_domain_cap.ChallengeResponse": {
"driver_asynq_worker.TaskMeta": {
"type": "object",
"properties": {
"challenge": {
"type": "object",
"properties": {
"c": {
"type": "integer"
},
"d": {
"type": "integer"
},
"s": {
"type": "integer"
}
}
"asynq_task": {
"type": "string"
},
"expires": {
"description": "ms timestamp",
"description": {
"type": "string"
},
"max_retry": {
"type": "integer"
},
"token": {
"name": {
"type": "string"
},
"params": {
"type": "array",
"items": {
"$ref": "#/definitions/driver_asynq_worker.TaskParam"
}
},
"queue": {
"type": "string"
},
"retryable": {
"description": "是否支持手动重试",
"type": "boolean"
},
"supports_time": {
"type": "boolean"
},
"type": {
"type": "string"
}
}
},
"github_com_Rain-kl_Wavelet_plugins_domain_cap.RedeemResponse": {
"driver_asynq_worker.TaskParam": {
"type": "object",
"properties": {
"error": {
"description": {
"description": "描述",
"type": "string"
},
"expires": {
"type": "integer"
"label": {
"description": "显示名称",
"type": "string"
},
"success": {
"name": {
"description": "参数键名",
"type": "string"
},
"placeholder": {
"description": "占位符",
"type": "string"
},
"required": {
"description": "是否必填",
"type": "boolean"
},
"token": {
"type": {
"description": "类型:string, text, number, boolean",
"type": "string"
}
}
@@ -5470,71 +5535,6 @@ const docTemplate = `{
"example": ""
}
}
},
"task.TaskMeta": {
"type": "object",
"properties": {
"asynq_task": {
"type": "string"
},
"description": {
"type": "string"
},
"max_retry": {
"type": "integer"
},
"name": {
"type": "string"
},
"params": {
"type": "array",
"items": {
"$ref": "#/definitions/task.TaskParam"
}
},
"queue": {
"type": "string"
},
"retryable": {
"description": "是否支持手动重试",
"type": "boolean"
},
"supports_time": {
"type": "boolean"
},
"type": {
"type": "string"
}
}
},
"task.TaskParam": {
"type": "object",
"properties": {
"description": {
"description": "描述",
"type": "string"
},
"label": {
"description": "显示名称",
"type": "string"
},
"name": {
"description": "参数键名",
"type": "string"
},
"placeholder": {
"description": "占位符",
"type": "string"
},
"required": {
"description": "是否必填",
"type": "boolean"
},
"type": {
"description": "类型:string, text, number, boolean",
"type": "string"
}
}
}
},
"securityDefinitions": {
+91 -91
View File
@@ -50,7 +50,7 @@
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/github_com_Rain-kl_Wavelet_plugins_domain_cap.ChallengeResponse"
"$ref": "#/definitions/cap.ChallengeResponse"
}
}
}
@@ -102,7 +102,7 @@
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/github_com_Rain-kl_Wavelet_plugins_domain_cap.RedeemResponse"
"$ref": "#/definitions/cap.RedeemResponse"
}
}
}
@@ -2149,7 +2149,7 @@
"data": {
"type": "array",
"items": {
"$ref": "#/definitions/task.TaskMeta"
"$ref": "#/definitions/driver_asynq_worker.TaskMeta"
}
}
}
@@ -4947,6 +4947,49 @@
}
}
},
"cap.ChallengeResponse": {
"type": "object",
"properties": {
"challenge": {
"type": "object",
"properties": {
"c": {
"type": "integer"
},
"d": {
"type": "integer"
},
"s": {
"type": "integer"
}
}
},
"expires": {
"description": "ms timestamp",
"type": "integer"
},
"token": {
"type": "string"
}
}
},
"cap.RedeemResponse": {
"type": "object",
"properties": {
"error": {
"type": "string"
},
"expires": {
"type": "integer"
},
"success": {
"type": "boolean"
},
"token": {
"type": "string"
}
}
},
"cap.challengeRequest": {
"type": "object",
"properties": {
@@ -4999,45 +5042,67 @@
}
}
},
"github_com_Rain-kl_Wavelet_plugins_domain_cap.ChallengeResponse": {
"driver_asynq_worker.TaskMeta": {
"type": "object",
"properties": {
"challenge": {
"type": "object",
"properties": {
"c": {
"type": "integer"
},
"d": {
"type": "integer"
},
"s": {
"type": "integer"
}
}
"asynq_task": {
"type": "string"
},
"expires": {
"description": "ms timestamp",
"description": {
"type": "string"
},
"max_retry": {
"type": "integer"
},
"token": {
"name": {
"type": "string"
},
"params": {
"type": "array",
"items": {
"$ref": "#/definitions/driver_asynq_worker.TaskParam"
}
},
"queue": {
"type": "string"
},
"retryable": {
"description": "是否支持手动重试",
"type": "boolean"
},
"supports_time": {
"type": "boolean"
},
"type": {
"type": "string"
}
}
},
"github_com_Rain-kl_Wavelet_plugins_domain_cap.RedeemResponse": {
"driver_asynq_worker.TaskParam": {
"type": "object",
"properties": {
"error": {
"description": {
"description": "描述",
"type": "string"
},
"expires": {
"type": "integer"
"label": {
"description": "显示名称",
"type": "string"
},
"success": {
"name": {
"description": "参数键名",
"type": "string"
},
"placeholder": {
"description": "占位符",
"type": "string"
},
"required": {
"description": "是否必填",
"type": "boolean"
},
"token": {
"type": {
"description": "类型:string, text, number, boolean",
"type": "string"
}
}
@@ -5463,71 +5528,6 @@
"example": ""
}
}
},
"task.TaskMeta": {
"type": "object",
"properties": {
"asynq_task": {
"type": "string"
},
"description": {
"type": "string"
},
"max_retry": {
"type": "integer"
},
"name": {
"type": "string"
},
"params": {
"type": "array",
"items": {
"$ref": "#/definitions/task.TaskParam"
}
},
"queue": {
"type": "string"
},
"retryable": {
"description": "是否支持手动重试",
"type": "boolean"
},
"supports_time": {
"type": "boolean"
},
"type": {
"type": "string"
}
}
},
"task.TaskParam": {
"type": "object",
"properties": {
"description": {
"description": "描述",
"type": "string"
},
"label": {
"description": "显示名称",
"type": "string"
},
"name": {
"description": "参数键名",
"type": "string"
},
"placeholder": {
"description": "占位符",
"type": "string"
},
"required": {
"description": "是否必填",
"type": "boolean"
},
"type": {
"description": "类型:string, text, number, boolean",
"type": "string"
}
}
}
},
"securityDefinitions": {
+67 -67
View File
@@ -628,6 +628,34 @@ definitions:
website:
type: string
type: object
cap.ChallengeResponse:
properties:
challenge:
properties:
c:
type: integer
d:
type: integer
s:
type: integer
type: object
expires:
description: ms timestamp
type: integer
token:
type: string
type: object
cap.RedeemResponse:
properties:
error:
type: string
expires:
type: integer
success:
type: boolean
token:
type: string
type: object
cap.challengeRequest:
properties:
scope:
@@ -662,32 +690,49 @@ definitions:
ttl_minutes:
type: integer
type: object
github_com_Rain-kl_Wavelet_plugins_domain_cap.ChallengeResponse:
driver_asynq_worker.TaskMeta:
properties:
challenge:
properties:
c:
type: integer
d:
type: integer
s:
type: integer
type: object
expires:
description: ms timestamp
asynq_task:
type: string
description:
type: string
max_retry:
type: integer
token:
name:
type: string
params:
items:
$ref: '#/definitions/driver_asynq_worker.TaskParam'
type: array
queue:
type: string
retryable:
description: 是否支持手动重试
type: boolean
supports_time:
type: boolean
type:
type: string
type: object
github_com_Rain-kl_Wavelet_plugins_domain_cap.RedeemResponse:
driver_asynq_worker.TaskParam:
properties:
error:
description:
description: 描述
type: string
expires:
type: integer
success:
label:
description: 显示名称
type: string
name:
description: 参数键名
type: string
placeholder:
description: 占位符
type: string
required:
description: 是否必填
type: boolean
token:
type:
description: 类型:string, text, number, boolean
type: string
type: object
handler.batchDownloadRequest:
@@ -973,51 +1018,6 @@ definitions:
example: ""
type: string
type: object
task.TaskMeta:
properties:
asynq_task:
type: string
description:
type: string
max_retry:
type: integer
name:
type: string
params:
items:
$ref: '#/definitions/task.TaskParam'
type: array
queue:
type: string
retryable:
description: 是否支持手动重试
type: boolean
supports_time:
type: boolean
type:
type: string
type: object
task.TaskParam:
properties:
description:
description: 描述
type: string
label:
description: 显示名称
type: string
name:
description: 参数键名
type: string
placeholder:
description: 占位符
type: string
required:
description: 是否必填
type: boolean
type:
description: 类型:string, text, number, boolean
type: string
type: object
info:
contact:
name: Wavelet
@@ -1050,7 +1050,7 @@ paths:
- $ref: '#/definitions/response.Any'
- properties:
data:
$ref: '#/definitions/github_com_Rain-kl_Wavelet_plugins_domain_cap.ChallengeResponse'
$ref: '#/definitions/cap.ChallengeResponse'
type: object
"500":
description: 内部服务错误
@@ -1081,7 +1081,7 @@ paths:
- $ref: '#/definitions/response.Any'
- properties:
data:
$ref: '#/definitions/github_com_Rain-kl_Wavelet_plugins_domain_cap.RedeemResponse'
$ref: '#/definitions/cap.RedeemResponse'
type: object
"400":
description: 参数错误或核销失败
@@ -2305,7 +2305,7 @@ paths:
- properties:
data:
items:
$ref: '#/definitions/task.TaskMeta'
$ref: '#/definitions/driver_asynq_worker.TaskMeta'
type: array
type: object
"401":