constructor - 如果涉及访问外部 API(域驱动设计),我应该将值对象创建逻辑放在哪里
问题描述
我有一个实体,假设它是 UserAccount。UserAccount 有一个值对象,即 MembershipID。MembershipID 成为值对象,因为它内部有一些业务逻辑,而不仅仅是一个字符串。但这里有一个棘手的情况。
要生成会员 ID,我需要使用随机字符串和创建用户帐户的时间戳。然后,这两个值被发送到外部 REST API,它的成功响应将包含 MembershipID。
这个值对象的创建逻辑涉及到外部 API 调用,这是属于基础设施层的东西。我首先想到的是工厂和存储库作为放置这个创建逻辑的地方,但这两个是为实体设计的,而不是值对象。那么我应该把这个值对象的创建逻辑放在哪里而不违反DDD呢?
解决方案
基本思想:应用程序(不是领域模型)负责从模型之外的世界获取数据。
要生成会员 ID,我需要使用随机字符串和用户帐户创建时间的时间戳。然后,这两个值被发送到外部 REST API,它的成功响应将包含 MembershipID。
实际上,您有一个看起来像函数的东西,它接受一个随机种子和一个时间戳作为参数,并返回一个 MembershipId。
该功能将由知道如何使用 REST、外部 api 托管在哪里等的应用程序组件来实现。
该函数将被调用,通常是两种方式之一。要么应用程序将完成工作,然后将值传递给域模型......
m = membershipId(domainModel.randomSeed, domainModel.createdTime)
domainModel.assignMembershipId(m)
或者,我们将把能力传递给领域模型,并让模型决定是否调用它:
DomainModel::doIt(membershipId) {
this.membershipId = membershipId(this.randomSeed, this.createdTime)
}
这两种方法之间的区别主要在于您如何处理故障。在第一种方法中,所有错误处理代码都位于域模型之外,只有在可用时才会接收值。第二个答案适用于更程序化的风格 - 您只需随时使用应用程序层提供的回调 - 但是当您在“域”代码中工作时,您必须更加注意错误处理。
我发现前一种风格 - 与外部 API 在我们想要的时候可能不可用这一事实相关的所有复杂性 - 更符合将域代码与管道分离的 DDD 方法。实际上,域模型变成了一个简单的内存状态机,它进行簿记,并且应用程序与所有管道问题进行交互。
现在我将外部 API 访问逻辑放在域服务中
使用上面的样式,您将在应用程序服务中实现访问逻辑,而不是在域服务中。从“其他地方”获取数据不是领域建模问题,而是管道问题。
第二种方式,我们将能力传递给要调用的域模型,类似于使用域服务。你会在很多例子中看到这种风格,因为人们倾向于应用模式来匹配他们通常做的事情,而不是调整他们的风格来匹配新的模式。
推荐阅读
- python - 运行python直到成功
- node.js - 带有 lambda 的 amazon lex aws 上的自定义有效负载
- mysql - 在 SQL 中将时间转换为范围
- python - 向量化用于修改图像分割中使用的掩码的 python 代码
- mysql - 如何在 mysql 查询中使用列别名更改列标题
- windows - 如何在 RTMP 流中嵌入 pic_timing SEI 挂钟时间码?
- php - 如果当前日期在两个日期之间,Wordpress 会显示不同的元素
- json - 使用 json typescript,导出一个类型化的对象
- node.js - 在 Windows 10 上无法识别 NodeJS 安装的模块
- javascript - 以不同的时间和功能循环播放视频