Clean Architecture与DDD富模型校验逻辑分层放置方案咨询
关于DDD分层校验边界的核心结论
首先要把两类完全不同的校验规则做拆分,不要把所有校验都归为「应该塞某一层」的单一分类:
- 第一类是领域不变量规则:只要领域对象存在,无论走哪个业务用例、从哪个入口触发操作,都必须遵守的固有约束,这类规则100%要放在领域层实现
- 第二类是用例特定的输入规则:和具体业务场景、操作流程绑定的参数要求,这类规则就应该放在应用层的Command校验逻辑里实现
两类规则的具体划分逻辑
你提到的「创建操作同名字段必填、更新操作同名字段选填」的差异,本质上属于第二类用例特定规则,本来就不属于领域层要管的范畴。
举个最常见的例子:
对于用户实体,领域层唯一要守护的和昵称相关的规则只有:昵称长度必须在2-20字符之间,不能包含平台违规敏感词。
这个规则是不管你创建用户、还是更新用户昵称,只要你要给昵称赋值,就必须满足的底线,不管什么场景都不能破,所以这段校验逻辑要写在Nickname这个值对象的构造逻辑、或者User实体的changeNickname()方法里,只要传入的值不符合规则,直接抛出领域异常,不需要关心调用方来自哪个用例。
而「创建用户时必须传昵称、更新用户信息时昵称可以不传」根本不是用户实体本身的固有约束:
- 创建用户要求传昵称,是「用户注册/新建」这个用例的流程要求——你走完注册流程总不能生成一个没有昵称的不完整用户账号
- 更新用户时昵称可传可不传,是「编辑用户资料」这个用例的流程要求——用户可能只想改头像、改绑定手机号,本来就不需要强制修改昵称
这个差异完全是不同用例的流程设计导致的,和领域对象的核心约束没有任何关系,放在应用层做Command参数校验是最合理的选择,硬塞到领域层反而会污染领域模型。
落地时的分层校验参考
实际写代码的时候可以按这个边界拆分,逻辑会非常清晰,不会出现职责混乱的问题:
应用层Command校验职责
- 按当前用例的要求做必填项检查:比如
CreateUserCommand校验昵称、手机号字段非空;UpdateUserCommand仅校验传入字段的存在性,不强制要求所有字段都传 - 做基础格式预校验:比如检查传入的字符串长度、数据类型是否符合基础要求,能不能转换成对应的领域值对象,不满足直接返回参数错误,不需要下沉到领域层
- 做身份权限、幂等性这类和用例流程相关的校验
领域层校验职责
- 值对象创建时校验自身的合法性:比如手机号值对象创建时校验号段规则、地址值对象校验省市区格式合法性,不合法直接抛出值对象无效的领域异常
- 实体执行业务方法时校验领域状态规则:比如订单执行
pay()方法时校验当前订单状态是不是待支付、用户余额够不够扣,不满足状态流转规则直接抛领域异常 - 所有跨实体、跨聚合的领域规则校验,比如下单时校验商品库存是否足够,这类规则无论哪个用例触发下单都必须遵守,必须放在领域层
常见踩坑提醒
不要为了追求「所有校验都放领域层才是纯正DDD」的错误认知,把用例层面的必填校验硬塞到实体构造函数里,最后为了兼容更新操作的选填逻辑,又在实体里加一堆特殊分支绕过构造函数校验,只会让领域模型和具体用例强绑定,越写越难维护。领域层的核心职责是守护模型自身的一致性边界,它不需要关心调用方是做创建还是做更新,只要传入的参数满足模型自身的合法要求即可。
内容的提问来源于stack exchange,提问作者tarek salem
相关产品推荐
相关产品推荐

