实体类、值对象和服务层组件按身份、可变性与生命周期自然区分:实体类有id、可变状态、外部管理生命周期;值对象无id、不可变、表达完整业务概念;服务层无状态、仅协调编排。

实体类、值对象和服务层组件不是按“要不要用”来选,而是按“它代表什么、生命周期归谁管、能不能被修改”来自然区分。核心是让每类对象各司其职:实体类管身份与状态变更,值对象管不可变的业务概念,服务层只做协调不藏逻辑。
实体类:有唯一标识、可变状态、生命周期由外部管理
实体类(如 User、Order)必须具备唯一标识(ID),状态可随业务推进而改变,且它的存在独立于某次操作——比如订单创建后可能经历支付、发货、退货等多个阶段。它不写业务方法,只提供受控的字段访问。
- 字段全为
private,getter/setter 带基础校验(如setEmail()检查格式,但不判断“用户是否已注册”) - 绑定持久化框架(如 JPA 的
@Entity、MyBatis 的@Table),只出现在 DAO 层或 Repository 接口参数/返回值中 - 构造函数强制初始化关键字段(如
new Order(orderId, createTime)),避免半初始化对象 - 不继承、不实现业务接口,也不持有其他实体引用(用组合而非继承表达关系)
值对象:无 ID、不可变、表达完整业务含义
值对象(如 Money、Address、OrderId)没有独立身份,两个内容相同的值对象视为相等(重写 equals/hashCode)。它一旦创建就不能改,要变就新建一个——比如修改地址,不是调 address.setCity("上海"),而是 user.updateAddress(new Address("上海", "浦东"))。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 所有字段
final,构造即完成,无 setter 方法 - 常用作实体的组成部分(
Order持有Money totalAmount),也可单独用于参数传递(如paymentService.pay(Money amount)) - 可自带业务语义方法(如
Money.add(Money other)、Address.isValid()),但仅限计算与验证,不触发状态流转 - 不与数据库表直接映射,不带 JPA 注解,也不进 DAO 层
服务层组件:不存状态、只编排、依赖明确
服务类(如 OrderService)本身无状态,是纯协调者。它不保存数据,也不封装规则——规则在 BO 或领域服务里;它只负责把 DO、VO、外部服务、事件发布器组装起来,按事务边界执行。
- 方法签名返回 BO、DTO 或 void,绝不直接返回实体类(避免上层误改 DO 状态)
- 事务注解(
@Transactional)加在 service 方法上,但状态变更逻辑必须下沉到 BO 中(如orderBO.cancel()内部校验+改状态+发事件) - 不写 if-else 判断业务可行性(如“能否退款”),这类判断属于 BO 职责;service 只调
if (refundBO.isEligible()) { refundBO.execute(); } - 依赖通过构造注入,明确声明所需组件(如
OrderRepository、InventoryClient、EventPublisher),不隐藏间接依赖
三者协作的关键细节
它们之间靠组合 + 接口 + 明确边界协作,而不是继承或强引用。比如订单取消流程:
-
OrderDO提供getStatus()和setStatus()(仅校验格式) -
OrderBO持有OrderDO实例,封装cancel():查库存、校验时间窗、调扣减服务、发OrderCancelledEvent -
OrderService创建OrderBO,传入OrderDO和必要依赖,调orderBO.cancel(),捕获异常并统一处理
不复杂但容易忽略。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










