DDD - 具有基础设施实施的领域服务
DDD - Domain Service with infrastructure implementation
我正在使用六边形架构在 DDD 中实施一个项目。
我正在尝试遵循所有避免贫血域模型的最佳实践,但我遇到了这个问题。
我有一个具有 属性 优先级的实体客户端,为了计算这个优先级,我使用了对外部 API 的 rest 调用。
我想避免在实体上使用 setPriority 方法,所以我提出了这个解决方案,但我不确定它是否真的“符合 DDD”。
这里是伪代码:
//DOMAIN
interface PriorityService {
public int exec(Client client);
}
class Client {
....
public void calculatePriority(PriorityService service) {
this.priority = service.exec(this);
}
}
//INFRASTRUCTURE
class ThirdPartyPriorityService implements PriorityService{
...
public int exec(Client client) {
int priority = this.externalApi(...);
return priority;
}
}
我发现这种集成第三方服务的方法非常简单有效,实体是唯一负责其内部状态一致性的人,但我可以轻松集成不同的系统。
有没有更好的方法(最好不使用 setter)?
Is there a better way to do it (preferably without the usage of a setter)?
也许吧。考虑:
Design is what we do to get more of what we want than we would get by just doing it -- Ruth Malan
是否有“更好的方法”将取决于您的上下文中的“我们想要什么”。
埃里克·埃文斯 (Eric Evans) 在 2003 年描述 ddd 时,他想要的一件事是将域代码与“管道”分开——我们应该能够描述业务,而我们的代码不会被数据库字符串和事务等。
public void calculatePriority(PriorityService service) {
this.priority = service.exec(this);
}
谜语:在这个设计中,你如何处理the network is unreliable这个事实?如果您的解决方案具有弹性,那么您将需要在某个地方使用逻辑来确保系统在网络不可用、远程服务器离线进行维护、凭据过时等情况下正常运行……
用不同的方式表达同样的想法:从 Web 服务器获取 HTTP 响应是 I/O,并且 I/O 会“污染”接触它的所有内容。因此ThirdPartyPriorityService
是I/O,因此Client::calculatePriority
是I/O。
同样的想法:我们的 HTTP 客户端必然存在于“命令式 shell”(Bernhardt 2012)中,通过将客户端注入到我们的域代码中,我们实际上是在创建域代码命令式的一部分 shell.
另一种方法是识别 ThirdPartyPriorityService
正在从某个远程位置检索信息_,也许这种关注真正属于应用程序,而不是域 - 您的域代码关心的信息是什么是,但不是它的来源。
因此,例如,您可以更改您的实现,以便应用程序调用优先级服务,然后将优先级传递给域模型。请参阅协议库中的 Cory Benfield's 2016 talk。
以这种方式设计您的代码并不比另一种更好或更差;这是一组不同的权衡。
if we failed to make our program align with what we then understood to be the proper way to think about our financial objects, then we were gonna continually stumble over that disagreement and that would slow us down -- Ward Cunningham, 2009
如果代码在您不查看时确实按照您认为的方式工作,那么您对更改它的估计可能会更好。那可能是有价值的“我们想要的”。
我正在使用六边形架构在 DDD 中实施一个项目。
我正在尝试遵循所有避免贫血域模型的最佳实践,但我遇到了这个问题。
我有一个具有 属性 优先级的实体客户端,为了计算这个优先级,我使用了对外部 API 的 rest 调用。
我想避免在实体上使用 setPriority 方法,所以我提出了这个解决方案,但我不确定它是否真的“符合 DDD”。
这里是伪代码:
//DOMAIN
interface PriorityService {
public int exec(Client client);
}
class Client {
....
public void calculatePriority(PriorityService service) {
this.priority = service.exec(this);
}
}
//INFRASTRUCTURE
class ThirdPartyPriorityService implements PriorityService{
...
public int exec(Client client) {
int priority = this.externalApi(...);
return priority;
}
}
我发现这种集成第三方服务的方法非常简单有效,实体是唯一负责其内部状态一致性的人,但我可以轻松集成不同的系统。
有没有更好的方法(最好不使用 setter)?
Is there a better way to do it (preferably without the usage of a setter)?
也许吧。考虑:
Design is what we do to get more of what we want than we would get by just doing it -- Ruth Malan
是否有“更好的方法”将取决于您的上下文中的“我们想要什么”。
埃里克·埃文斯 (Eric Evans) 在 2003 年描述 ddd 时,他想要的一件事是将域代码与“管道”分开——我们应该能够描述业务,而我们的代码不会被数据库字符串和事务等。
public void calculatePriority(PriorityService service) {
this.priority = service.exec(this);
}
谜语:在这个设计中,你如何处理the network is unreliable这个事实?如果您的解决方案具有弹性,那么您将需要在某个地方使用逻辑来确保系统在网络不可用、远程服务器离线进行维护、凭据过时等情况下正常运行……
用不同的方式表达同样的想法:从 Web 服务器获取 HTTP 响应是 I/O,并且 I/O 会“污染”接触它的所有内容。因此ThirdPartyPriorityService
是I/O,因此Client::calculatePriority
是I/O。
同样的想法:我们的 HTTP 客户端必然存在于“命令式 shell”(Bernhardt 2012)中,通过将客户端注入到我们的域代码中,我们实际上是在创建域代码命令式的一部分 shell.
另一种方法是识别 ThirdPartyPriorityService
正在从某个远程位置检索信息_,也许这种关注真正属于应用程序,而不是域 - 您的域代码关心的信息是什么是,但不是它的来源。
因此,例如,您可以更改您的实现,以便应用程序调用优先级服务,然后将优先级传递给域模型。请参阅协议库中的 Cory Benfield's 2016 talk。
以这种方式设计您的代码并不比另一种更好或更差;这是一组不同的权衡。
if we failed to make our program align with what we then understood to be the proper way to think about our financial objects, then we were gonna continually stumble over that disagreement and that would slow us down -- Ward Cunningham, 2009
如果代码在您不查看时确实按照您认为的方式工作,那么您对更改它的估计可能会更好。那可能是有价值的“我们想要的”。