很多小伙伴在实践DDD的过程中,一直分不清应用服务 Application Service(后面简称App)和领域服务 Domain Service(后面简称Domain)。最近在和几个同学在业余时间做一个小帅包子铺
项目,同样遇到这些问题:
光讲概念就没意思了,下面我会结合小帅包子铺
中的一个小场景说一说。
如果你第一次访问小帅包子铺
系统(后面简称系统),会要求通过手机号登录,输入手机号、获取验证码,提交登录就可以了。
讲个题外话,这个系统是真实存在的,完全基于DDD开发,预计在4月底会上线。文末有对这个项目的介绍。
在你提交登录后,系统需完成这些事:
我们尝试用时序图画一下这个过程,假设这时只有一个Service层:
这个时序图没什么大毛病,只是直觉上看起来,好像Service承担的东西有点多。
这时很容易能想到,把职责稍微拆分一下,分给其他的Service,我们忽略掉其他要素,只保留Service,大概是这样:
这时你会发现,Service的层级就显示出来了,LoginService是在组合其他几个Service的能力,这就是App和Domain Service的第一个区别:
仅此而已吗?为了做一个简简单单的登录,居然用了4个Service!!当然不是,划分出来的Domain层会有更大的用途。
现在我们有第二个需求:如果用户是在包子铺门店直接买包子,可以向收银员报一个手机号,假如这个手机号还未注册,则直接注册成为一个新用户。(收银员会在收银机上操作,下单时录入手机号)
咦?在门店买包子和登录,这两件事居然有交集了!它们都有可能发生用户注册。这时就可以把用户注册进行复用了,同时优化一下Service之间的关系:
这就是App和Domain Service的第二个区别:
App层和Domain层的输入、输出(也就是入参、出参)有区别吗?
当然有区别,例如不同的登录方式,App层接口定义可能是:
public interface LoginAppService {
// 短信登录,需要手机号、验证码
// 返回通用Response,带user id
Response<String> smsLogin(SMSLoginReq req);
// 邮箱登录,邮箱、密码
// 返回通用Response,带user id
Response<String> emailLogin(EmailLoginReq req);
}
在App层,为了保证与客户端交互上的一致性,会使用通用的返回体Response
,如果发生了异常,需要在Response里设置错误码。
但在Domain层,为保证最大程度可以复用,不能把App层的req对象透传下来,而是需要先转化为领域对象,因此Domain层的接口定义是:
public interface UserService {
// Domain层直接以领域对象做为入参,注册是一个写请求,无需返回值
void register(User user);
}
这是App和Domain Service的第三个区别
通常情况下,事务应该是在App层管理,App既然做为编排组合的组织者,需要保证事务性
我的个人习惯来说,如果业务逻辑足够简单,且没有可复用的部分,可以不需要Domain层
都可以的,只是说如果有Domain层进行了一些原子化封装,就可以尽量把对repository的访问下沉到Domain
在架构上新增一个层,不仅是加了一层这么简单,层数的增加意味着架构变得复杂,需要考虑的因素也要更多。我只看是否能解决某些问题,若不能解决问题,加再多层也只是增加负担。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有