In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-19 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article mainly shows you "how to use CQRS to avoid the impact of query on model design", the content is easy to understand, clear, hope to help you solve your doubts, the following let the editor lead you to study and learn "how to use CQRS to avoid the impact of query on model design" this article.
Do not consider query when designing the model
When designing a domain model according to DDD, there are two useful principles:
The model should not be aware of the presentation technology, such as whether to use WEB or app, which should not be considered when designing the model.
Don't worry about the requirements of complex queries and reports
After such a domain model is designed, the challenge is often poor query performance. Compared with the data-centered design thinking, the data storage granularity designed by the domain object-centered design thinking is finer and less redundant, which is really not conducive to query.
Another unfriendly aspect of queries is the concept of aggregation. For example, if I want to check a list of orders, each line in the list only needs the data of the aggregate root of the order, and there is no need to query the sub-entities under each order, so the performance will be very poor.
Lazy loading provided by JPA can be used to load only the aggregate root, but it will cause more other problems, and lazy loading has been considered to be an antipattern that is not recommended.
Most business systems read more and write less. Domain models are important, but it is also important to be able to provide queries that meet performance requirements. How does DDD resolve this contradiction?
What if the model is not friendly to the query?
DDD recommends the use of CQRS (Command Query Responsibility Segregation, command separation of responsibilities) mode to solve this problem.
Https://martinfowler.com/bliki/CQRS.html
Let's take a look at the application services under IDDD_Sample 's com.saasovation.collaboration.application.forum. These services are divided into two categories:
Image.png
One is ApplicationService and the other is QueryService.
The method in ApplicationService we saw in the last article, the input parameter of the method is a Command object, and the return value is a CommandResponse object. In the method, the aggregation root is obtained through Repository, and then persisted through Repository after manipulating the aggregation root. This is the "C" in CQRS, or Command, which changes the state of the system.
In the implementation of the context of collaboration, the input parameter is not wrapped as a Command object, you can refer to the implementation of ApplicationService in the context of agilepm. In order to demonstrate various styles, IDDD_Sample has different implementations in different contexts. There are also people who turn the methods in this ApplicationService into CommandHandler, which is more readable, but essentially the same.
The "Q" in CQRS refers to Query, and as the name implies, queries do not change the state of the system.
What does separation mean?
The shallowest understanding is to put these query methods in QueryService rather than ApplicationService. But it's just a facade. Let's look at how a Query method is written:
Public ForumDiscussionsData forumDiscussionsDataOfId (String aTenantId, String aForumId) {return this.queryObject (ForumDiscussionsData.class, "select" + "forum.closed, forum.creator_email_address, forum.creator_identity," + "forum.creator_name, forum.description, forum.exclusive_owner, forum.forum_id," + "forum.moderator_email_address Forum.moderator_identity, forum.moderator_name, "+" forum.subject, forum.tenant_id, "+" disc.author_email_address as o_discussions_author_email_address, "+" disc.author_identity as o_discussions_author_identity, "+" disc.author_name as o_discussions_author_name "+" disc.closed as o_discussions_closed, "+" disc.discussion_id as o_discussions_discussion_id, "+" disc.exclusive_owner as o_discussions_exclusive_owner, "+" disc.forum_id as o_discussions_forum_id, "+" disc.subject as o_discussions_subject + "disc.tenant_id as o_discussions_tenant_id" + "from tbl_vw_forum as forum left outer join tbl_vw_discussion as disc" + "on forum.forum_id = disc.forum_id" + "where (forum.tenant_id =? And forum.forum_id =?) ", new JoinOn (" forum_id "," o_discussions_forum_id "), aTenantId, aForumId);}
From ForumQueryService
We find that neither domain objects nor Repository are used in this query service, and even the two tables of join represent the data of the two aggregation roots! In the Query method, you can directly query the database to return a query display DTO, which can be seen as the first meaning of separation, that is, domain objects and Repository can not be used in Query.
I think you don't have to use it, which means you can also use it.
The second meaning is the separation of data storage. The easiest way to deal with Command is that ApplicationService/CommandHandler accesses the master library, while QueryService accesses the slave library.
At a more complex point, ApplicationService can access MySQL, while QueryService can access NoSQL such as Elasticsearch, which requires data synchronization. Triggering data synchronization can be achieved by listening to domain events or by using a framework like canal to listen to binlog. In addition to the NoSQL designed for query itself has better query performance, at the same time, we can also optimize the data structure design specifically for the query, such as putting multiple association aggregate data together.
Image.png
In fact, this is how the context of collaboration in IDDD_Sample 's example is implemented-domain objects are stored using LevelDB and queries are made using MySQL.
I saw a team using this approach many years ago, and they also divided Service into WriteServcie and ReadService, but they didn't call it CQRS, nor did it sum up as a pattern, but did it based on experience.
Event traceability and CQRS
One pattern that often occurs with CQRS is event traceability (Event Sourcing). Event traceability is a more "avant-garde" pattern that does not store the state of an object, but instead stores all events that affect its state.
When you need to query the current state of an object, you can get it by playing back all the events. However, this playback is too expensive, so you can keep an up-to-date snapshot of the object, which needs to be combined with CQRS mode.
Image.png
The context of collaboration in the IDDD_Sample example actually uses event traceability. Let's look at its Repository implementation, which stores events rather than entities themselves:
Event tracing plus CQRS is "cool", but it is generally not recommended to use it, and the cost of implementation is too high.
Public class EventStoreForumRepository extends EventStoreProvider implements ForumRepository {@ Override public void save (Forum aForum) {EventStreamId eventId = new EventStreamId (aForum.tenant () .id (), aForum.forumId () .id (), aForum.mutatedVersion ()) This.eventStore () .appendWith (eventId, aForum.mutatingEvents ();}}
Even if you only use the CQRS pattern, it is recommended to proceed step by step, starting with simplicity and, most fundamentally, separating QueryService from ApplicationService, and not being influenced by QueryService design when designing domain models.
These are all the contents of the article "how to use CQRS to avoid the impact of queries on model design". Thank you for reading! I believe we all have a certain understanding, hope to share the content to help you, if you want to learn more knowledge, welcome to follow the industry information channel!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.