使用依赖注入在 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()
)执行命令的业务逻辑。
问题是 IPersonRepository
。 IPersonRepository
包含将实体保存到某个存储的逻辑,因此属于我的应用程序的 数据层 。顺便说一句,我的应用程序的简化结构是这样的
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。命令模式在创建用户界面时非常有用,但在抽象业务逻辑时就没那么有用了。
因为您的命令同时包含数据和行为,所以变得更难:
- 单独测试该命令的使用者。
- 将依赖项注入命令,因为消费者应该创建命令,但不应该知道任何依赖项注入机制。
事实上,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 拦截。不想因为我的问题而复杂化,这是我找到的答案。
这些文章很有帮助:
我目前正在试验一些架构模式,其中之一是 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()
)执行命令的业务逻辑。
问题是 IPersonRepository
。 IPersonRepository
包含将实体保存到某个存储的逻辑,因此属于我的应用程序的 数据层 。顺便说一句,我的应用程序的简化结构是这样的
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。命令模式在创建用户界面时非常有用,但在抽象业务逻辑时就没那么有用了。
因为您的命令同时包含数据和行为,所以变得更难:
- 单独测试该命令的使用者。
- 将依赖项注入命令,因为消费者应该创建命令,但不应该知道任何依赖项注入机制。
事实上,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 拦截。不想因为我的问题而复杂化,这是我找到的答案。
这些文章很有帮助: