从 ddd 聚合查询远程休息服务

Query remote rest service from ddd aggregate

我读过 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贫血领域模型 混合在一起,因为一半的聚合状态是使用来自 setter 的类似方法设置的服务。使用 Double Dispatch 我可以通过例如方法参数中的 BitbucketService 接口。这样,聚合可以更多地保护它的不变量,因为一些数据只能通过连接到其余服务来验证(例如,如果聚合的 accessTokenbitbucketAccountrepositories 在同步),这是服务的责任。还有一件事是我的聚合中有一个 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 的文章,包括将远程资源表示转换为域类型的反腐败层的定义。

如果您将基础设施服务注入到您的聚合中(通过构造函数或通过方法调用),那么您将不会有 pure domain model anymore. This includes even services that have interfaces defined in the domain layer. It affects testability and introduces a dependency on the infrastructure. It also breaks the Single responsibility principle 并且它会强制聚合知道它并不真正需要的东西。

解决方法是之前调用服务并将结果传递给聚合的方法(即在应用层)。