接口名应体现领域行为而非技术细节,优先使用单数名词或动词名词化形式(如placer、validator),避免缩写与实现暗示;方法签名需稳定、参数用结构体、返回具体错误;接收者类型须统一且匹配语义;接口设计应聚焦领域契约而非通用性。

接口名要体现领域行为,不是技术细节
领域接口的核心是让调用方一眼看懂“它能做什么”,而不是“它怎么做的”。比如在订单系统里,OrderService 比 HTTPHandler 更贴近业务;PaymentProcessor 比 JSONMarshaller 更明确职责边界。
常见错误是把接口名写成 OrderRepo 或 OrderDAO —— 这暴露了存储实现,违背了领域驱动的抽象原则。应该换成 OrderRepository(首字母大写、名词化)或更进一步,用动词短语表达能力:Placer、Confirmor、Refunder。
- 接口名推荐用单数名词或能表达能力的动词名词化形式(如
Validator、Notifier) - 避免缩写(
CRUDer)、技术栈词(PostgresStore)、实现暗示(MemcacheClient) - 包内接口名不加前缀;跨包使用时靠导入路径区分,而非
MyAppOrderService
方法签名必须稳定,且只暴露必要参数
领域接口的方法一旦发布,就应尽量避免修改签名——因为下游可能已多处实现或依赖。新增能力优先用新接口,而不是往老接口里加方法。
例如订单确认逻辑升级后需要支持多种支付方式,不要改 Confirm(orderID string) error 为 Confirm(orderID string, method PaymentMethod) error,而是定义新接口 ConfirmorWithMethod 或扩展参数结构体:
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
type ConfirmRequest struct {
OrderID string
Method PaymentMethod
Source string // 来源渠道,可选字段
}
func (c *OrderService) Confirm(req ConfirmRequest) error { ... }
- 参数尽量用结构体传入,便于后续扩展字段而不破坏兼容性
- 返回值优先用具体错误类型(如
ErrInsufficientBalance),而非泛化的error,方便调用方做领域级判断 - 避免在接口方法中暴露底层技术概念:不传
*sql.Tx、不返回json.RawMessage、不接收context.Context除非确实需要控制生命周期(这时应明确定义在接口文档里)
接收者类型决定谁可以满足该接口
这是最容易踩坑的地方:一个结构体是否满足接口,不只看方法有没有,更取决于接收者是值还是指针。
假设你定义了 Shippable 接口要求 Ship() error,而你的 Order 类型实现了 func (o *Order) Ship() error,那么只有 *Order 能赋值给 Shippable,Order{} 值会编译失败。
- 如果接口方法会修改状态(如扣库存、发消息),用指针接收者;如果只是读取计算(如
TotalAmount()),值接收者更轻量 - 同一接口的所有方法接收者类型要一致,否则会出现部分满足、部分不满足的混乱状态
- 导出字段必须首字母大写,否则外部包无法调用其方法,导致“看似实现了却不能用”
空接口和类型断言不是领域接口的设计起点
别为了“通用”而提前用 interface{}。领域接口的价值恰恰在于约束和可读性——它告诉所有人:“这个东西必须能发货、能取消、能退款”,而不是“这个东西可以塞进任意变量里”。
类型断言(v, ok := i.(ConcreteType))应该只在极少数集成点出现(如适配器层),绝不该出现在核心领域逻辑里。一旦你在业务代码里频繁写 if x, ok := req.(OrderRequest); ok { ... },说明接口抽象失败了。
- 空接口适合做泛型容器(如日志字段、配置项)、反射入口,不适合建模领域行为
- 组合接口比嵌套类型更符合领域建模习惯:比如
type Shippable interface { Shipper; Cancelable },比继承type Shippable struct { *BaseOrder }更清晰 - 测试时用真实实现(哪怕内存版)代替 mock,能更快暴露接口契约是否合理
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










