<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: PasanAbeysekara</title>
    <description>The latest articles on DEV Community by PasanAbeysekara (@pasanabeysekara).</description>
    <link>https://dev.to/pasanabeysekara</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F685355%2Fcef29dd4-61ca-4a31-9182-1a914115fd58.jpeg</url>
      <title>DEV Community: PasanAbeysekara</title>
      <link>https://dev.to/pasanabeysekara</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pasanabeysekara"/>
    <language>en</language>
    <item>
      <title>Your Database is a Bottleneck. It's Time to Split Your Reads and Writes with CQRS.</title>
      <dc:creator>PasanAbeysekara</dc:creator>
      <pubDate>Fri, 01 Aug 2025 07:35:10 +0000</pubDate>
      <link>https://dev.to/pasanabeysekara/your-database-is-a-bottleneck-its-time-to-split-your-reads-and-writes-with-cqrs-3jm9</link>
      <guid>https://dev.to/pasanabeysekara/your-database-is-a-bottleneck-its-time-to-split-your-reads-and-writes-with-cqrs-3jm9</guid>
      <description>&lt;p&gt;You’ve seen it before. Maybe you’re living it right now.&lt;/p&gt;

&lt;p&gt;It starts with a simple e-commerce application. You design a beautiful, normalized database schema. &lt;code&gt;Users&lt;/code&gt;, &lt;code&gt;Products&lt;/code&gt;, &lt;code&gt;Orders&lt;/code&gt;, &lt;code&gt;OrderItems&lt;/code&gt;—all linked with pristine foreign keys. It’s a work of art in Third Normal Form. For a while, everything is great.&lt;/p&gt;

&lt;p&gt;Then, the business grows.&lt;/p&gt;

&lt;p&gt;The marketing team wants a complex dashboard showing sales trends, top customers, and regional performance. The queries to generate this data are massive &lt;code&gt;JOIN&lt;/code&gt;s that lock tables and slow down the checkout process.&lt;/p&gt;

&lt;p&gt;During a flash sale, the write-load skyrockets. Thousands of new orders flood the &lt;code&gt;Orders&lt;/code&gt; table. The database groans under the pressure of transactional writes, and simultaneously, the "My Previous Orders" page for every customer starts to time out.&lt;/p&gt;

&lt;p&gt;Developers become fearful. "We can't add another index to the &lt;code&gt;Products&lt;/code&gt; table; it will slow down writes." "We can't denormalize that table for faster reads; it will break transactional consistency."&lt;/p&gt;

&lt;p&gt;Your database, the heart of your application, is suffering from an identity crisis. It's being asked to be two completely different things at once: a lightning-fast, highly-consistent transactional processor, AND a flexible, high-performance data warehouse. It's trying to serve two masters, and failing at both.&lt;/p&gt;

&lt;p&gt;There is a better way. It’s time to stop forcing one model to do two jobs. It’s time to talk about &lt;strong&gt;Command Query Responsibility Segregation (CQRS)&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Core Problem: The Single Model Schizophrenia
&lt;/h2&gt;

&lt;p&gt;The root of this pain is a fundamental conflict. The optimal model for &lt;em&gt;changing&lt;/em&gt; data is almost never the optimal model for &lt;em&gt;reading&lt;/em&gt; data.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Write Side (The "Command" Side)
&lt;/h4&gt;

&lt;p&gt;When your application needs to change something—a user updates their profile, a customer places an order, an admin adds a product—it's executing a &lt;strong&gt;Command&lt;/strong&gt;. The data model that serves these commands needs to be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Normalized:&lt;/strong&gt; To avoid data duplication and ensure integrity. You store the &lt;code&gt;userID&lt;/code&gt; in the &lt;code&gt;Orders&lt;/code&gt; table, not the user's entire name and address.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Consistent:&lt;/strong&gt; It must enforce all business rules and invariants. You can't place an order for a product that's out of stock. The total price must always equal the sum of its items.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Transactional:&lt;/strong&gt; The entire operation must succeed or fail as a single unit. An order and its corresponding line items are created together, or not at all.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the world of Online Transaction Processing (OLTP). It’s optimized for a high volume of small, fast, consistent writes.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Our write model is built for integrity. It’s a fortress of rules and constraints, which is exactly what we want when handling commands.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  The Read Side (The "Query" Side)
&lt;/h4&gt;

&lt;p&gt;When your application needs to display something—a user’s order history, a product catalog, that complex sales dashboard—it's executing a &lt;strong&gt;Query&lt;/strong&gt;. The data model that serves these queries needs to be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Denormalized:&lt;/strong&gt; To avoid expensive &lt;code&gt;JOIN&lt;/code&gt;s at read time. For the "My Orders" page, you want a single record that already contains the customer's name, the order total, and the number of items, without joining five tables.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Optimized for a specific view:&lt;/strong&gt; The dashboard needs data aggregated by week. The product search needs a flat structure with all filterable attributes. The user profile page needs a different shape entirely.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Blazingly Fast:&lt;/strong&gt; Users expect pages to load instantly. Reads should be cheap and quick.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the world of Online Analytical Processing (OLAP). It’s optimized for a high volume of complex reads.&lt;/p&gt;

&lt;p&gt;Forcing a single, normalized database to handle both of these workloads is like asking a world-class sprinter to also win a weightlifting competition. They are fundamentally different disciplines.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: Command Query Responsibility Segregation (CQRS)
&lt;/h2&gt;

&lt;p&gt;Coined by Greg Young, CQRS is a pattern that builds on a simple principle from Bertrand Meyer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Asking a question should not change the answer.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In other words, methods that return data (Queries) should be separate from methods that change data (Commands). CQRS takes this a step further: it suggests using a completely different &lt;strong&gt;model&lt;/strong&gt; for reads and writes.&lt;/p&gt;

&lt;p&gt;This doesn't have to be complicated at first. Let's look at a simple code-level implementation.&lt;/p&gt;

&lt;h4&gt;
  
  
  Before CQRS: The Swiss Army Knife Service
&lt;/h4&gt;

&lt;p&gt;A typical service might look like this. The &lt;code&gt;UserService&lt;/code&gt; uses a single &lt;code&gt;User&lt;/code&gt; entity (maybe from an ORM) for both operations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// A typical "CRUD" style service&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DbContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="c1"&gt;// A Command&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;updateUserAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newAddress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;User not found&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;newAddress&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Mutating the entity&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SaveChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// A Query&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;getUserProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserProfileDto&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// ...map the complex User entity to a simpler DTO for the UI&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;mapToUserProfileDto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  After CQRS: The Specialist Services
&lt;/h4&gt;

&lt;p&gt;With CQRS, we split this into two distinct paths.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Command Path:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Command objects are explicit representations of intent&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UpdateUserAddressCommand&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;newAddress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// A handler focused solely on executing the command&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserCommandHandler&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;writeDbContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;WriteDbContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UpdateUserAddressCommand&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;writeDbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// ... complex business logic and validation here ...&lt;/span&gt;
    &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;newAddress&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;writeDbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SaveChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Query Path:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// A service focused on building read models&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserQueryService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;readDbContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ReadDbContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="c1"&gt;// This might use a different database, or even optimized raw SQL&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;getUserProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserProfileDto&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// This DTO could map directly to a denormalized table or view&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SELECT user_id, full_name, profile_avatar_url FROM user_profile_read_models WHERE user_id = @userId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readDbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserProfileDto&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the separation of concerns. The command handler deals with business logic and transactions. The query service is dumb and fast; its only job is to fetch data that has already been prepared for it.&lt;/p&gt;

&lt;p&gt;This separation allows us to go even further and split the physical databases.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The CQRS architecture. Commands update a normalized write DB. A synchronization process updates one or more denormalized read DBs, which are queried directly.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In this architecture:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; The &lt;strong&gt;API Gateway&lt;/strong&gt; routes &lt;code&gt;POST/PUT/DELETE&lt;/code&gt; requests to the &lt;strong&gt;Command Service&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; The &lt;strong&gt;Command Service&lt;/strong&gt; executes business logic against the &lt;strong&gt;Write Database&lt;/strong&gt; (e.g., a normalized SQL Server or PostgreSQL instance).&lt;/li&gt;
&lt;li&gt; Some &lt;strong&gt;Synchronization Mechanism&lt;/strong&gt; (we'll get to this!) is responsible for updating the read models.&lt;/li&gt;
&lt;li&gt; The &lt;strong&gt;API Gateway&lt;/strong&gt; routes &lt;code&gt;GET&lt;/code&gt; requests to the &lt;strong&gt;Query Service&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; The &lt;strong&gt;Query Service&lt;/strong&gt; performs simple, fast lookups against the &lt;strong&gt;Read Database&lt;/strong&gt; (e.g., a denormalized table, a document DB like MongoDB, or a search index like Elasticsearch).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is a huge improvement. But it begs a question: what is that "Synchronization Mechanism"? And what if our source of truth, the write database, was even more powerful?&lt;/p&gt;

&lt;h2&gt;
  
  
  Leveling Up: Introducing Event Sourcing (ES)
&lt;/h2&gt;

&lt;p&gt;Let's think about a bank account. When you look at your balance, you're seeing the &lt;em&gt;current state&lt;/em&gt;. But the bank doesn't just store your balance. It stores every single transaction—every deposit and withdrawal. That list of transactions is the &lt;strong&gt;real source of truth&lt;/strong&gt;. Your balance is just a calculation, a &lt;em&gt;projection&lt;/em&gt;, based on that list.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is the core idea of Event Sourcing.&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Instead of storing the current state of an entity, you store the full sequence of immutable events that have ever happened to it.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The write model is no longer a table with &lt;code&gt;UPDATE&lt;/code&gt; and &lt;code&gt;DELETE&lt;/code&gt; statements. It's an append-only log of facts.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Instead of &lt;code&gt;UPDATE Users SET Address = '123 Main St'&lt;/code&gt;, we append an &lt;code&gt;UserAddressChanged&lt;/code&gt; event.&lt;/li&gt;
&lt;li&gt;  Instead of &lt;code&gt;INSERT INTO Orders...&lt;/code&gt;, we append an &lt;code&gt;OrderPlaced&lt;/code&gt; event.&lt;/li&gt;
&lt;li&gt;  Instead of &lt;code&gt;DELETE FROM Products...&lt;/code&gt;, we append a &lt;code&gt;ProductDiscontinued&lt;/code&gt; event.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These events are small, immutable objects representing something that happened in the past.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Example Events&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserRegistered&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserAddressChanged&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;newAddress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An entity, like an &lt;code&gt;Order&lt;/code&gt; or a &lt;code&gt;User&lt;/code&gt;, is called an &lt;strong&gt;Aggregate&lt;/strong&gt;. Its state is not stored directly. Instead, it's rebuilt on-the-fly by replaying its event stream.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserAggregate&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;changes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt; &lt;span class="c1"&gt;// A list of uncommitted events&lt;/span&gt;

  &lt;span class="c1"&gt;// Reconstitute state by replaying events&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nf"&gt;fromEvents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt; &lt;span class="nx"&gt;UserAggregate&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;UserAggregate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;changes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt; &lt;span class="c1"&gt;// Clear changes after loading&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// A command method that produces an event&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;changeAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newAddress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;newAddress&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// No change needed&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// Business logic would go here&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enqueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;UserAddressChanged&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// A private method to apply events to the state&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;UserRegistered&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;UserAddressChanged&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;newAddress&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;enqueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;changes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The database that stores these append-only logs is called an &lt;strong&gt;Event Store&lt;/strong&gt;. It's the ultimate write model. It's the absolute, undeniable source of truth for your entire system.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Perfect Synergy: CQRS + ES
&lt;/h2&gt;

&lt;p&gt;Now, let's put it all together. Event Sourcing is the perfect engine for the write side of a CQRS architecture. The "Synchronization Mechanism" is no longer a mystery; it's a direct consequence of using events.&lt;/p&gt;

&lt;p&gt;Here is the complete, modern, and incredibly powerful architecture:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The synergy of CQRS and Event Sourcing. Commands generate events. Events are published. Projectors consume events to build bespoke read models.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's walk through the flow for updating a user's address:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; A &lt;code&gt;UpdateUserAddressCommand&lt;/code&gt; arrives at the &lt;strong&gt;Command Service&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; The service loads the &lt;code&gt;UserAggregate&lt;/code&gt; by fetching all its past events from the &lt;strong&gt;Event Store&lt;/strong&gt; (e.g., &lt;code&gt;UserRegistered&lt;/code&gt;, &lt;code&gt;UserNameChanged&lt;/code&gt;, &lt;code&gt;PreviousAddressChanged&lt;/code&gt;). It replays them to get the current state.&lt;/li&gt;
&lt;li&gt; It calls the &lt;code&gt;user.changeAddress()&lt;/code&gt; method. This performs validation and produces a new &lt;code&gt;UserAddressChanged&lt;/code&gt; event.&lt;/li&gt;
&lt;li&gt; The service appends this new event to the user's stream in the &lt;strong&gt;Event Store&lt;/strong&gt;. &lt;strong&gt;This is the only write operation.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt; The Event Store, upon successfully saving the event, publishes it to a &lt;strong&gt;Message Bus&lt;/strong&gt; (like Kafka, RabbitMQ, or AWS Kinesis).&lt;/li&gt;
&lt;li&gt; Multiple independent services, called &lt;strong&gt;Projectors&lt;/strong&gt;, are listening on the message bus.

&lt;ul&gt;
&lt;li&gt;  A &lt;code&gt;UserProfileProjector&lt;/code&gt; receives the &lt;code&gt;UserAddressChanged&lt;/code&gt; event and updates the &lt;code&gt;user_profile_read_models&lt;/code&gt; table in a &lt;strong&gt;PostgreSQL database&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;  A &lt;code&gt;SearchIndexProjector&lt;/code&gt; receives the same event and updates the user's document in an &lt;strong&gt;Elasticsearch cluster&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;  An &lt;code&gt;AuditLogProjector&lt;/code&gt; receives the event and simply writes "User X changed their address at time Y" to a log file.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This architecture is beautiful. It unlocks capabilities that are nearly impossible with traditional models:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Ultimate Flexibility:&lt;/strong&gt; Need a new feature that requires a completely new view of your data? No problem. Write a new projector, deploy it, and tell it to replay all events from the beginning of time. You can generate a brand-new read model without ever touching your core system.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Powerful Auditing &amp;amp; Debugging:&lt;/strong&gt; You have a complete, immutable log of everything that ever happened. A customer claims their order was different yesterday? You can know for a fact. Just replay their &lt;code&gt;Order&lt;/code&gt; event stream up to yesterday's date.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Temporal Queries:&lt;/strong&gt; You can query the state of the system at any point in time. "Show me our inventory levels just before the Black Friday sale started."&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Extreme Scalability:&lt;/strong&gt; The write side (appending to a log) is incredibly fast. The read models can be scaled independently. If the search feature is getting heavy traffic, you just scale up the Elasticsearch cluster and its projector, with zero impact on the checkout process.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Elephant in the Room: The Trade-offs
&lt;/h2&gt;

&lt;p&gt;This power does not come for free. Adopting CQRS and Event Sourcing is a major architectural decision with significant consequences. You must be honest about the challenges.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;This is not the right pattern for a simple CRUD blog or a small departmental application. This is a pattern for complex, core business domains.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Eventual Consistency:&lt;/strong&gt; This is the big one. When a command succeeds, the read models are &lt;strong&gt;not&lt;/strong&gt; updated instantly. There is a small delay (usually milliseconds, but it's not zero) as the event travels through the message bus to the projectors. Your UI must be designed to handle this. You can't write a value and immediately read it back expecting the new value. Strategies like optimistic UI updates, polling, or using WebSockets are often required.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Massive Architectural Complexity:&lt;/strong&gt; You just went from a simple monolith with one database to a distributed system with an API Gateway, command services, an Event Store, a message bus, and multiple projector services with their own databases. This is a lot more to build, deploy, monitor, and manage.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Developer Mindset Shift:&lt;/strong&gt; Thinking in events is a paradigm shift. It requires un-learning the "state-based" mindset that has been dominant for decades. Debugging a distributed flow across multiple services is harder than stepping through a monolithic call stack.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Event Versioning:&lt;/strong&gt; Your business will evolve, and so will your events. What happens when you need to add a &lt;code&gt;middleName&lt;/code&gt; field to your &lt;code&gt;UserRegistered&lt;/code&gt; event? Old events in your store won't have this field. You need a robust strategy for versioning your event schemas and "upcasting" old events to the new format when they are read.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion: Should You Use This?
&lt;/h2&gt;

&lt;p&gt;CQRS with Event Sourcing is one of the most powerful architectural patterns available to software engineers today. It provides a path to build systems that are scalable, resilient, flexible, and perfectly aligned with complex business domains.&lt;/p&gt;

&lt;p&gt;However, it is a sharp tool for a specific set of hard problems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You should seriously consider this pattern if:&lt;/strong&gt;&lt;br&gt;
✅ You are working in a complex, core business domain.&lt;br&gt;
✅ Your application has very different and demanding requirements for reads and writes.&lt;br&gt;
✅ You need a detailed audit log or the ability to inspect the system's state at any point in time.&lt;br&gt;
✅ You anticipate needing many different views of the same data for different users or services.&lt;br&gt;
✅ You are building a system that needs to be highly scalable and evolve over many years.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You should probably avoid this pattern if:&lt;/strong&gt;&lt;br&gt;
❌ You are building a simple CRUD application.&lt;br&gt;
❌ Your team is new to distributed systems concepts.&lt;br&gt;
❌ Strict, immediate consistency for all reads is a non-negotiable business requirement.&lt;br&gt;
❌ Your project has a very short timeline and a low tolerance for operational overhead.&lt;/p&gt;

&lt;p&gt;If you recognize the problems described at the beginning of this article—the slow dashboards, the database contention, the fear of change—then CQRS and Event Sourcing might not just be a good idea; they might be the key to your application's long-term survival and success. It's a journey, but for the right application, it's one worth taking.&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>performance</category>
      <category>microservices</category>
      <category>database</category>
    </item>
    <item>
      <title>Difference Between DMM and Oscilloscope</title>
      <dc:creator>PasanAbeysekara</dc:creator>
      <pubDate>Tue, 15 Mar 2022 05:13:59 +0000</pubDate>
      <link>https://dev.to/pasanabeysekara/difference-between-dmm-and-oscilloscope-5dgk</link>
      <guid>https://dev.to/pasanabeysekara/difference-between-dmm-and-oscilloscope-5dgk</guid>
      <description>&lt;p&gt;&lt;strong&gt;DMM vs Oscilloscope&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When dealing with electricity, it is essential that you have the right tools in order to get the right information. Two tools that you can use to deal with electricity are the oscilloscope and DMM, which stands for Digital Multimeter. The main difference between the DMM and oscilloscope is what they can do. The DMM is very versatile tool the can measure voltages, currents, resistances, and some can even check if diodes and transistors work. In contrast, an oscilloscope only measures voltage but with a lot more detail.&lt;/p&gt;

&lt;p&gt;The thing that the oscilloscope can do that the DMM cannot do is to actually inspect how the voltage changes over time. This is very useful when it comes to electronics when you are inspecting signals. You can easily tell the waveform of the voltage; whether it’s a sine wave, a square wave, sawtooth wave, and the like. The DMM can only show the average voltage level, so you cannot really inspect it in detail. Another feature of the oscilloscope is the ability to plot two voltages together, thus providing the ability to compare. So if you have an input and an output, you can have them both on-screen so that you can see the changes that your circuit has done in real time.&lt;/p&gt;

&lt;p&gt;Lastly, there is the matter of cost. The price may vary from place to place, but the price of an oscilloscope would always be a lot more than the price of a DMM. The difference can reach 10 times or even more.&lt;/p&gt;

&lt;p&gt;The oscilloscope and the DMM are specialized tools that have their own uses. The DMM is more common as it is used in a lot more areas, like checking for shorts, voltages, measuring currents, and a lot more. The DMM is essentially a basic and necessary tool for any electrician or electronics enthusiast. The oscilloscope is a more advanced tool that should only be used by more experienced practioners.&lt;/p&gt;

&lt;p&gt;Summary:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A DMM measures a lot of things while an oscilloscope just measures voltage&lt;/li&gt;
&lt;li&gt;An oscilloscope is able to show waveforms while a DMM cannot&lt;/li&gt;
&lt;li&gt;An oscilloscope is used mostly for electronics while a DMM is used for a lot of things&lt;/li&gt;
&lt;li&gt;An oscilloscope is able to measure to voltages at the same time while a DMM can only show one&lt;/li&gt;
&lt;li&gt;An oscilloscope costs a lot more than a DMM&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>electrical</category>
    </item>
  </channel>
</rss>
