背景:假设有一个 API 项目用的是 aspnetcore ,然后项目各个功能拆分成独立的组件库了(比如 identity 、user profiles 、sessions 、posts ),都有各自独立的接口,UI 层( API )需要哪个就引用哪个库配置哪个库。
比如有一个创建用户的接口,首先框架会自动基于 Model 属性和数据类型对请求数据做基本的数据验证,比如是不是一个有效的 int 、Guid 、Email 等等。
假如接下来我要验证这个电子邮件是不是符合某些规则,然后是不是唯一的没有被别人注册使用,这个逻辑我该在组件的 Service 里做还是数据存储层 Repository 里做,还是就在 Controller 那验证?
我搜了下外网的回答,各种观点的都有,总结下来就是都觉得自己的是好的,别的都不建议,然后都有一点点道理。
有的说应该确保从 Repository 存进和取出的数据都应该是有效的,否则世界就会变得太复杂,所以在 Repository 里验证;(到底有没有必要再在这里做一次验证?)
有的说应该在 Entity 或 Model 做(里面还分成很多派系);
有的说在 Service 做;
有的在 Specification ;有的在 Command ;有的在 Event ;
有的说在最开始创建或修改的地方做(通常是 Controller 那里);
还有,数据无效(比如有非法字符,或这个电子邮件已经被使用)该使用异常还是返回值?
有的观点是如果大量数据需要验证,使用异常会打断流程,但如果我在那层做完全部验证在把这些问题整合抛出一个异常呢?而且接口返回值也不需要再套几层对象包装。
比如我觉得
Task<User> Repository.AddAsync(User user)
就比
Task<IdentityResult> Repository.AddAsync(User user)
清晰简单,我也不用考虑这个 IdentityResult 怎么来兼容其它类型的操作,外面的那层也可以不用套上这个返回值类型的枷锁。
为什么我会需要考虑这个 IdentityResult 怎么来兼容其它类型的操作,比如我有一个接口,给用户添加一个 Claim ,然后返回完整的信息而不需要再查询一次,接口为这样:
Task<UserClaim> AddClaimAsync(User user, Claim claim)
如果改成返回 IdentityResult 就需要给 IdentityResult 增加属性,但是大部分时候是不需要这个属性的。
你们会怎么做的?
1
chihiro2014 2022-06-17 21:46:58 +08:00 1
在入口 Controller 做验证不行么?毕竟 Dao 层取出来的数据都是规范的。保证 Controller 的出和入的规范就好了
|
2
crysislinux 2022-06-17 21:47:17 +08:00 via Android 1
一般来说我倾向于不依赖别的服务能同步验证的,就放在 entity 里,比如 email 格式。其他的就搞个 factory ,在创建 entity 的时候验证,比如保证 email 唯一
|
3
leoskey 2022-06-18 14:04:20 +08:00 1
Abp 推荐的是 Email 校验在 EmailEntity 里完成对自身的校验。
如果 User.Email 是 string ,那么就在 User.SetEmail 里完成。(ps: 添加 SetEmail 方法 或 属性的 set 访问器都可以)。 email 唯一校验已超出 Entity 职责范围,应使用领域服务 UserManager 完成: userManager.SetEmailAsync(user, email) |
4
DreamStar 2023-03-01 11:40:54 +08:00
非空, 非负, 长度啥的在应用服务就搞定.
email 之类的实体数据, 用值对象解决, 构造的时候就判断了, 不可能有非法的. 唯一类验证交给 repo 服务做 exist 判断, 并发创建唯一交给数据库唯一索引就行 |