Java 中的命令查询职责分离:优化数据交互以实现可扩展性
大约 3 分钟
也称为
- CQRS
命令查询职责分离设计模式的意图
命令查询职责分离 (CQRS) 旨在将修改应用程序状态的操作(命令)与读取状态的操作(查询)分离。这种分离增强了复杂软件系统中的可扩展性、性能和可维护性。
命令查询职责分离模式的详细解释及现实世界示例
现实世界示例
想象一个现代图书馆,借书和还书(命令)的任务在服务台处理,而搜索书籍和阅读书籍(查询)的任务则在阅览区进行。服务台优化了交易效率和记录保存,确保书籍正确地借出和归还。同时,阅览区优化了舒适性和可访问性,使读者更容易找到并参与书籍。这种分离提高了图书馆的整体效率和用户体验,就像 CQRS 模式提高了软件系统的性能和可维护性一样。
通俗地说
CQRS 设计模式将修改数据(命令)的操作与检索数据(查询)的操作分离,以提高软件系统的性能、可扩展性和可维护性。通过实现 CQRS,您可以独立优化系统的读写操作,从而实现更有效的数据处理并提高整体系统性能。
Microsoft 的文档说
CQRS 将读写操作分离到不同的模型中,使用命令来更新数据,使用查询来读取数据。
Java 中 CQRS 模式编程示例
实现命令查询职责分离 (CQRS) 模式的一种方法是将读写操作分离到不同的服务中。
让我们先看看代码实现,然后解释它的工作原理。
public static void main(String[] args) {
// Create Authors and Books using CommandService
var commands = new CommandServiceImpl();
commands.authorCreated(AppConstants.E_EVANS, "Eric Evans", "evans@email.com");
commands.authorCreated(AppConstants.J_BLOCH, "Joshua Bloch", "jBloch@email.com");
commands.authorCreated(AppConstants.M_FOWLER, "Martin Fowler", "mFowler@email.com");
commands.bookAddedToAuthor("Domain-Driven Design", 60.08, AppConstants.E_EVANS);
commands.bookAddedToAuthor("Effective Java", 40.54, AppConstants.J_BLOCH);
commands.bookAddedToAuthor("Java Puzzlers", 39.99, AppConstants.J_BLOCH);
commands.bookAddedToAuthor("Java Concurrency in Practice", 29.40, AppConstants.J_BLOCH);
commands.bookAddedToAuthor("Patterns of Enterprise"
+ " Application Architecture", 54.01, AppConstants.M_FOWLER);
commands.bookAddedToAuthor("Domain Specific Languages", 48.89, AppConstants.M_FOWLER);
commands.authorNameUpdated(AppConstants.E_EVANS, "Eric J. Evans");
// Query the database using QueryService
var queries = new QueryServiceImpl();
var nullAuthor = queries.getAuthorByUsername("username");
var evans = queries.getAuthorByUsername(AppConstants.E_EVANS);
var blochBooksCount = queries.getAuthorBooksCount(AppConstants.J_BLOCH);
var authorsCount = queries.getAuthorsCount();
var dddBook = queries.getBook("Domain-Driven Design");
var blochBooks = queries.getAuthorBooks(AppConstants.J_BLOCH);
LOGGER.info("Author username : {}", nullAuthor);
LOGGER.info("Author evans : {}", evans);
LOGGER.info("jBloch number of books : {}", blochBooksCount);
LOGGER.info("Number of authors : {}", authorsCount);
LOGGER.info("DDD book : {}", dddBook);
LOGGER.info("jBloch books : {}", blochBooks);
HibernateUtil.getSessionFactory().close();
}
命令服务:
CommandServiceImpl
类用于写操作。它提供创建作者和书籍以及将书籍添加到作者的方法。查询服务:
QueryServiceImpl
类用于读操作。它提供获取作者和书籍详细信息的方法。
这种关注点分离允许应用程序灵活地处理数据访问和操作,这是 CQRS 模式的一个关键方面。
程序输出
17:37:56.040 [main] INFO com.iluwatar.cqrs.app.App - Author username : null
17:37:56.040 [main] INFO com.iluwatar.cqrs.app.App - Author evans : Author(name=Eric J. Evans, email=evans@email.com, username=eEvans)
17:37:56.041 [main] INFO com.iluwatar.cqrs.app.App - jBloch number of books : 3
17:37:56.041 [main] INFO com.iluwatar.cqrs.app.App - Number of authors : 3
17:37:56.041 [main] INFO com.iluwatar.cqrs.app.App - DDD book : Book(title=Domain-Driven Design, price=60.08)
17:37:56.042 [main] INFO com.iluwatar.cqrs.app.App - jBloch books : [Book(title=Effective Java, price=40.54), Book(title=Java Puzzlers, price=39.99), Book(title=Java Concurrency in Practice, price=29.4)]
何时在 Java 中使用命令查询职责分离模式
- 需要为可扩展性和可维护性而对读写操作使用不同模型的系统,例如电子商务平台和高流量网站。
- 复杂的领域模型,例如金融服务或医疗保健应用程序,其中更新对象的任务与读取对象数据的任务有很大不同。
- 读取操作性能优化至关重要的场景,系统可以从用于读写操作的不同数据模型或数据库中受益,从而提高数据检索速度和准确性。
Java 中 CQRS 模式的现实世界应用
- 分布式系统和微服务架构,其中不同的服务管理读写职责。
- 事件溯源系统,其中应用程序状态的更改以事件序列的形式存储。
- 高性能 Web 应用程序,分离读写数据库以优化负载处理。
命令查询职责分离模式的优缺点
优点
- 可扩展性:通过分离读写模型,每个模型都可以根据其特定需求进行独立扩展。
- 优化:允许优化读模型以提高查询效率,以及优化写模型以确保事务完整性。
- 可维护性:通过分离关注点来降低复杂性,从而产生更简洁、更易于维护的代码。
- 灵活性:提供根据其需求为读写端选择不同技术的灵活性。
权衡
- 复杂性:由于读写模型之间的同步(尤其是在一致性维护方面)而引入复杂性。
- 开销:对于简单的系统来说可能有点过分,因为其好处可能无法超过额外的复杂性。
- 学习曲线:需要更深入的理解和仔细的设计才能有效地实现,从而增加初始学习曲线。
相关的 Java 设计模式
- 事件溯源:通常与 CQRS 结合使用,其中应用程序状态的更改以事件序列的形式存储。
- 领域驱动设计 (DDD):CQRS 非常适合 DDD 上下文,提供了清晰的边界和关注点分离。
- 资源库:可以用来抽象数据层,在命令端和查询端之间提供更无缝的集成。