简体   繁体   English

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

[英]Domain Driven Design (DDD) - Can Domain Services have remote procedure calls?

I am working on a DDD project and I have a UseCase(ApplicationService) that needs to deploy a project to Kube.netes.我正在做一个 DDD 项目,我有一个需要将项目部署到 Kube.netes 的 UseCase(ApplicationService)。 So, I have DeployProjectUseCase with inputs (projectId, databaseType) and it first needs to check the database type that the application uses in order to create the specific pods, secrets etc. It will invoke the external service of Kube.netes, but according with the type it has to use some specific methods from it.因此,我有带输入(projectId、databaseType)的DeployProjectUseCase ,它首先需要检查应用程序使用的数据库类型,以创建特定的 Pod、机密等。它将调用 Kube.netes 的外部服务,但根据它必须使用它的某些特定方法的类型。 I thought that this logic shouldn't be in the useCase and be polluted, but instead use a DomainService that will dependency inject the external service.我认为这个逻辑不应该在 useCase 中并且被污染,而是使用将依赖注入外部服务的 DomainService。 I don't know if Domain Services can have remote procedure calls, but I found this from Vaughn Vernon's book:我不知道域服务是否可以进行远程过程调用,但我从 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.

So, I thought an implementation like this:所以,我想到了这样的实现:

I have a Factory that returns the appropriate Domain Service according to the database type.我有一个根据数据库类型返回适当域服务的工厂。 The DeployProjectUseCase calls this and delegates the job to the domain service. DeployProjectUseCase调用它并将作业委托给域服务。 I have created a NetworkServicePort and an adapter that uses Kube.netes library for Nodejs, in order to be injected in the useCase and then to the Domain Service:我已经为 Nodejs 创建了一个NetworkServicePort和一个使用 Kube.netes 库的适配器,以便注入到 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>;
  ...
}

DeployProjectUseCase部署项目用例

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 Mongo网络服务

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);
  }
}

PostgreSQLNetworkService 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);
  }
}

It seems to me that the above has a domain logic that could be inside a Domain Service, but can a Domain Service have a remote procedure call?在我看来,上面的域逻辑可能位于域服务内部,但是域服务可以进行远程过程调用吗? Otherwise, this logic could be inside the Application Service, but it seems wrong to me and it would be polluted.否则,这个逻辑可能在应用程序服务内部,但对我来说似乎是错误的,它会被污染。 Does something like this make sense?这样的事情有意义吗? How would you implement it with DDD?你将如何用 DDD 实现它? Any ideas would be really helpful.任何想法都会很有帮助。

Thank you in advance!先感谢您!

From my point of view MongoNetworkService, PostgreSQLNetworkService are neither domain nor application services.从我的角度来看,MongoNetworkService、PostgreSQLNetworkService 既不是域服务也不是应用程序服务。 They are more like infrastructure services.它们更像是基础设施服务。 That is why the design is fine.这就是为什么设计很好。 The only problem I can see here is the line我在这里看到的唯一问题是这条线

project.addInfraNetworkService(infraNetworkService)

I couldn't clearly understand if "project" in this line is your domain object if so this call shouldn't be leaked to infrastructure service should be handled on layer above.我无法清楚地理解这一行中的“项目”是否是您的域 object 如果是这样,则不应将此调用泄露给基础设施服务,而应在上面的层上处理。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM