洋葱模型解析与应用的深度理解
前言
要理解洋葱模型,首先需要对其结构有一个简单的了解。
正文
洋葱模型的结构如下图所示:
这是一种被称为ddd的洋葱模型。
最内层是领域模型。
为什么是领域模型呢?这是核心的业务逻辑模型。
领域模型的作用是实现我们的业务逻辑模型,这是非常重要的一环。
接下来看看领域服务(domain services)是什么?
当涉及多个领域模型时,就需要领域服务来进行关联。
每个领域模型就是一个聚合根。
领域模型和领域服务实现了我们的核心业务,也就是领域层。
实际上,我们的一个业务例子,应该调用一个领域模型或领域服务的方法,而不是调用多个。
因为领域层已经实现了核心领域逻辑业务,将多个领域逻辑业务组合在一起也是业务逻辑,因此应该不放在上层。
接着向上看,为什么上面还有一个应用服务(application service)呢?理论上领域层已经实现了业务逻辑。
应用服务是用于数据组装和校验的。
应用服务基本上做两件事:
- 单纯查询数据,当需要查询数据时,因为这不涉及领域业务。
有人可能会问,为什么查询不涉及领域业务呢?因为查询不会进行任何修改,本质上只是对数据库的查询,不涉及任何业务。
- 为领域层提供基本数据校验和数据查询
基本数据校验:
例如,如果需要修改用户A的姓名,那么应用服务层需要做的事情是检索用户A,验证该用户是否存在,如果不存在则抛出业务异常。
数据查询:
例如,领域层有一个需要修改用户电子邮件的方法:
void EditEmail(string email, string code)
{
if(Code == code)
{
Email = email;
}else
{
throw new Exception();
}
}
该方法需要传入电子邮件和验证码,验证码是用于校验的。
可能应用服务方法是这样的。
void EditEmail(string email)
{
1. 获取验证码
2. 调用领域层EditEmail方法
}
这只是一个假设,也可能需要获取其他服务的信息。
有人可能会产生疑问,这是初学者的疑惑。
例如,业务案例是这样的,在修改电子邮件后,需要更新领域B的字段C,此时B领域在另一个B服务中。
这应该在应用服务中这样写吗?
void EditEmail(string email)
{
1. 获取验证码
2. 调用领域层EditEmail方法
3. 调用服务B更新字段C
}
还是在领域层中:
void EditEmail(string email, string code)
{
if(Code == code)
{
Email = email;
// 调用服务B更新字段C
}else
{
throw new Exception();
}
}
这应该在领域层实现,因为这本质上是业务逻辑的一部分,涉及到修改,保持一致性和业务完整性。
是否放在领域层取决于它是否是该领域的业务逻辑。
在外部环就是具体的实现,例如具体的服务、数据库、作业等。
应用服务也只是数据的组装和校验,没有具体的实现,真正的实现在基础设施。
这几层构成了一个应用程序。
接着再向外看,就是Kibana等外部依赖服务了。
这实际上实现了控制反转。
业务核心逻辑处于最高层,具体的实现位于较低层,高层不依赖低层,而具体实现依赖高层。
为什么要这样做呢?因为我们开发项目的目的是解决业务问题,业务是核心逻辑。
当需要替换底层时,可以立即进行替换,而核心逻辑则无需更改。
当需要更改核心逻辑时,而又无需关心底层逻辑,因为高层不依赖低层。
最重要的一点是,洋葱模型就是洋葱模型,不要与微服务、cqrs、eventbus等挂钩,那是不同的事情。
结
简单的自我理解