跳到主要内容

模块怎么写

统一内部形状

业务模块通常长这样:

services/{module}/
domain/ # 领域对象与核心规则
application/ # 用例编排
adapter/http/ # HTTP 适配
adapter/repo/ # 仓储实现
app.go # 模块注册入口(或 runtime 接线)
cmd/{module}/ # 抽取服务时的边界入口(可能尚未接线)

各层职责

做什么不要做什么
domain不变量、实体、领域错误依赖 HTTP、具体 SQL 驱动细节
application用例、事务边界、调用仓储/合约直接解析 HTTP 细节散落各处
adapter/http路由、DTO 映射、状态码堆核心业务规则
adapter/repo持久化实现被其他 service 直接 import

Runtime 接线(推荐形态)

活跃维护的模块倾向于显式 Runtime:

type Runtime
type RuntimeDeps

func NewRuntimeWithDeps(deps RuntimeDeps) (Runtime, error)
func RegisterRuntime(app *server.HTTPServer, deps platform.Deps, runtime Runtime) error

说明:

  • Deps 显式传入:缺依赖在启动期失败,而不是运行期空指针
  • RegisterRuntime:把 HTTP 路由挂到 monolith 的 server 上
  • 旧式 Register(app, deps) 可能仍存在兼容;新代码优先 Runtime 形态

contracts:模块之间的「法律」

libs/contracts 放:

  • 跨模块 DTO
  • 接口(端口)
  • 共享错误类型与事件形状

规则:

  • 允许services/foolibs/contractslibs/platform
  • 禁止services/fooservices/bar/... 内部包
  • 需要别的模块能力时:通过 contracts 接口 + monolith 装配时注入实现

这是后续「拆服务不改业务语义」的前提。

路由注册注意

使用 pkg.gostartkit.com/web 时:

  • 同一路径深度不要用不同参数名造成冲突
  • 错误示例:/devices/:gateway_id/subdevices/devices/:device_id/gateway 参数名不一致冲突
  • 改路由时补测试:走真实注册入口或 monolith 全图,捕获 router panic

平台能力复用

优先复用已有平台能力,而不是每个模块再造一遍:

能力倾向位置
事件libs/contracts/events + 共享 bus
可重试后台任务services/jobs
组织级配置services/configuration
文件/附件services/files
出站 webhookservices/integration
用户侧通知编排services/notifications

工程优先级(写代码时)

  1. 正确性、安全、租户隔离、数据完整
  2. 公共 API 与持久化兼容
  3. 可测量的热点路径性能
  4. 简单可维护

不要用正确性换性能;非平凡性能改动应有依据(基准、分配、DB 往返等)。

自检

  • 新用例落在 application,而不是 handler 里摊大饼
  • 无跨 service 内部 import
  • 新路由参数命名无冲突
  • 需要的能力是否已有模块可注入

下一步:本地开发