【Java设计模式】Java中的命令查询责任分离(CQRS):优化数据交互以实现可扩展性

摘要: 了解Java中的命令查询责任分离(CQRS)模式。探索如何分离命令和查询可以增强软件系统的可扩展性、性能和可维护性。

一、命令查询责任分离设计模式的别名

  • CQRS

二、命令查询责任分离设计模式的意图

命令查询责任分离(CQRS)旨在将修改应用程序状态的操作(命令)与读取状态的操作(查询)分离。这种分离在复杂的软件系统中提高了可扩展性、性能和可维护性。

三、命令查询责任分离模式的详细解释及实际示例

实际示例:
想象一个现代图书馆,借阅和归还书籍的任务(命令)在前台处理,而查找和阅读书籍的任务(查询)在阅读区进行。前台优化了交易效率和记录保存,确保书籍正确地借出和归还。同时,阅读区优化了舒适度和可访问性,使读者更容易找到和阅读书籍。这种分离提高了图书馆的整体效率和用户体验,就像CQRS模式提高了软件系统的性能和可维护性一样。

通俗解释:
CQRS设计模式将修改数据的操作(命令)与检索数据的操作(查询)分开,以提高软件系统的性能、可扩展性和可维护性。通过实现CQRS,您可以独立优化系统的读写操作,从而实现更高效的数据处理和提高整体系统性能。

微软文档说明:
CQRS将读写分离到不同的模型中,使用命令来更新数据,使用查询来读取数据。

四、Java中CQRS模式的编程示例

实现命令查询责任分离(CQRS)模式的一种方法是将读写操作分离到不同的服务中。
让我们先看代码实现,然后解释它是如何工作的。

import java.util.List;
import java.util.logging.Logger;

public class App {
    private static final Logger LOGGER = Logger.getLogger(App.class.getName());

    public static void main(String[] args) {
        // 创建作者和书籍使用命令服务
        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");
        // 使用查询服务查询数据库
        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();
    }
}

class AppConstants {
    public static final String E_EVANS = "eEvans";
    public static final String J_BLOCH = "jBloch";
    public static final String M_FOWLER = "mFowler";
}

// 命令服务实现类
class CommandServiceImpl {
    // 创建作者
    public void authorCreated(String username, String name, String email) {
        // 具体实现逻辑
    }

    // 向作者添加书籍
    public void bookAddedToAuthor(String title, double price, String authorUsername) {
        // 具体实现逻辑
    }

    // 更新作者姓名
    public void authorNameUpdated(String username, String newName) {
        // 具体实现逻辑
    }
}

// 查询服务实现类
class QueryServiceImpl {
    // 根据用户名获取作者
    public Author getAuthorByUsername(String username) {
        // 具体实现逻辑
        return null;
    }

    // 获取作者的书籍数量
    public int getAuthorBooksCount(String authorUsername) {
        // 具体实现逻辑
        return 0;
    }

    // 获取作者数量
    public int getAuthorsCount() {
        // 具体实现逻辑
        return 0;
    }

    // 获取书籍
    public Book getBook(String title) {
        // 具体实现逻辑
        return null;
    }

    // 获取作者的书籍列表
    public List<Book> getAuthorBooks(String authorUsername) {
        // 具体实现逻辑
        return null;
    }
}

class Author {
    private String name;
    private String email;
    private String username;

    // 构造函数、 getter 和 setter 方法
}

class Book {
    private String title;
    private double price;

    // 构造函数、 getter 和 setter 方法
}
  1. 命令服务CommandServiceImpl类用于写操作。它提供了创建作者和书籍以及向作者添加书籍的方法。
  2. 查询服务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应用程序,分离读写数据库以优化负载处理。

七、命令查询责任分离模式的优点和权衡

优点:

  • 可扩展性:通过分离读写模型,每个模型都可以根据其特定需求独立扩展。
  • 优化:允许针对查询效率优化读模型,针对事务完整性优化写模型。
  • 可维护性:通过分离关注点,降低了复杂性,导致更干净、更可维护的代码。
  • 灵活性:提供了根据需求为读写端选择不同技术的灵活性。

权衡:

  • 复杂性:由于读写模型之间的同步,引入了复杂性,特别是在一致性维护方面。
  • 开销:对于简单系统来说,可能是一种过度设计,其中好处不足以抵消额外的复杂性。
  • 学习曲线:需要更深入的理解和仔细的设计才能有效实施,增加了初始学习曲线。

八、源码下载

https://download.csdn.net/download/weixin_42545951/89693582

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐