Event sourcing and CQRS as a basis for application design
By Andreev Arkadiy (agandreev@edu.hse.ru)
Introduction
Event Sourcing [4] and CQRS [5] are software design and architecture patterns that are used to develop complex and distributed systems.
Combining Event Sourcing and CQRS allows creating a more scalable and flexible application architecture. Event Sourcing assumes storing an event history that provides detailed information about what happened in the system and allows restoring the state in the past. CQRS allows separating the processes of writing and reading data, which makes it possible to optimize each operation for the appropriate needs. Both patterns support distributed systems and scalability, which is why they can be especially useful for processing event-oriented systems, micro-service architecture or high-load systems.
In general, Event Sourcing and CQRS offer new approaches to application architecture design that can significantly improve the scalability, flexibility and simplicity of a system in a certain context, unlike conventional approaches to building systems [1, 5].
Event Sourcing
To understand the prospects of using the approaches described above, it is necessary to first consider them separately. Event Sourcing is a design pattern implying a way to store data, which consists in recording and saving a sequence of events representing all changes in the state of the system or system data in chronological order. Instead of saving only the current state, Event Sourcing saves all the events that occurred that led to the state of the system at the time of interest or necessity [1].
In the traditional approach to data storage, the state of objects is updated directly in the database or other storage, while the previous states are lost. In contrast, when using Event Sourcing, events are recorded in the storage, and the system can recreate the current state by applying all recorded events sequentially [4].
Each event represents a separate change in the state of the system and usually has a specific structure that includes the event ID, event type, timestamp, and data associated with that event. The sequence of events can be presented in the form of a log that can be viewed and played back in any order.
The advantages of Event Sourcing include the ability to track the history of events, ensure the accuracy of chronological analysis, the ability to restore the state of the system at a specific point in time, and the ability to audit and provide evidence of consistency for each event.
However, the implementation of Event Sourcing also has its own difficulties. In particular, the data model must be designed in such a way that all state changes can be expressed as separate events, and restoring the state can become a costly operation with a large number of events. In this case, with the permission of the business logic, the load can be reduced by storing calculated final state, which will be recalculated each time an event is added. In this case, aggregation of all events will be performed in rare cases.
In general, Event Sourcing is a powerful pattern that allows you to keep a complete history of changes in the system and provides a flexible framework for developing applications such as financial systems, logging systems, e-commerce systems and others where accuracy, auditing and system health restoration are important requirements [1, 3].
CQRS
The second important component of the approach under discussion is CQRS. This is a design pattern that separates the data writing processes (commands) from the data reading process (queries) in the system [5].
In the traditional architecture commonly used in CRUD applications, data writes and reads are performed through the same interface using a common data model. However, in complex systems with high load or different application needs, this approach can lead to problems with scalability, performance and flexibility.
CQRS solves this problem by separating writing and reading data into two separate sides of the system. Commands are responsible for updating data and changing the state of the system, and queries are responsible for retrieving data and presenting information to the user or another system.
Commands are executed on a model that is explicitly defined to change the state of the system. They contain information about the desired change and may include creating, updating, or deleting data. Commands are processed within a single data storage entity, which ensures consistency of the system state.
Queries, on the other hand, are executed on a separate model for reading data. This model is optimized to provide fast and efficient access to data, and can be configured specifically for the needs of each query. The data reading model can be redundant and denormalized to speed up read operations and provide optimized data representations [2].
This split architecture allows you to optimize each side of the system separately, manage the load and improve performance. It also makes it easier to scale each side independently depending on its specific needs, since each part can be implemented within a separate application within the same codebase.
However, the implementation of CQRS can increase the complexity of system development and support, as it now requires managing two or more different models instead of one. Also, due to differences between commands and queries, synchronization problems may arise, which may require additional mechanisms to ensure data consistency.
CQRS is widely used in various fields such as financial systems, content management systems, e-commerce systems and others where high read and write performance, separation of competencies and the ability to optimize each side of the system independently of the other are required [1, 3].
Event Sourcing and CQRS
Event Sourcing and CQRS can be used together to develop flexible and scalable applications. Here are the needs for which they can be combined and what advantages can be derived from this [3].
In CQRS commands can be recorded as events and saved using Event Sourcing. This allows you to register each change in the state of the system as an event, which can have its own data structure and meta-information. The events can then be recreated to restore the state or analyze the system.
The data reading model used in CQRS can be based on events recorded using Event Sourcing. Events can be processed and transformed into easy-to-read data representations that can be optimized for specific queries. Thus, the reading model can contain denormalized representations of data and provide quick access to information without having to read all events and apply them sequentially [1].
When using CQRS and Event Sourcing, commands and queries can be executed asynchronously, which makes it possible to optimize each operation individually. Commands can change the state of the system and record the corresponding events, and then be asynchronously processed and applied to the model to read data. This allows you to create responsive systems with fast access to data and the ability to apply important commands regardless of the reading process according to a given prioritization.
Event Sourcing and CQRS facilitate the creation of a micro-service architecture and integration with other systems. The command and query model allows each service in the system to have its own data model and business logic, which contributes to independence and extensibility. Events can also be published and transmitted to external systems for data integration and synchronization.
The common benefits of Event Sourcing and CQRS include improved scalability, flexibility, data analysis, auditing, and logging of operations. By using these patterns together, you can benefit from both approaches, creating a powerful and flexible application architecture. However, it is important to think carefully and adapt these patterns to the specific requirements and features of your application or business logic, otherwise you can greatly complicate the architecture without gaining performance [2].
Conclusion
As a conclusion, it would be useful to note the importance and potential of Event Sourcing and CQRS in application development. Here are a few key points that demonstrate their importance [1, 3]:
- Accuracy and inviolability of data: Event Sourcing allows you to save the history of changes in the state of the system, which ensures the accuracy of the data. All information about the events that have occurred is preserved and unchangeable.
- Ease of auditing and debugging: Event Sourcing makes it easy to track and analyze ongoing events and operations in the system. The entire history of operations and status changes is available for auditing and debugging.
- High flexibility and ease of system development: Event Sourcing makes it easy to make changes to the system at any time. Adding new features, modifying business logic, and updating data models are made easier by storing changes as events.
- Scalability and Distributed systems: The combination of Event Sourcing and CQRS provides high scalability and support for distributed systems. The separation of write and read operations allows you to optimize each part of the system according to its requirements and scale them independently of each other.
Overall, Event Sourcing and CQRS have huge potential in application development, especially for complex and distributed systems. They ensure data accuracy, ease of auditing and debugging, high flexibility, scalability and contribute to improving the user experience. Using them can significantly improve the architecture and functionality of applications [2].
References
- Greg Young CQRS Documents by Greg Young https://cqrs.files.wordpress.com/2010/11/cqrs_documents.pdf (accessed: 23.12.2023). CQRS.
- Command Query Responsibility Segregation (CQRS) https://developer.confluent.io/courses/event-sourcing/cqrs/ (accessed: 23.12.2023). Confluent.
- CQRS and Event Sourcing in Java https://www.baeldung.com/cqrs-event-sourcing-java (accessed: 23.12.2023). BAELDUNG.
- Pattern: Event sourcing https://microservices.io/patterns/data/event-sourcing.html (accessed: 23.12.2023). Microservices.
- Pattern: Command Query Responsibility Segregation (CQRS) https://microservices.io/patterns/data/cqrs.html (accessed: 23.12.2023). Microservices.