[[Presentation]] on [[DevDuck]] at [[Brainhub’s BB office]] about [[CQRS, Event Sourcing and Projections - was it worth it]] [Slides](https://docs.google.com/presentation/d/1-M4YO6_QxQ-eImfnSx0tEGUSNghyvdxhhG9tQbUGeWE/edit#slide=id.p) ![[DevDuck 14.png]] ## Slides - **CQRS, Event Sourcing & projections - was it worth it?** - Mateusz "Koniu" Konieczny, Full Stack Developer @ [[Brainhub]] - **[[CQS]] - Command Query Separation** - [[Bertrand Meyer]], 1986. Every method in the system belongs to one of two groups: - [[Command]] - changes the state of the system but returns nothing (`void`) - [[Query]] - returns something but does not change the state of the system - **CQS - quintessence** - "Asking a question should not change the answer" - **CQS - a bad method / a good separation** - code examples - **[[CQRS]] - Command Query Responsibility Segregation** - nearly 20 years after CQS, [[Greg Young]] and Udi Dahan: "Talking about CQS, we are thinking about methods. Talking about CQRS, we are thinking about objects." - **How to implement it?** - "CQRS is just a pattern, not an entire architecture" - **CQRS - example architecture** - command (async) → bus → command handler → [[Repository|repository]] → write DB; event (async) → bus → event handler; query (sync) → query handler → read DB - **Agenda** - I. basics; II. [[Event Sourcing]] (adds an event store); III. [[Projections]] (adds projections between the event store and the read DB) - **Example - a simple messenger** - 1 channel, n users, post and get messages - *post a message* - the user posts a message (command) → `PostMessageCommandHandler` saves it in the DB → message is posted (event) → an event handler emits it to the other users - *get messages* - user N+1 joins the chat and wants all messages → `GetAllMessagesQuery` → query handler → repository → DB - **CQRS - bad practices** - commands/events hell, [[Viscosity|viscosity]] (code smell), broken [[Single Responsibility Principle|SRP]], [[Rigidity|rigidity]] (code smell), lack of [[Inversion of Control|IoC]], lots of effects, additional logic, nested commands, hard to test, a command bus inside a command, transactions vs events, execution order, previous state - **CQRS is difficult to do well** - "The basic idea of CQRS is simple. But it can lead to a more complex application design, especially if they include the Event Sourcing pattern" - [[Martin Fowler]] - **CQRS - good practices** - command-event chains (1 command → 1 event → n event handlers); [[SOLID]]: SRP (every command, event and query has only one purpose), [[OCP|open-closed]] (action-reaction pairs, scalability through events), [[Dependency Inversion Principle|dependency inversion]] (IoC containers); [[TypeScript]] is nice; do NOT use nested commands; [[KISS]]; easy unit tests - **CQRS - benefits** - command logic (complex) separated from query logic (simple); division of teamwork; different data types for write and read - cleaner code; asymmetric [[Scalability|scalability]] with 2 data sources; SRP - every class is responsible for one intention; well-defined dependencies; easy unit tests; a more understandable project structure; a low entry barrier; [[Microservices|microservices]] - breaking a [[Monolith|monolith]] into smaller parts - **CQRS - disadvantages** - hard to maintain; doesn't like complex business logic; code smells; lots of ambiguous implementations - **CQRS - frontend example** - command = action, command bus = dispatcher, command handler = reducer, query/projection = selector (as in [[Redux]]) - **Example 2 - going offline** - the user writes messages, loses the connection for 2 minutes, the connection comes back, and the state is expected to be restored - *bad ideas* - reloading; requesting the missing data (figuring out which data, few data carry a timestamp, lots of new queries, updating reducers…); every new feature then needs its own offline handling - *the real problem* - offline we lose events, not data → keep an event store - **Event Sourcing - "the story of change"** - *benefits* - restoring the application state at any point in time, audit, debugging, alternative histories, compatible with CQRS - *but* - [[Eventual consistency|eventual consistency]] (a queue separates the write and read models - reads will be available soon, but when?); event schema (handling schema changes is hard, multiple client versions); event granularity (too fine-grained events carry too little information to be useful, too coarse ones hurt performance through serialisation, disk space and load on the message broker) - **Example 3 - searching** - the user wants to find messages containing an exact word across the entire history - *what could go wrong* - the message schema (plain text is easy; Markdown, emojis, mentions… in a JSON column), performance - the filtering belongs in SQL `WHERE`, not in the query handler - *approach* - search operates on plain text: convert rich messages to plain text, add a full-text search [[Index|index]] - "the read DB should reflect what the user wants". A `MessagePostedEvent` triggers a search-preparing handler that creates, updates or deletes the search projection - **Projections** - "Projection is about deriving current state from the stream of events." Benefits: a simple query (`SELECT … FROM … WHERE …`), a better read model, a simpler complex domain, performance and read optimisation, indices - **CQRS, ES & projections - summary** - lots of benefits, but you need to be aware of the consequences - **"CQRS and event sourcing are a mischievous lover, if you know their inner needs better than themselves they will be everything you ever wanted. Otherwise they will make your life a living hell."** - Hugo Rocha - **In my opinion…**, **Any questions?**, **Thank you**