v1.1.0 v1.0.0 · 稳定根 GA · 扩展模块按成熟度标注 查看发布策略 →

System Topology

架构

帮助读者看清 Plumego 的稳定根、扩展家族与 canonical 请求路径如何构成统一心智模型。

把架构页当作边界地图来读。

判断 Plumego 最容易的方式,是先看清哪些职责留在内核、哪些能力向 x/* 扩展家族外分,以及默认请求路径从哪里开始、在什么地方结束。

stable roots

让长期职责保持收敛

路由、契约、传输中间件以及面向存储的基础原语,应当保持足够清晰,才能长期守住兼容性。

extension rule

把可选能力向外扩展

产品能力或协议适配应该从 x/* 家族开始,而不是不断吸入内核,最终让默认路径失去边界。

canonical path

先教会读者一条默认请求路径

网站、文档和 reference app 都应该先把从 bootstrap 到 write path 的默认路径讲清楚,再让读者进入更深的扩展区域。

三层 + 控制面——完整架构全貌。

每个 Plumego 服务处于三个模块层与机器可读控制面的交叉点。下图展示了完整结构、导入方向,以及 specs/ 如何规范允许哪些变更。

先把架构看成三块互相配合的区域

只有当内核、canonical 阅读路径与向外分叉的能力家族,能够带着真实仓库事实同时出现在一个视图里时,Plumego 才更容易被判断与信任。

两条并行轨道——模块代码与控制平面。

模块层次只是其中一条轨道。控制平面(specs/docs/tasks/) 在旁边并行运行,持有支配模块代码变更方式的机器可读规则。两条轨道都是一等公民,没有从属关系。

模块代码轨

x/* 扩展家族 experimental · beta
canonical path reference/standard-service
稳定根 core · router · contract · middleware · …

控制平面轨

tasks/ 人类与 Agent 的可执行任务卡
specs/ task-routing · dependency-rules · change-recipes
docs/ 人类可读架构 · 模块手册 · 路线图

internal/checks/ 在 CI 阶段对模块代码执行 specs/ 中的规则。 人类和 AI 编码助手都不能绕过这个关卡。

读者应该能立刻分清的三层结构

如果一个新读者分不清某个改动属于稳定根、能力家族还是 canonical 应用路径,说明站点还没有把架构解释到位。

workflow

canonical 请求路径

默认应用路径给团队提供统一 bootstrap 模型、统一路由流向,以及开始理解仓库的共同入口。

  • docs/getting-started
  • docs/reference-app
  • internal/app/app.go
  • internal/app/routes.go

不靠猜测地检查一条请求路径

最快的架构阅读方式,是先从 reference app 入手,确认 app-local wiring,然后再向外展开到那些显然属于具体功能或协议的包。

01

边界请求先进入 net/http

先把入口保持克制,ownership 才会从显式代码开始。

02

应用本地 wiring 负责组装依赖

bootstrap 是否自律,直接决定后续代码是否还容易读。

03

routes 负责划定公开请求路径

route 注册是第一张架构地图,而不是可以跳过的细节。

04

路径读清以后,能力再向外分层

扩展应当建立在分类之后,而不是为了省事。

canonical path 在代码里应该长什么样。

route 注册是服务的第一张架构地图。它应该能直接在评审里被读懂,而不是藏在框架源码或隐式注册里。

internal/app/routes.go
func (a *App) Routes() http.Handler {
    r := router.New()

    r.Use(middleware.RequestID())
    r.Use(middleware.Logger(a.log))
    r.Use(middleware.Recovery(a.log))

    r.Get("/api/items",  a.items.List)
    r.Post("/api/items", a.items.Create)
    r.Get("/health", health.Handler(a.checker))

    // route map 保持可见,没有隐藏注册
    return r
}
internal/app/app.go
type App struct {
    log     log.Logger
    db      *sql.DB
    items   *items.Handler
    checker health.Checker
}

func New(cfg *config.Config, deps AppDeps) (*App, error) {
    // 每个依赖都在构造函数里显式出现
    return &App{
        log:     deps.Logger,
        db:      deps.DB,
        items:   items.NewHandler(deps.DB),
        checker: health.NewChecker(),
    }, nil
}

稳定根的信号必须保持显式

架构页不应该只把内核列出来,还应该说明哪些工作真的属于那里,以及哪些 root 名称本身就在提醒你:能力关注点正在继续向内漂移。

discouraged roots

能力导向的名字,通常意味着应该继续向外分层

这些名字在产品能力或协议语境里很常见,但它们不适合作为默认根,因为会模糊内核的 ownership 边界。

  • ai
  • tenant
  • net
  • pubsub
  • rest
  • validator
  • utils
  • frontend

用 x/* 扩展家族可以构建什么。

扩展家族不只是模块名——每个家族拥有一个具体的能力域。用下面五个场景判断哪个 x/* 家族最相关,再决定是否打开对应的模块手册。

multi-tenant saas

带 per-tenant 路由、配额与 JWT 策略的多租户服务

x/tenant 在 HTTP 内核之外提供强制租户身份、per-tenant 配额和策略评估。稳定根承担传输基线,x/tenant 承担租户层,两者互不污染。

打开 x/tenant 模块手册

ai service

带 streaming 和工具路由的多 Provider AI 服务

x/ai 在稳定 net/http 内核旁边提供 provider 抽象、session 生命周期和 streaming 响应处理。AI 能力工作留在默认请求路径之外。

打开 x/ai 模块手册

api gateway

边缘代理、负载均衡与路由组合

x/gateway 在边缘提供反向代理、上游健康检查和负载均衡,而不改变上游服务的稳定路由模型。

打开 x/gateway 模块手册

real-time

在同一 HTTP 服务器上的 WebSocket 连接

x/websocket 提供全双工 WebSocket 处理,与标准 HTTP 路由共存,复用同一套稳定中间件链和 handler 形态。

打开 x/websocket 模块手册

observability

在服务边界接线 trace、metrics 和结构化日志

x/observability 提供 exporter、tracer、collector 和 adapter 接线,且不触及稳定路由模型。埋点逻辑留在请求内核之外。

打开 x/observability 模块手册

rest api

带一致响应规范的 CRUD 资源控制器

x/rest 为所有 CRUD 端点提供类型化资源接口、统一响应封装和验证钩子,让每个新资源都遵循同一套结构契约。

打开 x/rest 模块手册

messaging

异步消息、队列与入站 Webhook 投递

x/messaging 在一个家族入口点下连接消息队列、pubsub 流、定时任务和入站/出站 Webhook 传输,将异步关注点保留在 HTTP 内核之外。

打开 x/messaging 模块手册

frontend

静态资产服务与 SPA 嵌入

x/frontend 将嵌入式或目录式资产服务作为传输层关注点处理。缓存清除、预压缩投递和目录安全均留在扩展层。

打开 x/frontend 模块手册

所有 x/* 家族,按成熟度分组。

Beta 家族有 API 快照、Owner 确认和晋级证据——可放心用于生产评估。 Experimental 家族已纳入测试,但 API 尚未冻结;从家族入口点开始,预期会有迭代。

experimental

Experimental — 采用前请先评估

已纳入仓库质量门禁并有测试,但 API surface 尚未冻结。从家族入口点开始,勿直接使用子包。

仓库形态也应该强化同一套心智模型

架构并不只是包结构故事,它同样是仓库结构故事:docs 负责教路径,reference app 负责证明路径,生成事实负责公开边界,app-local code 负责保持服务 ownership 的可读性,control plane 负责把规则变成机器可读的信号。

docs

承担学习路径与边界解释

Docs 负责先教会 canonical path、request flow 与公开心智模型,而不是让读者单靠包名猜结构。

打开文档

reference

提供可运行的服务形态

reference/standard-service 让默认 bootstrap 与 route ownership 保持可见,让新读者先拥有一条可以对照的仓库基线。

阅读参考应用

generated facts

公开发布库存与成熟度信号

同步生成的事实把稳定根、扩展家族与成熟度信号变成机器可读信号,而不是重复书写的营销表述。

查看项目状态

app-local code

让 ownership 保持可见的代码位置

internal/app 是服务自有 wiring 最该保持直观的地方。在更深的能力包进入讨论之前,它应该始终是第一站。

查看示例路径

control plane

供人和 agent 读取的机器可读规则

specs/ 以机器可读格式存放 dependency rules、task routing 和 change recipes。AI coding assistant 在改代码之前先读这些规则来分类工作——和人工评审使用的是同一套信号。

阅读 Agent 工作流

stable roots 与扩展模块的区别,本质上是归属区别

这两层都重要。差别在于,一层负责保护几乎所有服务都会依赖的默认路径,另一层负责防止可选能力继续向内渗透。

信号 稳定根 扩展模块
归属判断 它是否属于几乎所有服务都会依赖、并且需要更强兼容性承诺的职责? 它是否属于可选、协议相关或产品形态很强的能力,不应该重写默认路径?
演进速度 更慢、更收敛、更容易在评审中为兼容性负责。 可以更快演进,只要和 canonical path 的边界仍然足够清楚。
读者预期 新读者应该只看文档和 reference app 就能理解为什么它存在。 新读者应该先理解自己为什么需要它,再决定是否打开具体家族。

回到 reference path 继续

架构页解答改动应该落在哪一层。下一个问题是如何运行它——先从「开始使用」入手,再用「示例」看它实际跑起来的样子。如果仍在评估 Plumego 是否适合,适配判断页是更好的起点。