领域驱动设计(DDD)- 领域服务可以有远程过程调用吗?

3

我正在开发一个DDD项目,我有一个UseCase(ApplicationService),需要将项目部署到Kubernetes。因此,我有一个名为DeployProjectUseCase的输入(projectId,databaseType),它首先需要检查应用程序使用的数据库类型,以便创建特定的pods、secrets等。它将调用Kubernetes的外部服务,但根据类型,它必须使用其中一些特定方法。我认为这个逻辑不应该在useCase中出现,而是使用DomainService,它将依赖注入外部服务。我不知道Domain Services是否可以进行远程过程调用,但我从Vaughn Vernon的书中找到了这个:

There are times when a Domain Service is concerned with remote invocations on
a foreign Bounded Context (2). Yet, the focus here is different in that the Domain
Service is not itself providing a remote procedure call interface but is rather the
client of the RPC.

所以,我想实现如下:

我有一个工厂,根据数据库类型返回适当的域服务。 DeployProjectUseCase 调用此工厂并将工作委托给域服务。 我创建了一个 NetworkServicePort 和一个适配器,使用 Kubernetes 库用于 Nodejs,以便注入到 useCase 中,然后再注入到域服务中:

NetworkServicePort

export interface NetworkServicePort {
  createSecret(namespace: string, body: TCreateSecretBody): Promise<void>;
  createStatefulSet(namespace: string, body: TCreateStatefulSetBody): Promise<void>;
  createConfigMap(...): Promise<void>;
  createPersistentVolume(...): Promise<void>;
  ...
}

部署项目用例

export class DeployProjectUseCase {
 constructor(
    private projectRepo: ProjectWriteRepoPort,
    private networkService: NetworkServicePort,
  ) {}
  
  async execute(requestDTO: DeployProjectRequestDTO): Promise<DeployProjectUseCaseResponse> {
    const projectFound = await this.projectRepo.getById(projectId);
    if (!projectFound) {
      return fail(...);
    }

    const domainNetworkService = DomainNetworkServiceFactory.createDomainNetworkService(requestDTO.databaseType, this.networkService);
    await domainNetworkService.deploy(projectFound);
    
    ...

    await this.projectRepo.update(projectFound);

    return ok();
  }
}

DomainNetworkServiceFactory

export interface DomainNetworkService {
  deploy(project: ProjectEntity): Promise<void>;
}

export class DomainNetworkServiceFactory {
  static createDomainNetworkService(
    type: string,
    networkService: NetworkServicePort,
  ): DomainNetworkService {
    switch (type) {
      case 'mongo': {
        return new MongoNetworkService(networkService);
      }
      case 'postgreSQL': {
        return new PostgreSQLNetworkService(networkService);
      }
      default: {
        ...
      }
    }
  }
}

MongoNetworkService

export class MongoNetworkService implements DomainNetworkService {
  constructor(private networkService: NetworkServicePort) {}

  public async deploy(project: ProjectEntity): Promise<void> {
    // here have a mapper from Project aggregate to the DTO that service accepts
    await this.networkService.createNamespace(...);
    generatePassword(); // generate username and password from a library
    await this.networkService.createSecret(...);
    ...
    await this.networkService.createStatefulSet(...);
    ...
    const infraNetworkService = InfraNetworkServiceEntity.create({
      type: INFRA_NETWORK_SERVICE_TYPES.MONGO,
    });
    project.addInfraNetworkService(infraNetworkService);
  }
}

PostgreSQL 网络服务

export class PostgreSQLNetworkService implements DomainNetworkService {
  constructor(private networkService: NetworkServicePort) {}

  public async deploy(project: ProjectEntity): Promise<void> {
    // here have a mapper from Project aggregate to the DTO that service accepts
    await this.networkService.createNamespace(...);
    await this.networkService.createConfigMap(...);
    await this.networkService.createPersistentVolume(...);
    ...
    const infraNetworkService = InfraNetworkServiceEntity.create({
      type: INFRA_NETWORK_SERVICE_TYPES.POSTGRESQL,
    });
    project.addInfraNetworkService(infraNetworkService);
  }
}

在我看来,上述内容具有可以放在领域服务内的领域逻辑,但是领域服务能够进行远程过程调用吗?否则,这个逻辑可以放在应用服务中,但是这样做似乎不对,会污染应用服务。这种情况有意义吗?您如何使用DDD实现它?任何想法都将非常有帮助。

提前感谢您!

1个回答

0
从我的角度来看,MongoNetworkService和PostgreSQLNetworkService既不是领域服务也不是应用程序服务。它们更像基础设施服务。这就是为什么设计很好的原因。我唯一能看到的问题就是这行代码。
project.addInfraNetworkService(infraNetworkService)

我无法清楚地理解这行代码中的“project”是否为您的领域对象,如果是,则此调用不应泄漏到基础设施服务中,而应在上一层进行处理。


谢谢您的回答!是的,该项目是一个域对象。MongoNetworkService 还将生成用户名和密码,这可能是值对象,并且具有一些域逻辑在其中,这就是我在想它是否可以被视为域服务的原因。 - elli
这样的设计根据需求可能会在长期运行中引起一些麻烦。首先,您将一个域对象作为参数传递给这些异步方法,并尝试更新该对象的状态以便稍后保存它。如果有任何其他可能的客户端在您更新实体之前在其他用例中更新了相同的数据,则可能会出现数据一致性问题。其次,虽然每个人都有自己的理解,但我认为将聚合作为参数传递不是长期维护的明智选择。我宁愿采取另一种方式。 - cool
但是领域服务不是处理业务逻辑,因此将聚合、值对象等作为参数吗? - elli
正如我所说,这始终是一个有争议的问题,但在我的实现中,如果业务逻辑跨越多个聚合,则领域服务仅包含业务逻辑。在所有其他情况下,领域实体具有业务逻辑,并且如果逻辑需要,服务(基础设施)将作为参数提供给实体的方法。 - cool

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接