我已经了解了 Double Dispatch 模式,它可以将服务接口传递到聚合方法:https://lostechies.com/jimmybogard/2010/03/30/strengthening-your-domain-the-double-dispatch-pattern/,http://blog.jonathanoliver.com/dddd-double-dispatch/。
在我的域中,我有一个BitbucketIntegration
聚合,这是一个远程bitbucket帐户的本地副本,其中包含一些其他特定于域的数据。现在,我必须从云中同步存储库和团队等,以便能够对它们进行业务操作。在我的第一个实现中,我使用服务访问Bitbucket Cloud,然后设置聚合的存储库,团队,帐户。通过这种方式,我将 DDD 与 Anemic Domain Model 混合在一起,因为一半的聚合状态是使用来自服务的类似setter的方法设置的。使用 Double Dispatch 我可以通过例如方法参数的BitbucketService
接口。这样,聚合可以更多地保护它的不变量,因为一些数据只能通过连接到其他服务来验证(例如,如果聚合accessToken
,bitbucketAccount
和repositories
同步),这是服务的责任。另外一件事就是我的聚合中有一个accessToken
字段,这只是一个技术问题。
是否有任何建议的模式用于在ddd聚合中保留远程资源的副本?另外,如何保持技术方面的不受影响?或者第一种域名服务方法是否足够好?
现在代码看起来像:
class BitbucketIntegration extends Aggregate<UUID> {
accountId: BitbucketId
repos: List<Repository>
localData: ...
// ... and more
Single integrateWith(accessToken, queryService) {
var id = queryService.getAccountAsync(accessToken);
var repos = queryService.getReposAsync(accessToken);
return Single.zip(id, repos,
(i, r) -> new BitbucketIntegratedEvent(accessToken, i, r))
.onSubscribe(event -> apply(event))
}
Observable doSomeBusinessLocally(data) { ... return events; }
// this is triggered by a saga
Single pollForChanges(queryService) {
var dataFromRemote = queryService.synchronizeAsync(this.accessToken);
....
return event;
}
}
class CommandHandler {
queryService: BitbucketService
Completable handle(integrateCmd) {
aggregate = repo.get(integrateCmd.id);
return aggregate.integrateWith(integrateCmd.accessToken, queryService)
.flatMap(event -> repo.store(event));
}
}
作为旁注,我只查询 Bitbucket。
修改 Martin Fowler撰写了关于accessing an external system的文章,其中包括反腐败层的定义,它将远程资源表示转换为域类型。
答案 0 :(得分:2)
如果您将基础结构服务注入到Aggregate中(通过构造函数或方法调用),那么您将不再拥有pure域模型。这甚至包括在域层中定义接口的服务。它会影响可测试性并引入对基础结构的依赖性。它还打破了Single responsibility principle,它迫使Aggregate知道它不需要的东西。
解决方法是在之前调用服务,并将结果传递给聚合方法(即在应用程序层中)。