分层领域模型规约(新手友好版)
分层领域模型规约
领域模型本身有没固定的使用限制,但是在一套项目架构体系中需要有统一的规范。
领域对象描述:
-
DO(Data Object):此对象与数据库表结构一一对应,通过
DAO层向上传输数据源对象。 -
DTO(Data Transfer Object):数据传输对象,Service 或 Manager 向外传输的对象。 -
BO(Business Object):业务对象,可以由 Service 层输出的封装业务逻辑的对象。
-
Query:数据查询对象,各层接收上层的查询请求。注意超过2个参数的查询封装,禁止使用Map类来传输。
-
VO(View Object):显示层对象,通常是 Web 向模板渲染引擎层传输的对象。
各层之间的关系如下图所示,里面包含了领域模型的传递过程。

分层领域模型的核心是:给不同环节的 “数据” 分角色、定规矩,就像开一家外卖店,不同岗位(后厨、配送、前台)用不同的 “单据”,只包含自己需要的信息,不冗余、不混乱。下面用「外卖平台」这个生活场景,把每个概念讲透:
先记核心逻辑:
不同 “对象” 对应外卖店不同的 “单据 / 本子”,只干自己的活,数据不混用。
表格
| 模型名称 | 通俗理解(外卖店场景) | 核心作用 |
|---|---|---|
| DO | 原始档案本 | 存数据库原始数据,和表结构一一对应 |
| DTO | 配送员的配送单 | 跨环节传数据,只带需要的信息 |
| BO | 后厨的订单处理单 | 封装业务规则,服务层内部用 |
| Query | 前台的查询小票 | 封装查询条件,避免参数混乱 |
| VO | 用户 App 的订单详情页 | 给前端展示,数据做 “美化 / 简化” |
1. DO(Data Object):数据对象 → 外卖店的「原始档案本」
通俗解释:
就像外卖店的 “顾客档案本”,每一页只记数据库里的原始信息,格式和数据库表完全一致,没有任何加工、删减。比如档案本里只记:顾客 ID、姓名、电话、地址、会员等级(和数据库customer表的字段一一对应),多一个字都不写,少一个字也不行。
实际案例:
假设外卖平台数据库里有个customer表,字段是:id(顾客 ID)、name(姓名)、phone(电话)、address(地址)、vip_level(会员等级)。那么代码里的CustomerDO对象就长这样(新手不用管语法,看属性就行):
cpp
运行
// CustomerDO:和数据库表字段完全对应,只存原始数据
class CustomerDO {
private:
int id; // 对应表的id字段
string name; // 对应表的name字段
string phone; // 对应表的phone字段
string address; // 对应表的address字段
int vip_level; // 对应表的vip_level字段
// 只有“存/取”这些数据的方法,没有任何业务逻辑
};
谁用? 只有 DAO 层(相当于 “管档案本的店员”)会用 DO,比如从数据库查顾客信息时,把表的一行数据转成 CustomerDO,只做 “原始数据搬运”,不加工。
2. DTO(Data Transfer Object):数据传输对象 → 外卖店的「配送单」
通俗解释:
配送员不需要看顾客的会员等级、档案 ID,只需要 “姓名、电话、地址、菜品、配送时间”—— 这些信息来自「顾客档案本(CustomerDO)」+「订单档案本(OrderDO)」,是专门为 “传输” 给配送员准备的单据,只包含需要的信息,去掉冗余。
实际案例:
用户下单后,后端需要把订单信息传给配送系统,就做一个OrderDTO:
cpp
运行
// OrderDTO:只包含配送系统需要的信息,冗余信息全删掉
class OrderDTO {
private:
string customerName; // 顾客姓名(来自CustomerDO)
string customerPhone; // 顾客电话(来自CustomerDO)
string address; // 配送地址(来自CustomerDO)
string dishName; // 菜品名称(来自OrderDO)
string deliveryTime; // 配送时间(来自OrderDO)
// 没有会员等级、订单支付金额(配送员不需要)
};
为什么要用? 比如配送系统只需要 5 个信息,若直接传 CustomerDO+OrderDO(共 10 个字段),会多传 5 个无用信息,浪费网络 / 内存;DTO 只传需要的,效率更高。
3. BO(Business Object):业务对象 → 外卖店的「订单处理单」
通俗解释:
后厨总管(Service 层)处理订单时用的 “内部单据”,不仅包含原始档案信息,还加了业务逻辑处理后的结果。比如:
- 原始信息:顾客姓名、订单金额、会员等级;
- 业务处理结果:“是否免配送费(会员免)”“预计送达时间(按距离算)”。BO 是 “服务层自己用的”,不会传给前端 / 配送员,只用来处理业务规则。
实际案例:
cpp
运行
// OrderBO:包含原始数据+业务处理结果,供服务层内部使用
class OrderBO {
private:
// 原始数据(来自DO)
CustomerDO customer; // 顾客原始信息
OrderDO order; // 订单原始信息
// 业务处理后的结果
bool isFreeDelivery; // 是否免配送费(会员等级≥2则免)
string expectedTime; // 预计送达时间(按地址距离计算)
// 业务逻辑方法:判断是否免配送费
void calculateFreeDelivery() {
if (customer.getVipLevel() >= 2) {
isFreeDelivery = true;
} else {
isFreeDelivery = false;
}
}
};
谁用? 只有 Service 层(后厨总管)用,比如算配送费、安排出餐顺序,都基于 BO 里的业务逻辑。
4. Query:数据查询对象 → 外卖店的「查询小票」
通俗解释:
顾客来问 “我想查近 7 天、朝阳区、点过麻辣烫的订单”,前台店员(Controller 层)不会拿一堆零散纸条(多个参数),而是写在统一格式的小票(Query) 上,字段就是 “时间范围、区域、菜品类型”,清晰不混乱。
实际案例:
如果不用 Query,查询方法要传 4 个参数(startTime、endTime、area、dishType),容易漏 / 乱;封装成OrderQuery就清晰了:
cpp
运行
// OrderQuery:封装所有查询条件,参数再多也不乱
class OrderQuery {
private:
string startTime; // 开始时间(近7天)
string endTime; // 结束时间
string area; // 区域(朝阳区)
string dishType; // 菜品类型(麻辣烫)
};
新手注意: 超过 2 个查询参数就用 Query,别用 Map(比如Map<String, String>)——Map 没有固定格式,新人维护时不知道里面有啥参数,容易出错。
5. VO(View Object):显示层对象 → 外卖 App 的「订单详情页」
通俗解释:
用户在 App 上看到的 “订单详情”,是专门做了 “美化 / 简化” 的信息。比如:
- 数据库里存的 “配送状态码 1”,VO 里改成 “骑手已接单(正在赶来)”;
- 数据库里的 “时间戳 1718901234”,VO 里改成 “18:30(还有 15 分钟)”;VO 只给用户看,数据都是 “人话”,没有原始编码 / 乱码。
实际案例:
cpp
运行
// OrderVO:给App前端展示的信息,全是用户能看懂的
class OrderVO {
private:
string orderNo; // 订单号(格式化:20240620123456)
string dishName; // 菜品名称
string deliveryStatus; // 配送状态(“骑手已接单”,而非数字1)
string expectedTime; // 预计送达时间(“18:30”,而非时间戳)
string address; // 配送地址(隐藏门牌号后两位,保护隐私)
};
谁用? 前端(App / 网页)用,后端把 BO/DTO 的数据加工成 VO,传给前端展示。
用「外卖订单流程」串起所有模型(新手必看)
- 用户在 App 上查订单:输入 “近 7 天、朝阳区、麻辣烫”→ 前台店员(Controller)把条件封装成
OrderQuery,传给后厨总管(Service); - 后厨总管让档案员(DAO)从数据库拿
CustomerDO(顾客档案)、OrderDO(订单档案); - 后厨总管把 DO 封装成
OrderBO,处理业务逻辑(比如判断会员是否免配送费); - 后厨总管从 BO 里挑出配送员需要的信息,做成
OrderDTO,传给配送系统; - 同时,后厨总管把 BO 里的信息加工成
OrderVO(比如把状态码转成 “骑手已接单”),传给 App 前端,用户看到易懂的订单详情。
新手总结:为什么要搞这些 “对象”?
就像外卖店不分岗位会乱套(管档案的去送外卖,配送员去做账),代码不分模型也会乱:
- 若直接把 DO 传给前端,会泄露用户隐私(比如会员等级、支付金额);
- 若不用 Query,查询参数多了容易漏传 / 传错;
- 若不用 BO,业务逻辑会散在各处,新人改代码时找不到在哪改。
简单说:每个对象只干自己的活,数据不混用,新手维护时一眼就知道 “改 VO 是改前端显示,改 DO 是改数据库数据”,不会懵。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)