首页 > 解决方案 > 如果涉及访问外部 API(域驱动设计),我应该将值对象创建逻辑放在哪里

问题描述

我有一个实体,假设它是 UserAccount。UserAccount 有一个值对象,即 MembershipID。MembershipID 成为值对象,因为它内部有一些业务逻辑,而不仅仅是一个字符串。但这里有一个棘手的情况。

要生成会员 ID,我需要使用随机字符串和创建用户帐户的时间戳。然后,这两个值被发送到外部 REST API,它的成功响应将包含 MembershipID。

这个值对象的创建逻辑涉及到外部 API 调用,这是属于基础设施层的东西。我首先想到的是工厂和存储库作为放置这个创建逻辑的地方,但这两个是为实体设计的,而不是值对象。那么我应该把这个值对象的创建逻辑放在哪里而不违反DDD呢?

标签: constructordomain-driven-designvalue-objects

解决方案


基本思想:应用程序(不是领域模型)负责从模型之外的世界获取数据。

要生成会员 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 访问逻辑放在域服务中

使用上面的样式,您将在应用程序服务中实现访问逻辑,而不是在域服务中。从“其他地方”获取数据不是领域建模问题,而是管道问题。

第二种方式,我们将能力传递给要调用的域模型,类似于使用域服务。你会在很多例子中看到这种风格,因为人们倾向于应用模式来匹配他们通常做的事情,而不是调整他们的风格来匹配新的模式。


推荐阅读