使用依赖注入在 Web 环境中实现 CQRS 的命令

Implementing the Commands of CQRS in a Web environment with dependency INjection

我目前正在试验一些架构模式,其中之一是 CQRS 的实施,尤其是模式的 "Command" 部分。

基本上我有像

这样的命令
public class SavePersonCommand {

   @Inject
   private IPersonRepository repository;

   Person person;

   public SavePersonCommand(Person personToSave){
      this.person = personToSave;
   }

   public void execute(){
      ...
      repository.save(personToSave);
      ...
   }
}

为简单起见,我没有指定任何 interfaces/abstract 类 命令可能执行的内容。关键是,这是命令的标准实现,您将所有必要的信息传递给构造函数,然后您有一个无参数方法(即 execute())执行命令的业务逻辑。

问题是 IPersonRepositoryIPersonRepository 包含将实体保存到某个存储的逻辑,因此属于我的应用程序的 数据层 。顺便说一句,我的应用程序的简化结构是这样的

ApiLayer -> Core <- DAL

其中 ApiLayer 依赖于 Core,DAL 依赖于 Core。这意味着 Core 不依赖于特定的 DAL,它显然也不依赖于 Api 层。所以 类 的分布是..

ApiLayer
  PersonApi
Core
  IPersonRepository
  PersonCommand
  Person
DAL
  PersonRepository -> IPersonRepository

现在,在 Api 层中,我想要的地方通常会得到 SavePersonCommand.

的实例
@Path("/api/v1/person")
public class PersonApi {

   @POST
   public void savePerson(Person person) {
      SavePersonCommand personCommand = new SavePersonCommand(person);
      personCommand.execute();
   }

}

这里的 问题是,如何将 IPersonRepository 注入到命令中。我不想将它注入 ApiLayer 之类的

@Path("/api/v1/person")
public class PersonApi {

   @Inject
   private IPersonRespository personRepo;

   @POST
   public void savePerson(Person person) {
      // obviously modify the interface of SavePersonCommand
      SavePersonCommand personCommand = new SavePersonCommand(personRepo, person);
      ...
   }
}

..这有点丑..

我有一些想法,但想听听你是如何正常实施的。

您遇到的问题是因为您正在应用 command pattern。命令模式在创建用户界面时非常有用,但在抽象业务逻辑时就没那么有用了。

因为您的命令同时包含数据和行为,所以变得更难:

  1. 单独测试该命令的使用者。
  2. 将依赖项注入命令,因为消费者应该创建命令,但不应该知道任何依赖项注入机制。

事实上,CQRS 模式描述的命令与命令模式描述的有所不同。 CQRS 中的命令仅仅是消息;它们不包含任何行为。

所以您必须做的是将命令中的行为提取到它自己的 class:命令处理程序中。看一下 this article,它更深入地描述了这种模式。这篇文章是在 .NET 环境下写的,但我认为它也适用于 Java。

根据 Steven 的提示,解决我的问题的最简单方法如下。

核心层

这里我有以下

public class SavePersonCommandHandler implements ICommandHandler<SavePersonCommand> {

    @Inject
    IPersonRepository personRepository;

    @Override
    public void handle(SavePersonCommand command) {
        Person person = command.getPerson();
        personRepository.save(person); 
    }
}

SavePersonCommand只是一个数据对象,将数据从我的Api层传输到核心层。

public class SavePersonCommand {
    private Person person;

    public SavePersonCommand(Person person) {
       this.person = person;
    }

    public Person getPerson() {
       return this.person;
    }
}

我什至只能将此人的属性作为本机数据类型传递,而不是传递整个 Person 对象。因此,我可以删除对我可能只想在我的核心层中使用的数据类型的依赖。

Api层

api 层然后获取所需命令处理程序的实例。

@Path("/api/v1/person")
public class PersonApi {

   @Inject
   ICommandHandler<SavePersonCommand> commandHandler;

   @POST
   public void savePerson(Person person) {
      commandHandler.handle(new SavePersonCommand(person));
   }

}

注意 通常您还会在 "sends" 管道中的命令之间添加一个总线,然后被正确的 CommandHandler 拦截。不想因为我的问题而复杂化,这是我找到的答案。

这些文章很有帮助: