Skip to content

代码注释规范(领域模型与应用服务) ​

本文规定领域模型层与应用服务层的业务语义注释标准:哪些元素必须写、写到什么程度、哪些写法无效。类级注释、公有方法注释等通用要求见仓库根目录 AGENTS.md;本文在其之上补充字段级标准,不重复通用条款。

1. 定位与适用范围 ​

1.1 注释承载什么 ​

代码能表达"是什么",表达不了"业务上为什么、口径是什么、边界在哪"。合格的业务语义注释只承载三类信息:

类型说明示例
业务口径单位、币种、取整规则、编码来源、取值域实付金额:平台优惠后金额,与 platformDiscount 同币种
边界约束状态迁移范围、写入时机、只读 / 不可逆只推进物流状态,不改动支付状态与订单生命周期状态
副作用事实记录操作、收集事件、触发同步记录 ADD_ITEM 操作,不发布事件

以下内容不属于注释职责:实现步骤、伪代码、类型复述、示例代码、需求编号与版本历史。这些信息由代码本身、设计文档与 Git 历史承载。

1.2 适用范围与强度 ​

层元素强度要求
领域模型聚合根 / 实体字段命中 §1.3 清单时必填业务口径
领域模型公有业务方法必填业务意图 + 状态迁移范围 + 副作用
领域模型公有构造方法必填创建语义、初始状态、副作用
应用服务{Action}Input 类必填三要素(见 §3.1)
应用服务Input 字段命中 §1.3 清单时必填调用方传入口径

本规范只覆盖上表元素。值对象、领域事件、参数对象(IParamObject)与接口层 DTO 的注释,按 AGENTS.md 的通用要求执行,不在本文强制范围。

1.3 什么时候必须写 ​

字段采用非自解释才必填原则。命中以下任一类,必须写业务口径注释:

#类别判定信号必须写清
1金额 / 数量类型为 Money / BigDecimal / 数值单位、币种、含税与优惠口径
2时间LocalDateTime / Instant / 时间戳业务本地时间还是系统时间、由谁写入
3编码 / 状态命名含 code / status / type编码的业务来源(如快递公司编码)、取值域
4派生字段由其他字段计算得出由谁派生、何时刷新
5外部回填由外部系统返回后写入来源系统、写入时机
6非直观命名名称存在歧义或业务专有词业务含义与易混淆字段的区别

名称已完整表达业务口径、且不命中上表的字段,注释可省略——productId、receiverPhone 属于此类。

⚠️ 重要约束:同一类内风格必须一致。不能一半字段有注释、一半没有;命中清单的必须写,未命中的可省,但"可省"不等于"随手补一句翻译"。

可执行判定法:把字段名交给不熟悉该业务的人,若他无法说出"这个字段在业务上是什么口径",就属于必须注释。

2. 领域模型层 ​

2.1 字段注释:写业务口径 ​

  • 位置:字段上方,使用 Javadoc 块注释。
  • 内容:业务含义 + 命中 §1.3 时的口径信息。
  • 禁止:同义反复(/** 商品标识 */ private Long productId;)、持久化细节(列名、字段长度)、与代码重复的类型说明。
java
// ✅ 推荐:状态字段写清赋值时机(Order)
/**
 * 订单生命周期状态:仅在创建 / 取消 / 关闭 / 完成等生命周期动作中显式赋值。
 */
private OrderStatus status;

// ✅ 推荐:外部回填字段写清来源(Order)
/**
 * 库存预占单号:外部库存系统预占成功后的事实回填。
 */
private String reserveNo;

// ✅ 推荐:派生字段写清派生关系(OrderItem 补充后)
/**
 * 订单项小计,由 price 与 quantity 相乘派生,随数量或单价变更同步刷新。
 */
private Money subtotal;

// ❌ 反例:同义反复,不提供任何业务信息
/** 商品 ID。 */
private Long productId;

// ❌ 反例:派生字段未写派生关系,读者无法判断何时刷新(OrderItem 原始写法)
private Money subtotal;

2.2 业务方法注释:意图 + 状态迁移 + 副作用 ​

公有业务方法必须回答三件事:

  1. 做什么:一句话说明业务动作。
  2. 影响哪些状态:明确本次变更推进 / 不推进哪些状态位;相邻状态容易被误解时,显式写出"不改什么"。
  3. 有什么副作用:是否 recordOperation、是否 collectEvent、是否触发数据同步。

@param 只写参数的业务口径;参数名已表达含义时省略,不写 @param quantity 数量 这类复述。

java
// ✅ 推荐:动作 + 副作用 + 状态边界(Order)
/**
 * 标记订单已发货并记录物流信息,并发布订单发货事件。
 * 只推进物流状态,不改动支付状态与订单生命周期状态。
 *
 * @param logisticsInfo 物流信息
 */
public void ship(LogisticsInfo logisticsInfo) {
    // ...
}

// ✅ 推荐:说明与相邻动作的差异,防止误用(Order)
/**
 * 修正已录入的物流信息,并发布物流信息修正事件。
 * 仅替换物流信息用于纠错,不推进物流状态、不改动支付状态与订单生命周期状态。
 */
public void correctLogisticsInfo(LogisticsInfo logisticsInfo, String reason) {
    // ...
}

// ❌ 反例:裸方法,读者无法判断是否会刷小计、是否发事件
public void changeQuantity(int quantity) {
    // ...
}

业务方法遵循"纯赋值"原则(见聚合设计原则 §3.3)。注释因此不写守卫条件、字段组装、格式转换等内容——这些职责不在业务方法内。

2.3 构造方法注释 ​

  • 业务构造:说明构造语义、初始状态与副作用(是否 recordOperation / collectEvent)。
  • 无参 protected 重建构造:一句话说明"仅供持久化框架重建",不写业务逻辑。
java
/**
 * 基于初始化数据创建订单并持有初始订单项,发布订单创建事件。
 *
 * @param data    订单初始化参数
 * @param orderId 订单标识
 */
public Order(OrderInitData data, Long orderId) {
    // ...
}

/**
 * 仅供持久化框架重建,空实现。
 */
protected Order() {
}

3. 应用服务层 ​

3.1 Input 类注释:三要素 ​

每个 {Action}Input 的类注释必须写清三件事:

  1. 业务语义:哪个用例的入参(如"订单发货入参")。
  2. 边界:与协议无关、应用层自有扁平结构、不引用领域值对象。
  3. 转换归属:由哪个 Factory / Updater 转换为领域对象。
java
// ✅ 推荐:三要素齐备(ShipOrderInput)
/**
 * 订单发货入参:业务语义、与协议无关、自有扁平结构。
 * 不引用领域值对象 LogisticsInfo,由 OrderShipUpdater 转换为领域 LogisticsInfo。
 * 时间字段使用 LocalDateTime(业务本地发货时间,与 LogisticsInfo.shippedAt 对齐)。
 */
@Data
public class ShipOrderInput {
    // ...
}

3.2 Input 字段注释:写协议看不出来的口径 ​

Input 字段是外部调用方与领域模型之间的契约,字段注释写调用方必须知道的口径,而不是转换后的领域结构。

必写场景(在 §1.3 基础上补充):

场景要求
金额写清币种、是单价还是小计、是否已扣优惠
时间写清是业务时间还是系统时间、时区
编码型字段写清业务来源(如 companyCode 为快递公司编码)
组合字段写清多个字段如何共同构成一个领域值对象(如金额 + 币种 → Money)
java
// ✅ 推荐:金额与币种成组说明(AddOrderItemInput)
/**
 * 单价币种。
 */
private String currency;

/**
 * 单价金额。
 */
private BigDecimal unitPrice;

// ✅ 推荐:编码型字段写清来源(ShipOrderInput 补充后)
/**
 * 快递公司编码,由物流服务商提供。
 */
private String companyCode;

// ❌ 反例:金额与币种分离,且未写两者关系(CreateOrderItemInput)
private BigDecimal unitPriceAmount;

private String unitPriceCurrency;

CreateOrderInput.orderItems 的字段注释应说明元素语义与转换归属,例如"下单商品项,由 OrderFactory 转换为领域 OrderItem 列表"。

4. 注释形式规范 ​

项规则
位置字段 / 方法上方;禁止行尾 // 与行尾块注释
单行与多行单句用 /** ... */;多句或含边界说明用多行块。同一个类内保持一致
分段多段内容用 <p> 分隔
元信息作者标注按团队约定,不做要求;不写 @version / @since / 日期 / 需求号
参数@param 只写业务口径,禁止复述参数名和类型;无额外口径时省略
术语与代码标识符、领域术语保持一致(订单项、预占单号、实付金额)
语言中文,使用中文标点,句末加句号

5. 常见反模式 ​

反模式问题正确做法
翻译式注释(/** 商品 ID */)与代码重复,零信息量名称自解释则省略;否则写业务口径
命中金额 / 时间 / 编码类却不写口径调用方靠猜,易错按 §1.3 清单补口径
注释代替命名用长注释解释糟糕命名先改命名,再补必要口径
过期注释行为已改、注释未改,误导读者注释与行为不一致视为缺陷,随代码同改
注释写实现步骤 / 伪代码与代码重复且易失效只写意图、边界与副作用
业务方法注释漏掉状态边界相邻状态被误改写清"推进什么、不推进什么"
@param 复述参数名无信息量省略,或补充业务口径
注释里放示例代码示例失真后无人维护示例放文档,注释只写规则

6. 自检清单 ​

7. 下一步 ​