{
    "componentChunkName": "component---src-templates-post-template-js",
    "path": "/en/projections_and_read_models_in_event_driven_architecture/",
    "result": {"data":{"post":{"id":"9b3a00b1-d305-5444-89e7-335a6b885b7f","html":"<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; height: auto\"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/505e8cec2615da883c59ff655ba4437d/c60e9/2023-01-20-cover.jpg\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 62.5%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAIDBf/EABUBAQEAAAAAAAAAAAAAAAAAAAEA/9oADAMBAAIQAxAAAAHtTpCUC//EABgQAAMBAQAAAAAAAAAAAAAAAAEQEQAh/9oACAEBAAEFAj3SMr//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAWEAADAAAAAAAAAAAAAAAAAAAAICH/2gAIAQEABj8CIv8A/8QAGRAAAgMBAAAAAAAAAAAAAAAAAREAELEh/9oACAEBAAE/IQAI7AHWqUBKv//aAAwDAQACAAMAAAAQl/8A/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAHBABAAICAwEAAAAAAAAAAAAAAQARIYExQZFR/9oACAEBAAE/EFD4+FIUKo95X7NzTyEKBV8VEpn/2Q=='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"cover\"\n        title=\"cover\"\n        src=\"/static/505e8cec2615da883c59ff655ba4437d/c60e9/2023-01-20-cover.jpg\"\n        srcset=\"/static/505e8cec2615da883c59ff655ba4437d/37402/2023-01-20-cover.jpg 200w,\n/static/505e8cec2615da883c59ff655ba4437d/4cda9/2023-01-20-cover.jpg 400w,\n/static/505e8cec2615da883c59ff655ba4437d/c60e9/2023-01-20-cover.jpg 800w\"\n        sizes=\"(max-width: 800px) 100vw, 800px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n  </a>\n    </span></p>\n<p><strong>If I had to choose the killer feature of Event Sourcing, I’d select projections.</strong> Why? I’ll explain that in detail in this article.</p>\n<p>Events are facts; they represent something that has happened in the past. Projections are a different interpretation of the set of facts. Sounds enigmatic? Let’s have a look at this picture.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; height: auto\"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/8e06b2e04d4e310637d16cf6532b64b6/a331c/2023-01-20-projection-01.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 55.99999999999999%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsTAAALEwEAmpwYAAACnElEQVQozyWOW0hTYQCAz0vb8iFxkAjiwDdFQad4SdjLejHmw2BPzbapM2jog+ib9uK6WCARSITlMiwCcXljRolUtDQ118HLGtnadHPbuf3nP+c/5+ycmevE6uN7/z4MIpkTlF9HqYXlle0d/PuPKL4XjsaPMxRMk1DK5XN5NXuq5s5USclLyh9eVCCSIMrSrIixvESxQiLN4HuRw1hybn5p/MFD3/TM7NzC1PTMo8dPFpcDq2sfAm/ezS+t+J6/CG5sQyQzADFQxDI0T9A8w2UBLyP5rMd9vbT0otHYcKm1taPD4nQ6Lxcwt7W1mUym4uIL3jv3lLyaJgDFChgNRcBnWSQznCTK+a5ud4m+pKampra29qrdPur1Wq3W6urqurq65uZmfUnJ2P3x36qaJlgKIIyi+USajieJeJIAnOTq6ikqKqqsrDQYDBaLxefzWa3WsrKylpYWo9F4XqcbvXX3TFUJClIswk6OUptbofWtUAjfS6ap7p5ejeZceXm5Xq+32Wx+v/+aw6HRaOrr65uamrRarff2mKqqJM0Xto9TVOQwlqEhFBRRyds7HTqdtqLC0NDQYLPZTCaT2Wxub7/i6nJVVVVhGPa/nCJYEggYBUQKigSDMhRHQ+np1LOBgYH+/n6PxzM0NNTY2NjX1zcycrPT3um54el1u1++mpVOVZLmCmUSCASDSIAoVgCchOO7S4uLk5OTI8PDQ4ODvb1ul8vpdDodDsfExMRKIBA7SkAhR7MCzQoYAwTqnwTDk0A4iBxufw19w/f2w5H9cORnNOZ/vfB2dW3jy1bw88an4Dq+G06RkGIQySDsIEHHEpkMxbJIZvksQDLgZRYpnKDAgjmS4U4IENoNr2/u7OAH7z8Go/ETyMskQH8Bxl+5TP7ja+4AAAAASUVORK5CYII='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"projection\"\n        title=\"projection\"\n        src=\"/static/8e06b2e04d4e310637d16cf6532b64b6/a331c/2023-01-20-projection-01.png\"\n        srcset=\"/static/8e06b2e04d4e310637d16cf6532b64b6/36ca5/2023-01-20-projection-01.png 200w,\n/static/8e06b2e04d4e310637d16cf6532b64b6/a3397/2023-01-20-projection-01.png 400w,\n/static/8e06b2e04d4e310637d16cf6532b64b6/a331c/2023-01-20-projection-01.png 800w\"\n        sizes=\"(max-width: 800px) 100vw, 800px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n  </a>\n    </span></p>\n<p>It shows the result of a boxing fight. Even if you’re not a boxing fan, you can see that the triumphing guy is Muhammad Ali. Below him lies Sonny Liston.</p>\n<p>Why am I telling you about it?</p>\n<p><strong>Because it shows pretty well what’s an event.</strong> The fight result is a fact. It cannot be retracted. It happened on 25th May 1965, so at a certain point in time. It has specific information about what has happened, like information of:</p>\n<ul>\n<li>who fought,</li>\n<li>that it only lasted 1 minute 44 seconds,</li>\n<li>the venue was Central Maine Youth Center in Lewiston, Maine,</li>\n<li>etc.</li>\n</ul>\n<p><strong>It also shows well what’s projection.</strong> The same result of the fight may be interpreted differently: Muhammad is triumphing, Sonny not so much. What’s more, to this day, boxing fans are arguing if this was a real fight or a rigged one. The punch knocked down Sonny Liston is known as <a href=\"https://en.wikipedia.org/wiki/Muhammad_Ali_vs._Sonny_Liston#The_phantom/anchor_punch\">phantom punch</a> as no one saw it reaching Sonny Liston’s face.</p>\n<p><strong>In projections, we’re taking a set of events representing various facts (about the same object, multiple), correlating them and building interpretation.</strong> Most of the time, we store the result as a materialised read model. Yet, the projection result can be anything: text file, Excel spreadsheet, PDF, in-memory data etc.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; height: auto\"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/3f3bf31c85c37fad4ca4520e6dadda98/a331c/2023-01-20-projection-02.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 55.99999999999999%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB7ElEQVQozy1SyW4UQQzt/7/wAYgDVyQgi0iUiAAJCaOIMNnFZDJbarq6qmyXXUvXNOKAUEWxLNkHb+89N5IGSYPzCXySWDiWzd9/F5fX27t7X09+HH8fnZyOLi7v1hokDQYD+hTShmNBzo2XfqnM2pDqsANBzq2h0fn4w87e6zdvP25/2trZ2z88urr5vbbeUuqcWIrEGblvWsN7B0f7h18+fzttLTtKrfXGCXCmUCgU4Azct5Y7JyTF+WQpOozgUwPcn41+vnu/dXx2Dtw7n52v1Sg9SgHpgbOl5CiBzwaDo2ggOAy1GZ+HtYaQo4TEEoETSV3oqA5C7h3nzpFIiDH1ue+MtRgqZo4bTgOz1/Pb1fRaz+87o4wXiUMpf2IeOA0oPflQSiEiCUFCxHpdbsCniopYq/lq8aBWM4ukrFXWHNzo8Rw59BqDNrBc2+lCz5ad0tZRsBgbS8lgckB6OXm8Hz893hpnlIWHp+mr/V+7F0tHMGtbpc3jyk0WZrIwSjvk5HxurM+WMiCDUdA9kVUW3Mp0yrrxzE0UoWQNASrkDFQTz4L1L3JTA/eAuJrePtyNl5OrtW67ymoiyfAsjMGoDWzKizkg48RRau4ULlTXOV9pSwOFglJ8LD5uqoeXhELVDCU/H5yQ60f+B8DKUvFV23X5AAAAAElFTkSuQmCC'); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"projection\"\n        title=\"projection\"\n        src=\"/static/3f3bf31c85c37fad4ca4520e6dadda98/a331c/2023-01-20-projection-02.png\"\n        srcset=\"/static/3f3bf31c85c37fad4ca4520e6dadda98/36ca5/2023-01-20-projection-02.png 200w,\n/static/3f3bf31c85c37fad4ca4520e6dadda98/a3397/2023-01-20-projection-02.png 400w,\n/static/3f3bf31c85c37fad4ca4520e6dadda98/a331c/2023-01-20-projection-02.png 800w\"\n        sizes=\"(max-width: 800px) 100vw, 800px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n  </a>\n    </span></p>\n<p>Events are facts and, thus, an excellent source for data exploration. Projections, together with analytics, and data drilling, can bring valuable insights. For instance, by checking products bought in the e-commerce system, we can find the gaps like abandoned carts, build a recommendation engine, etc. Read more in <a href=\"/en/never_lose_data_with_event_sourcing/\">Never Lose Data Again - Event Sourcing to the Rescue!</a>.</p>\n<p>As mentioned, a single fact can have multiple interpretations; events can be a source of numerous projections. For instance, information about the product added to the shopping cart can trigger the contents view update, change the product availability, and feed the information out for analytics.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; height: auto\"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/ea2769bcb12f3f79baf5ec98c133ffe3/a331c/2023-01-20-projection-04.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 55.99999999999999%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB9ElEQVQozy2SW2sUQRCF5///BfFBFAQfNIkwBDHEJJpFWbMJu5qd7M7OzqWnL9VVfZ8VH0R7EeqhaU6fU/VVF8YfjJskeoXB+EQuTb//zBcPp+/Ly+vbq5vZ1efZfLHqmSI3cbCA3viJXFIUCrSpY6oboWPQc1QUBq5nX+/enpXPnr94d1qenJXnHy7ul2smjcQwKiO00xSAQiF0uF9W5fnFx8ubuh0VhoHjKK2iqG3SNkkMQHHf8c2uZwKl9hycBKvQF2DSfLF69frNyVlZ75k2SaIHimAimJQtKHGwdSvWNdu2Qh29JDiJvlAmzhfLTzez6y/ffqxrdIccZYKkoE3UJnBNrRCPdb9c76tmFOiyL4acTG4C9NpGMEGbYMNk3ERusuEX07ERVqLVNg0cmo61TKBN5CZtYgYmtVcU1DFHDk1TPew3q35kQFh+r15eVRKxHcWuV9tWbjsltFMUBDihfQYmwAudZ2ACumHsmeBgFNplo24fhxE0B/vUjD83/XrHOOTHCoPEUAgMQvu8ZwraJnQHdNN/VCYC+V4oBrjvxdN23w5CUQATNUWFoYDj95DocyfgpPYiVz7zYwlwHVfVji2rdtupUVmpvTwqi1ULdTeOEslN6BJkEnnD5BL5fEN+AptWT+3darOuB7Qxy8j/o/0XFRdRoRb6SJAAAAAASUVORK5CYII='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"projection\"\n        title=\"projection\"\n        src=\"/static/ea2769bcb12f3f79baf5ec98c133ffe3/a331c/2023-01-20-projection-04.png\"\n        srcset=\"/static/ea2769bcb12f3f79baf5ec98c133ffe3/36ca5/2023-01-20-projection-04.png 200w,\n/static/ea2769bcb12f3f79baf5ec98c133ffe3/a3397/2023-01-20-projection-04.png 400w,\n/static/ea2769bcb12f3f79baf5ec98c133ffe3/a331c/2023-01-20-projection-04.png 800w\"\n        sizes=\"(max-width: 800px) 100vw, 800px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n  </a>\n    </span></p>\n<p><strong>The most popular approach to handling them is to perform the so-called <em>left-fold</em> approach.</strong> We’re taking the current state of the read model, applying the upcoming event and getting the new state as a result.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; height: auto\"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/67c4cb591797c466195864b224e261bd/a331c/2023-01-20-projection-03.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 55.99999999999999%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB7UlEQVQozy2Sy2/TQBCH/f+fuCMOnJFoGyCVihCiVC2F1m2i0BI3aWwn9u7Oa70P24EDQlsqzWE0mvnm8ZvM+tH60bAH9tZFcf345+9VPjt6d/z59PzL14vTs4urm8W2AetHhR2y7/wgrkcJGUlYl21jpGqwBQsSdorOL/ODyfTlq9cHR9PDyfT45NPt/H6rWJNvwWpyJAElZo+Vfj/9eDiZvnk7WW0akLhT/J9CtqeuNxJR4lZxYyxKNORbdAY7YJ+BjZtaX1zmv1YVSDQcDCcq2oi2B4kgQZNHiSR9CqZINOhMKk55fRd+d75n69g6EE+pLBh6BoGN1A2JxQEkJAQH4JCJG9jtCaFZz6ti1qwXraoVW+vGvt+7MLAburDfKSq3ugt78YP4kWyaMQP2IAFQmnpVPS7rcqWBaq1rrU7mTb5GsqnVbNme3Wy/L9q8oB93ar4ShT7T5BUFA9CU9w8/r6tiroyqNRbVw4vp9YerjUHYtKoBRtuTTSIVd2eb4huwyzQHTQGQUVXQlqRrDaZUba1NvjbLLaF4nb4goATuIneRxBJL2jmtLhEAymK+XOSb5e222bXYGfZkA7DX6Eyazil8tqQIeUM+W9T4WLetYfFjukTXox3Y9ezTqZLjBu56eXLk6bdSTlLU/wNU51MeMbANqAAAAABJRU5ErkJggg=='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"projection\"\n        title=\"projection\"\n        src=\"/static/67c4cb591797c466195864b224e261bd/a331c/2023-01-20-projection-03.png\"\n        srcset=\"/static/67c4cb591797c466195864b224e261bd/36ca5/2023-01-20-projection-03.png 200w,\n/static/67c4cb591797c466195864b224e261bd/a3397/2023-01-20-projection-03.png 400w,\n/static/67c4cb591797c466195864b224e261bd/a331c/2023-01-20-projection-03.png 800w\"\n        sizes=\"(max-width: 800px) 100vw, 800px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n  </a>\n    </span></p>\n<p><strong>Of course, nothing stops you from batch processing by taking all the events, merging them and storing the result.</strong> For instance, if you’re doing set-based aggregations like the total of sold products, money accrual, and average grades in school, it could be more efficient if done <em>en masse</em>.</p>\n<h2 id=\"projections-can-be-synchronous-and-asynchronous\" style=\"position:relative;\"><a href=\"#projections-can-be-synchronous-and-asynchronous\" aria-label=\"projections can be synchronous and asynchronous permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Projections can be synchronous and asynchronous</h2>\n<p>Although most tooling shows that they need eventual consistency, that’s incorrect. Their state doesn’t have to be updated with a delay. It’s a technical <em>“detail”</em> of the exact implementation. If you’re storing events and read models in the same relational database (like e.g. <a href=\"https://martendb.io/\">Marten</a> in Postgres), then you can wrap all in a transaction. Then either events are appended and projections updated, or no change is made.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; height: auto\"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/675be49499359f862c1a42a1dc4025f7/a331c/2023-01-20-projection-05.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 55.99999999999999%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB1UlEQVQozy1Sy24TQRDc//8GJC6cuBDiYKNIEQrOgyjvxDiyMSTOemempx/z3pU4IBgbqTWHnqqamupuXBwk9MDRcnShSCjD7z9Xt/cfR+MvX8+Op+fHJ+dXt7O2sy4OGj1y9LGXUFBSI75sNL0qfFW4MYySN5rOLq4/jCZv3r7bO/i8N5pMDo/uHp5aw4aiss5QIEkouVHW73863NsfH4wPp2eXVtJGs7YekI3WSnVgybq+1dyBQ8mGosYA6C3HBl2+uZ+ffrs+Ob98mC/JFaCErqj2eTW/WT5eqK4FzugSuYIuo2QrGSjAjky+kOvJFfIFJdeOK1biizbT7y8gFYAuAycrtaoEp/qyhJ5D4f9nrYrgtAFYa3x/uugwYOVE9kVCv8X35LKV1Nhtzrtr4AiSlEYWAYEYEzlvxYaYNKChgJVTMYABKDSGksZoKBiqZPSlUwAWjIW+9MPQk3DOBdkp6w1FQ7E6r7ZTYzjtaLUlicPQdsa2T76bxRg1yO1sdTdfLVZrqLBkd6Fsv9bsNKBKBEMBJG8UULdM9GsYBpT4uHiZ/1ivnlu9nbCphqPB6rSZrfHnq1LAEmtm5IuyApaJvTiPJIBsyWkg8lm2u0Xb9fo3qr+SqlX2G5zbNwAAAABJRU5ErkJggg=='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"projection\"\n        title=\"projection\"\n        src=\"/static/675be49499359f862c1a42a1dc4025f7/a331c/2023-01-20-projection-05.png\"\n        srcset=\"/static/675be49499359f862c1a42a1dc4025f7/36ca5/2023-01-20-projection-05.png 200w,\n/static/675be49499359f862c1a42a1dc4025f7/a3397/2023-01-20-projection-05.png 400w,\n/static/675be49499359f862c1a42a1dc4025f7/a331c/2023-01-20-projection-05.png 800w\"\n        sizes=\"(max-width: 800px) 100vw, 800px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n  </a>\n    </span></p>\n<p><strong>Of course, we should not be afraid of eventual consistency.</strong> How soon is now? There’s no now! Everything in the real world happens with some delay. Also, if we want to make a reliable and fault-tolerant processing pipeline doing stuff as a background process may be a go-to way. Read more in <a href=\"/en/outbox_inbox_patterns_and_delivery_guarantees_explained/\">Outbox, Inbox patterns and delivery guarantees explained</a>.</p>\n<p><strong>Projections are a concept that’s part of Event-Driven Architecture and essential building block of <a href=\"/en/cqrs_facts_and_myths_explained/\">CQRS</a>. They’re not tied to Event Sourcing.</strong> Yet, event stores can help you in making them efficient. Most of the time, they allow you to subscribe to the appended events notifications, for instance, <a href=\"/en/integrating_Marten/\">Marten’s Async Daemon</a> or <a href=\"/en/persistent_vs_catch_up_eventstoredb_subscriptions_in_action/\">EventStoreDB subscriptions</a>. They are an <a href=\"/en/push_based_outbox_pattern_with_postgres_logical_replication/\">outbox pattern</a> provided as a commodity by the database. That gives both the durability for the events and strong guarantees around processing them.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; height: auto\"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/112c60a0d43a8ba3dab0ae97d43f8f62/a331c/2023-01-20-projection-06.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 55.99999999999999%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB4UlEQVQoz32RW08bQQyF9///g0p96GulogZRIi4FiYIKlUABKRTRhMsum92Z8dgzntm5bEKTNtA+ttZ5sCx99tFxYVwijhJZoTMciEOcLy9GV4Ot4eGXr0fHp0cnp+ej8VOjjEsCrEJnXSQOQL4A8mWtaoFVA7Ugia5q4OTsYmNz+83bdxub2x83h8Pdg9HVTdViCzyTpgUL5IG64r4S7z8MtoZ72zufH6rWh4zGGRe4y+wzd9m4xD5LbRWy9Yk4oO2QnERXSO32Do42Bp/2D4/JeBcW2mZlU0jz1b8rpSw0F9qGmTST+7oW5LqsKPk4R17z/eJnF/vUL+bPy9KU4+Y6PodXOKcsNRfEkThx1yNHF9JMxxZjo0Op4rT1o4mcNmzy8uzucvfy0Pb8F+6ldoVCp9ABeaG5C+mm4p1vD/sXlaC0Wq18nM8w1hDGpbmtdYOhhgAmdj4K4EJo34IT2jXArovfS7t//nhyLRodV6tlOdPTJ5yWclLC5F7MhLl9UD8ehdLre4VEL/U6OqFdFzOHhbIZ/SL2z/8JLOe5RF8AeoVeomuBpbZkLGir0RrLSBbJajQvsposmj9DAFrbHldwVzWNJOMimK4BbsEKZIlO0Xrpi/i1UeTVi0cB9veffwHUYVx8ZZx33gAAAABJRU5ErkJggg=='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"projection\"\n        title=\"projection\"\n        src=\"/static/112c60a0d43a8ba3dab0ae97d43f8f62/a331c/2023-01-20-projection-06.png\"\n        srcset=\"/static/112c60a0d43a8ba3dab0ae97d43f8f62/36ca5/2023-01-20-projection-06.png 200w,\n/static/112c60a0d43a8ba3dab0ae97d43f8f62/a3397/2023-01-20-projection-06.png 400w,\n/static/112c60a0d43a8ba3dab0ae97d43f8f62/a331c/2023-01-20-projection-06.png 800w\"\n        sizes=\"(max-width: 800px) 100vw, 800px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n  </a>\n    </span></p>\n<h2 id=\"projections-rebuild\" style=\"position:relative;\"><a href=\"#projections-rebuild\" aria-label=\"projections rebuild permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Projections rebuild</h2>\n<p><strong>The significant benefit of the projections is that they’re predictable.</strong>  That means that the same logic will generate the same result for the same events.</p>\n<p><strong>As events are our source of truth, then we can think of read models as secondary data.</strong> If we treat events as the source of truth and base projection processing only on data we get from them, we can also rebuild our read models.</p>\n<p>We could break it into two phases:</p>\n<ul>\n<li><strong>catching up</strong>, where we’re far from the current state.</li>\n<li><strong>live</strong>, where we’re processing new, upcoming events.</li>\n</ul>\n<p><strong>How to decide if the gap is <em>“small enough”</em>?</strong> We might never be fully caught up if events are appended continuously. Because of that, we need to define some threshold, e.g. in our context, live means that we have a maximum of ten events to process from the latest registered event. Of course, this can be done case by case and differ for various projections. Read also more in <a href=\"/en/lets_talk_about_positions_in_event_stores/\">Let’s talk about positions in event stores</a>.</p>\n<p>We can use different projection handling techniques depending on the phase we’re in, e.g., batch processing when we’re catching up and left-fold when we’re live.</p>\n<p>Projections rebuilds are usually made asynchronously, while the left-fold approach can work well in sync and async ways.</p>\n<p><strong>The simplest rebuild we can do is truncate the current state of the read model and then reapply all events through projection logic.</strong> We can do that if we can afford the downtime. There will be a time when there’s no data, or we’re far from being up to date.</p>\n<p><strong>If we cannot afford downtime, we can do a <em>blue-green</em> rebuild.</strong> In that, we’re setting up a read model in the other storage (e.g. different database, schema, table, etc.). Then we’re reapplying events to this secondary read model. Once we’re caught up, we can switch queries to target the new read model and archive the old one.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; height: auto\"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/d1cc091a4e13a66c1f1f5f6f305b79d5/a331c/2023-01-20-projection-07.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 55.99999999999999%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsTAAALEwEAmpwYAAACEklEQVQozy2QW2/TQBCF/f+fkBAPCPGAkJB4QL0AQVSI0gIFqjRp3LS5OE4c33Y9s7Pj3fXaIRSEjJBGR/NypO98AduO3Q65Ifa1bbXx7f7PcBwevR6cnl+cff52/uX7cDzNS2TbSTQSKZNFLiVoF6B2yzidr7bhXZTkFbLLBV5cjg6OB0+ePjs4GhweD96dfBxd324LQN1kFc7Xy6wskF0g0L46fPvw0ePnL14u4gx0kwsqgUE3ir2q24ocmW5yF1+O50nBqxSjFHJppbIBmW6TyXE4W64zZF+Rq6gB3SA3yB50/wPZVVZfr+rhnZhENJxVYcxCuQC0Q/b9cuuJja4daKv6mquUk+RQN8CNYtf4bre/b3d7YqOoRnKBNi2ZjhTmcZgsr4t4WohMELPpvN8Z15Jp2e1ySdE6LyrNttO2Uz2jDYAskAOli3SVrBdpshKgUilTKU7CYhSjYofcTJbi0zA5v1qPFnh5K8IVC7SBVFYoJwHLZB7dXm2jGwEylRhtoweD0ZvhpkLYCFGAVuyN/+V399Hs6yb6AWQCSU4qB6hBpFAmSmYSIBFlKqtRXC0yhdrKns6S8ZPp7PTsS1YIzQxkAyTX+0RMljeL6Xgzn2RFXmJdKavYAVmJplJWYA26GYez9x/OMqGBXIUmmG5xnYqyIt2baFXdILdk/L9r/2fd9l5rb9u9//mbbIu6Z/kLPhNOV6dLAQ0AAAAASUVORK5CYII='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"projection\"\n        title=\"projection\"\n        src=\"/static/d1cc091a4e13a66c1f1f5f6f305b79d5/a331c/2023-01-20-projection-07.png\"\n        srcset=\"/static/d1cc091a4e13a66c1f1f5f6f305b79d5/36ca5/2023-01-20-projection-07.png 200w,\n/static/d1cc091a4e13a66c1f1f5f6f305b79d5/a3397/2023-01-20-projection-07.png 400w,\n/static/d1cc091a4e13a66c1f1f5f6f305b79d5/a331c/2023-01-20-projection-07.png 800w\"\n        sizes=\"(max-width: 800px) 100vw, 800px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n  </a>\n    </span></p>\n<h2 id=\"idempotency\" style=\"position:relative;\"><a href=\"#idempotency\" aria-label=\"idempotency permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Idempotency</h2>\n<p>Some event-driven tooling promises to bring you the Holy Grail: exactly-once delivery. I explained in detail in <a href=\"/en/outbox_inbox_patterns_and_delivery_guarantees_explained/\">another article</a> that this is impossible.</p>\n<p><strong>The safe assumption is that we might get the same event more than once and run projection logic multiple times for the same event.</strong> Because of that, our projection handling needs to be idempotent. It’s a fancy word to say that no matter how many times we apply the same event, we’ll get the same result as we’d applied it only once.</p>\n<p>How to handle idempotency correctly?</p>\n<p><strong>The simplest case is <a href=\"https://martinfowler.com/articles/201701-event-driven.html\">Event-Carried State Transfer</a>.</strong> It’s a fancy term for just pushing the latest state through events. Generally, it’s not the best practice, as it creates a coupling between event producer and consumer. Also, if we just send the state, we’re losing the context of the operation that caused the state change. However, it can be more than enough for cases like read model updates. Especially if our read model is just another representation of write model data.</p>\n<p><strong>Yet, it’s better to avoid having <a href=\"/en/state-obsession/\">state obsession</a> and work on the event model design.</strong> For instance, if we have read model with the bank account balance <em>Payment Registered</em> event. If we put the transaction amount to the event payload, applying it more than once will result in the wrong balance calculation. If we additionally include the account balance after payment was registered, then updating the balance would become upsert, idempotent by default.</p>\n<p><strong>Of course, it’s a tradeoff, as we should keep events granular. <a href=\"/en/events_should_be_as_small_as_possible/\">They should be as small as possible, but not smaller</a>.</strong> We should treat events as API. We should apply the same design practices as the others, so think if it should be API first or more tuned to consumer needs.</p>\n<p>If you’re still unsure of my reasoning, let’s look at that from a different angle. Which component should be responsible for doing balance calculation? In reality, it’s much more complex than just incrementing the value. We need to account for taxes, policies, previous balance etc. Do you really want to repeat this logic in read models or all the other consumers? The business logic should rather do such a calculation and propagate it further to subscribers. As always, pick your poison.</p>\n<p><strong>The next option is a more generic solution based on the event’s position in the log.</strong> We could pass the entity version in event metadata and store it in the read model during the update. If we assume that we’re getting events in the proper order, then we could compare the value during the update and ignore the change if it’s smaller than the value in the read model. I wrote about it longer in:</p>\n<ul>\n<li><a href=\"/en/simple_trick_for_idempotency_handling_in_elastic_search_readm_model/\">Dealing with Eventual Consistency and Idempotency in MongoDB projections</a></li>\n<li><a href=\"/en/dealing_with_eventual_consistency_and_idempotency_in_mongodb_projections/\">A simple trick for idempotency handling in the Elastic Search read model</a></li>\n</ul>\n<p><strong>You could also store events’ ids and ensure their uniqueness.</strong> This could be done as part of middleware around your event handler that tries to store the event id and stops processing if it was already handled. It may be part of your <a href=\"/en/outbox_inbox_patterns_and_delivery_guarantees_explained/\">inbox pattern deduplication</a> or stored in a dedicated table together with business results wrapped in the database transaction.</p>\n<p>If we decide to do that, we should consider keeping the handled events’ ids information for each projection. Why? To enable projection rebuilds and add new projections based on existing events.</p>\n<p><strong>My general recommendation is to work on the design and use the last handled position (checkpoints) to verify if the event was already handled.</strong> Other mechanisms may appear to be more sophisticated but also more fragile.</p>\n<h2 id=\"eventual-consistency\" style=\"position:relative;\"><a href=\"#eventual-consistency\" aria-label=\"eventual consistency permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Eventual Consistency</h2>\n<p><strong>I often hear the phrase: <em>“business won’t let me to have stale data”</em>.</strong> That’s not surprising, as <a href=\"/en/bring_me_problems_not_solutions/\">we’re not great at speaking with each other</a>.</p>\n<p>The first step to solving that is not to use technical jargon while talking with a business. We’re scaring them by using it and not helping to understand what we’re trying to say. Cross out eventual consistency from your dictionary before speaking with business. Ask them questions like:</p>\n<ul>\n<li><em>“What worse can happen if user won’t see this information immediately?”</em> or</li>\n<li><em>“Can we solve it differently, so we don’t need to show it at once?”</em></li>\n</ul>\n<p><strong>Show them the money and tell them how much development time and money it will cost for each solution.</strong> Don’t go too far into the implementation details. Try to build professional relationships in which we trust each other. So business knows about business, and we know the technical aspects.</p>\n<p><strong>Start also talking more with UX designers.</strong> Simple usability tricks may cut a lot of complexity from you. For instance, when adding a new product to the shopping cart, you might not redirect it to the shopping cart view. You may keep the user on the product page. It reduces the chance of not seeing the product in the shopping cart details and makes the user experience smoother. Typically users want to continue shopping and go to selected product details when they want to confirm and proceed to payment. Between those actions, our read model should become consistent.</p>\n<p>You can also use different techniques like push notifications from the server or simulate the synchronous flows in the asynchronous world by using long-polling. Read more in <a href=\"/en/long_polling_and_eventual_consistency/\">Long-polling, how to make our async API synchronous</a>.</p>\n<p><strong>Still, the best advice is to work closely with the business and designers; this will save you a lot of accidental complexity.</strong> The real world has delays, and we should embrace that also in our technical design.</p>\n<h2 id=\"scaling-and-data-isolation\" style=\"position:relative;\"><a href=\"#scaling-and-data-isolation\" aria-label=\"scaling and data isolation permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Scaling and data isolation</h2>\n<p><em><strong>Will it scale? How to scale?</strong></em> Those are mantras we tell in all cases. Most of the time, we don’t know what we need and don’t know the exact metrics, but we’re already trying to solve imaginary scenarios. Don’t get me wrong; it’s essential to consider this before going to production. Yet, those considerations before knowing where we need to go are just pointless.</p>\n<p>Performance optimisation should be made based on the precise requirements (expressed as SLOs etc.) and verified with the benchmarks. Before trying to parallelise processing, we should ascertain if we need to optimise and if our current solution needs to scale more.</p>\n<p>I wrote about those considerations in <a href=\"/en/how_to_scale_projections_in_the_event_driven_systems/\">How to scale projections in the event-driven systems?</a>.</p>\n<p><strong>In short, the foundational aspect of scaling projections is not the tech stack we use but the data partitioning we apply.</strong> To be able to parallelise, we need to isolate data. We could do it per module, customer, and region, but we can also go deeper. We can have a processor for each projection type (e.g. different for the user dashboard and shopping cart view). We can also go extreme and distribute the load on the row level. We should be good if projections are not competing for resources.</p>\n<p><strong>That also means we should not have multiple projections updating the same data.</strong> If we’re using a normalised relational database for our read models and have a shopping cart with product items as separate tables, then we should still have a single projection updating them. They’re conceptually grouped together, as you won’t have product items without a shopping cart, so keep a single writer updating it as a whole.</p>\n<p>Speaking about nested data. Check also <a href=\"/en/how_to_create_projections_of_events_for_nested_object_structures/\">How to create projections of events for nested object structures?</a></p>\n<h2 id=\"talk-is-cheap-show-me-the-code\" style=\"position:relative;\"><a href=\"#talk-is-cheap-show-me-the-code\" aria-label=\"talk is cheap show me the code permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Talk is cheap; show me the code!</h2>\n<p>Let’s say that we have to implement the following projections:</p>\n<ol>\n<li>Detailed view of the shopping cart with:</li>\n</ol>\n<ul>\n<li>the total amount of products in the basket,</li>\n<li>total number of products</li>\n<li>list of products (e.g. if someone added the same product twice, then we should have one element with the sum).</li>\n</ul>\n<ol start=\"2\">\n<li>View with short information about pending shopping carts. It’s intended to be used as a list view for administration:</li>\n</ol>\n<ul>\n<li>the total amount of products in the basket,</li>\n<li>total number of products</li>\n<li>confirmed and cancelled shopping carts should be hidden.</li>\n</ul>\n<p>Let’s say we’re using some database that can store/upsert and delete the entities with provided id.</p>\n<p>We could add to it a helper that’d get the current state and store the updated result using C#:</p>\n<div class=\"gatsby-highlight\" data-language=\"csharp\"><pre class=\"language-csharp\"><code class=\"language-csharp\"><span class=\"token keyword\">public</span> <span class=\"token keyword\">static</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">DatabaseExtensions</span>\n<span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">public</span> <span class=\"token keyword\">static</span> <span class=\"token return-type class-name\"><span class=\"token keyword\">void</span></span> <span class=\"token generic-method\"><span class=\"token function\">GetAndStore</span><span class=\"token generic class-name\"><span class=\"token punctuation\">&lt;</span>T<span class=\"token punctuation\">></span></span></span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span> <span class=\"token class-name\">Database</span> database<span class=\"token punctuation\">,</span> <span class=\"token class-name\">Guid</span> id<span class=\"token punctuation\">,</span> <span class=\"token class-name\">Func<span class=\"token punctuation\">&lt;</span>T<span class=\"token punctuation\">,</span> T<span class=\"token punctuation\">></span></span> update<span class=\"token punctuation\">)</span> <span class=\"token keyword\">where</span> <span class=\"token class-name\">T</span> <span class=\"token punctuation\">:</span> <span class=\"token type-list\"><span class=\"token keyword\">class</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">new</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span></span>\n    <span class=\"token punctuation\">{</span>\n        <span class=\"token class-name\"><span class=\"token keyword\">var</span></span> item <span class=\"token operator\">=</span> database<span class=\"token punctuation\">.</span><span class=\"token generic-method\"><span class=\"token function\">Get</span><span class=\"token generic class-name\"><span class=\"token punctuation\">&lt;</span>T<span class=\"token punctuation\">></span></span></span><span class=\"token punctuation\">(</span>id<span class=\"token punctuation\">)</span> <span class=\"token operator\">??</span> <span class=\"token keyword\">new</span> <span class=\"token constructor-invocation class-name\">T</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n        database<span class=\"token punctuation\">.</span><span class=\"token function\">Store</span><span class=\"token punctuation\">(</span>id<span class=\"token punctuation\">,</span> <span class=\"token function\">update</span><span class=\"token punctuation\">(</span>item<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>Having that, we could implement the first projection as:</p>\n<div class=\"gatsby-highlight\" data-language=\"csharp\"><pre class=\"language-csharp\"><code class=\"language-csharp\"><span class=\"token keyword\">public</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">ShoppingCartDetails</span>\n<span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">public</span> <span class=\"token return-type class-name\">Guid</span> Id <span class=\"token punctuation\">{</span> <span class=\"token keyword\">get</span><span class=\"token punctuation\">;</span> <span class=\"token keyword\">set</span><span class=\"token punctuation\">;</span> <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">public</span> <span class=\"token return-type class-name\">Guid</span> ClientId <span class=\"token punctuation\">{</span> <span class=\"token keyword\">get</span><span class=\"token punctuation\">;</span> <span class=\"token keyword\">set</span><span class=\"token punctuation\">;</span> <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">public</span> <span class=\"token return-type class-name\">ShoppingCartStatus</span> Status <span class=\"token punctuation\">{</span> <span class=\"token keyword\">get</span><span class=\"token punctuation\">;</span> <span class=\"token keyword\">set</span><span class=\"token punctuation\">;</span> <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">public</span> <span class=\"token return-type class-name\">IList<span class=\"token punctuation\">&lt;</span>PricedProductItem<span class=\"token punctuation\">></span></span> ProductItems <span class=\"token punctuation\">{</span> <span class=\"token keyword\">get</span><span class=\"token punctuation\">;</span> <span class=\"token keyword\">set</span><span class=\"token punctuation\">;</span> <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token constructor-invocation class-name\">List<span class=\"token punctuation\">&lt;</span>PricedProductItem<span class=\"token punctuation\">></span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">public</span> <span class=\"token return-type class-name\">DateTime<span class=\"token punctuation\">?</span></span> ConfirmedAt <span class=\"token punctuation\">{</span> <span class=\"token keyword\">get</span><span class=\"token punctuation\">;</span> <span class=\"token keyword\">set</span><span class=\"token punctuation\">;</span> <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">public</span> <span class=\"token return-type class-name\">DateTime<span class=\"token punctuation\">?</span></span> CanceledAt <span class=\"token punctuation\">{</span> <span class=\"token keyword\">get</span><span class=\"token punctuation\">;</span> <span class=\"token keyword\">set</span><span class=\"token punctuation\">;</span> <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">public</span> <span class=\"token return-type class-name\"><span class=\"token keyword\">decimal</span></span> TotalPrice <span class=\"token punctuation\">{</span> <span class=\"token keyword\">get</span><span class=\"token punctuation\">;</span> <span class=\"token keyword\">set</span><span class=\"token punctuation\">;</span> <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">public</span> <span class=\"token return-type class-name\"><span class=\"token keyword\">decimal</span></span> TotalItemsCount <span class=\"token punctuation\">{</span> <span class=\"token keyword\">get</span><span class=\"token punctuation\">;</span> <span class=\"token keyword\">set</span><span class=\"token punctuation\">;</span> <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">ShoppingCartDetailsProjection</span>\n<span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">private</span> <span class=\"token keyword\">readonly</span> <span class=\"token class-name\">Database</span> database<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">public</span> <span class=\"token function\">ShoppingCartDetailsProjection</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">Database</span> database<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>database <span class=\"token operator\">=</span> database<span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">public</span> <span class=\"token return-type class-name\"><span class=\"token keyword\">void</span></span> <span class=\"token function\">Handle</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">EventEnvelope<span class=\"token punctuation\">&lt;</span>ShoppingCartOpened<span class=\"token punctuation\">></span></span> @<span class=\"token keyword\">event</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n        database<span class=\"token punctuation\">.</span><span class=\"token function\">Store</span><span class=\"token punctuation\">(</span>@<span class=\"token keyword\">event</span><span class=\"token punctuation\">.</span>Data<span class=\"token punctuation\">.</span>ShoppingCartId<span class=\"token punctuation\">,</span>\n            <span class=\"token keyword\">new</span> <span class=\"token constructor-invocation class-name\">ShoppingCartDetails</span>\n            <span class=\"token punctuation\">{</span>\n                Id <span class=\"token operator\">=</span> @<span class=\"token keyword\">event</span><span class=\"token punctuation\">.</span>Data<span class=\"token punctuation\">.</span>ShoppingCartId<span class=\"token punctuation\">,</span>\n                Status <span class=\"token operator\">=</span> ShoppingCartStatus<span class=\"token punctuation\">.</span>Pending<span class=\"token punctuation\">,</span>\n                ClientId <span class=\"token operator\">=</span> @<span class=\"token keyword\">event</span><span class=\"token punctuation\">.</span>Data<span class=\"token punctuation\">.</span>ClientId<span class=\"token punctuation\">,</span>\n                ProductItems <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token constructor-invocation class-name\">List<span class=\"token punctuation\">&lt;</span>PricedProductItem<span class=\"token punctuation\">></span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n                TotalPrice <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">,</span>\n                TotalItemsCount <span class=\"token operator\">=</span> <span class=\"token number\">0</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">public</span> <span class=\"token return-type class-name\"><span class=\"token keyword\">void</span></span> <span class=\"token function\">Handle</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">EventEnvelope<span class=\"token punctuation\">&lt;</span>ProductItemAddedToShoppingCart<span class=\"token punctuation\">></span></span> @<span class=\"token keyword\">event</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n        database<span class=\"token punctuation\">.</span><span class=\"token generic-method\"><span class=\"token function\">GetAndStore</span><span class=\"token generic class-name\"><span class=\"token punctuation\">&lt;</span>ShoppingCartDetails<span class=\"token punctuation\">></span></span></span><span class=\"token punctuation\">(</span>@<span class=\"token keyword\">event</span><span class=\"token punctuation\">.</span>Data<span class=\"token punctuation\">.</span>ShoppingCartId<span class=\"token punctuation\">,</span> item <span class=\"token operator\">=></span>\n        <span class=\"token punctuation\">{</span>\n            <span class=\"token class-name\"><span class=\"token keyword\">var</span></span> productItem <span class=\"token operator\">=</span> @<span class=\"token keyword\">event</span><span class=\"token punctuation\">.</span>Data<span class=\"token punctuation\">.</span>ProductItem<span class=\"token punctuation\">;</span>\n            <span class=\"token class-name\"><span class=\"token keyword\">var</span></span> existingProductItem <span class=\"token operator\">=</span> item<span class=\"token punctuation\">.</span>ProductItems<span class=\"token punctuation\">.</span><span class=\"token function\">SingleOrDefault</span><span class=\"token punctuation\">(</span>p <span class=\"token operator\">=></span> p<span class=\"token punctuation\">.</span>ProductId <span class=\"token operator\">==</span> productItem<span class=\"token punctuation\">.</span>ProductId<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n            <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>existingProductItem <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">{</span>\n                item<span class=\"token punctuation\">.</span>ProductItems<span class=\"token punctuation\">.</span><span class=\"token function\">Add</span><span class=\"token punctuation\">(</span>productItem<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span>\n            <span class=\"token keyword\">else</span>\n            <span class=\"token punctuation\">{</span>\n                item<span class=\"token punctuation\">.</span>ProductItems<span class=\"token punctuation\">.</span><span class=\"token function\">Remove</span><span class=\"token punctuation\">(</span>existingProductItem<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                item<span class=\"token punctuation\">.</span>ProductItems<span class=\"token punctuation\">.</span><span class=\"token function\">Add</span><span class=\"token punctuation\">(</span>\n                    <span class=\"token keyword\">new</span> <span class=\"token constructor-invocation class-name\">PricedProductItem</span><span class=\"token punctuation\">(</span>\n                        existingProductItem<span class=\"token punctuation\">.</span>ProductId<span class=\"token punctuation\">,</span>\n                        existingProductItem<span class=\"token punctuation\">.</span>Quantity <span class=\"token operator\">+</span> productItem<span class=\"token punctuation\">.</span>Quantity<span class=\"token punctuation\">,</span>\n                        existingProductItem<span class=\"token punctuation\">.</span>UnitPrice\n                    <span class=\"token punctuation\">)</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span>\n\n            item<span class=\"token punctuation\">.</span>TotalPrice <span class=\"token operator\">+=</span> productItem<span class=\"token punctuation\">.</span>TotalAmount<span class=\"token punctuation\">;</span>\n            item<span class=\"token punctuation\">.</span>TotalItemsCount <span class=\"token operator\">+=</span> productItem<span class=\"token punctuation\">.</span>Quantity<span class=\"token punctuation\">;</span>\n\n            <span class=\"token keyword\">return</span> item<span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">public</span> <span class=\"token return-type class-name\"><span class=\"token keyword\">void</span></span> <span class=\"token function\">Handle</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">EventEnvelope<span class=\"token punctuation\">&lt;</span>ProductItemRemovedFromShoppingCart<span class=\"token punctuation\">></span></span> @<span class=\"token keyword\">event</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n        database<span class=\"token punctuation\">.</span><span class=\"token generic-method\"><span class=\"token function\">GetAndStore</span><span class=\"token generic class-name\"><span class=\"token punctuation\">&lt;</span>ShoppingCartDetails<span class=\"token punctuation\">></span></span></span><span class=\"token punctuation\">(</span>@<span class=\"token keyword\">event</span><span class=\"token punctuation\">.</span>Data<span class=\"token punctuation\">.</span>ShoppingCartId<span class=\"token punctuation\">,</span> item <span class=\"token operator\">=></span>\n        <span class=\"token punctuation\">{</span>\n            <span class=\"token class-name\"><span class=\"token keyword\">var</span></span> productItem <span class=\"token operator\">=</span> @<span class=\"token keyword\">event</span><span class=\"token punctuation\">.</span>Data<span class=\"token punctuation\">.</span>ProductItem<span class=\"token punctuation\">;</span>\n            <span class=\"token class-name\"><span class=\"token keyword\">var</span></span> existingProductItem <span class=\"token operator\">=</span> item<span class=\"token punctuation\">.</span>ProductItems<span class=\"token punctuation\">.</span><span class=\"token function\">SingleOrDefault</span><span class=\"token punctuation\">(</span>p <span class=\"token operator\">=></span> p<span class=\"token punctuation\">.</span>ProductId <span class=\"token operator\">==</span> productItem<span class=\"token punctuation\">.</span>ProductId<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n            <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>existingProductItem <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span> <span class=\"token operator\">||</span> existingProductItem<span class=\"token punctuation\">.</span>Quantity <span class=\"token operator\">-</span> productItem<span class=\"token punctuation\">.</span>Quantity <span class=\"token operator\">&lt;</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span>\n                <span class=\"token comment\">// You may consider throwing exception here, depending on your strategy</span>\n                <span class=\"token keyword\">return</span> item<span class=\"token punctuation\">;</span>\n\n            <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>existingProductItem<span class=\"token punctuation\">.</span>Quantity <span class=\"token operator\">-</span> productItem<span class=\"token punctuation\">.</span>Quantity <span class=\"token operator\">==</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">{</span>\n                item<span class=\"token punctuation\">.</span>ProductItems<span class=\"token punctuation\">.</span><span class=\"token function\">Remove</span><span class=\"token punctuation\">(</span>productItem<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span>\n            <span class=\"token keyword\">else</span>\n            <span class=\"token punctuation\">{</span>\n                item<span class=\"token punctuation\">.</span>ProductItems<span class=\"token punctuation\">.</span><span class=\"token function\">Remove</span><span class=\"token punctuation\">(</span>existingProductItem<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                item<span class=\"token punctuation\">.</span>ProductItems<span class=\"token punctuation\">.</span><span class=\"token function\">Add</span><span class=\"token punctuation\">(</span>\n                    <span class=\"token keyword\">new</span> <span class=\"token constructor-invocation class-name\">PricedProductItem</span><span class=\"token punctuation\">(</span>\n                        existingProductItem<span class=\"token punctuation\">.</span>ProductId<span class=\"token punctuation\">,</span>\n                        existingProductItem<span class=\"token punctuation\">.</span>Quantity <span class=\"token operator\">-</span> productItem<span class=\"token punctuation\">.</span>Quantity<span class=\"token punctuation\">,</span>\n                        existingProductItem<span class=\"token punctuation\">.</span>UnitPrice\n                    <span class=\"token punctuation\">)</span>\n                <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span>\n\n            item<span class=\"token punctuation\">.</span>TotalPrice <span class=\"token operator\">-=</span> productItem<span class=\"token punctuation\">.</span>TotalAmount<span class=\"token punctuation\">;</span>\n            item<span class=\"token punctuation\">.</span>TotalItemsCount <span class=\"token operator\">-=</span> productItem<span class=\"token punctuation\">.</span>Quantity<span class=\"token punctuation\">;</span>\n\n            <span class=\"token keyword\">return</span> item<span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">public</span> <span class=\"token return-type class-name\"><span class=\"token keyword\">void</span></span> <span class=\"token function\">Handle</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">EventEnvelope<span class=\"token punctuation\">&lt;</span>ShoppingCartConfirmed<span class=\"token punctuation\">></span></span> @<span class=\"token keyword\">event</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n        database<span class=\"token punctuation\">.</span><span class=\"token generic-method\"><span class=\"token function\">GetAndStore</span><span class=\"token generic class-name\"><span class=\"token punctuation\">&lt;</span>ShoppingCartDetails<span class=\"token punctuation\">></span></span></span><span class=\"token punctuation\">(</span>@<span class=\"token keyword\">event</span><span class=\"token punctuation\">.</span>Data<span class=\"token punctuation\">.</span>ShoppingCartId<span class=\"token punctuation\">,</span> item <span class=\"token operator\">=></span>\n        <span class=\"token punctuation\">{</span>\n            item<span class=\"token punctuation\">.</span>Status <span class=\"token operator\">=</span> ShoppingCartStatus<span class=\"token punctuation\">.</span>Confirmed<span class=\"token punctuation\">;</span>\n            item<span class=\"token punctuation\">.</span>ConfirmedAt <span class=\"token operator\">=</span> DateTime<span class=\"token punctuation\">.</span>UtcNow<span class=\"token punctuation\">;</span>\n\n            <span class=\"token keyword\">return</span> item<span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n\n    <span class=\"token keyword\">public</span> <span class=\"token return-type class-name\"><span class=\"token keyword\">void</span></span> <span class=\"token function\">Handle</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">EventEnvelope<span class=\"token punctuation\">&lt;</span>ShoppingCartCanceled<span class=\"token punctuation\">></span></span> @<span class=\"token keyword\">event</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n        database<span class=\"token punctuation\">.</span><span class=\"token generic-method\"><span class=\"token function\">GetAndStore</span><span class=\"token generic class-name\"><span class=\"token punctuation\">&lt;</span>ShoppingCartDetails<span class=\"token punctuation\">></span></span></span><span class=\"token punctuation\">(</span>@<span class=\"token keyword\">event</span><span class=\"token punctuation\">.</span>Data<span class=\"token punctuation\">.</span>ShoppingCartId<span class=\"token punctuation\">,</span> item <span class=\"token operator\">=></span>\n        <span class=\"token punctuation\">{</span>\n            item<span class=\"token punctuation\">.</span>Status <span class=\"token operator\">=</span> ShoppingCartStatus<span class=\"token punctuation\">.</span>Canceled<span class=\"token punctuation\">;</span>\n            item<span class=\"token punctuation\">.</span>CanceledAt <span class=\"token operator\">=</span> DateTime<span class=\"token punctuation\">.</span>UtcNow<span class=\"token punctuation\">;</span>\n\n            <span class=\"token keyword\">return</span> item<span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>And the second projection as:</p>\n<div class=\"gatsby-highlight\" data-language=\"csharp\"><pre class=\"language-csharp\"><code class=\"language-csharp\"><span class=\"token keyword\">public</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">ShoppingCartShortInfo</span>\n<span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">public</span> <span class=\"token return-type class-name\">Guid</span> Id <span class=\"token punctuation\">{</span> <span class=\"token keyword\">get</span><span class=\"token punctuation\">;</span> <span class=\"token keyword\">set</span><span class=\"token punctuation\">;</span> <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">public</span> <span class=\"token return-type class-name\">Guid</span> ClientId <span class=\"token punctuation\">{</span> <span class=\"token keyword\">get</span><span class=\"token punctuation\">;</span> <span class=\"token keyword\">set</span><span class=\"token punctuation\">;</span> <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">public</span> <span class=\"token return-type class-name\"><span class=\"token keyword\">decimal</span></span> TotalPrice <span class=\"token punctuation\">{</span> <span class=\"token keyword\">get</span><span class=\"token punctuation\">;</span> <span class=\"token keyword\">set</span><span class=\"token punctuation\">;</span> <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">public</span> <span class=\"token return-type class-name\"><span class=\"token keyword\">decimal</span></span> TotalItemsCount <span class=\"token punctuation\">{</span> <span class=\"token keyword\">get</span><span class=\"token punctuation\">;</span> <span class=\"token keyword\">set</span><span class=\"token punctuation\">;</span> <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">ShoppingCartShortInfoProjection</span>\n<span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">private</span> <span class=\"token keyword\">readonly</span> <span class=\"token class-name\">Database</span> database<span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">public</span> <span class=\"token function\">ShoppingCartShortInfoProjection</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">Database</span> database<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>database <span class=\"token operator\">=</span> database<span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">public</span> <span class=\"token return-type class-name\"><span class=\"token keyword\">void</span></span> <span class=\"token function\">Handle</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">EventEnvelope<span class=\"token punctuation\">&lt;</span>ShoppingCartOpened<span class=\"token punctuation\">></span></span> @<span class=\"token keyword\">event</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n        database<span class=\"token punctuation\">.</span><span class=\"token function\">Store</span><span class=\"token punctuation\">(</span>@<span class=\"token keyword\">event</span><span class=\"token punctuation\">.</span>Data<span class=\"token punctuation\">.</span>ShoppingCartId<span class=\"token punctuation\">,</span>\n            <span class=\"token keyword\">new</span> <span class=\"token constructor-invocation class-name\">ShoppingCartShortInfo</span>\n            <span class=\"token punctuation\">{</span>\n                Id <span class=\"token operator\">=</span> @<span class=\"token keyword\">event</span><span class=\"token punctuation\">.</span>Data<span class=\"token punctuation\">.</span>ShoppingCartId<span class=\"token punctuation\">,</span>\n                ClientId <span class=\"token operator\">=</span> @<span class=\"token keyword\">event</span><span class=\"token punctuation\">.</span>Data<span class=\"token punctuation\">.</span>ClientId<span class=\"token punctuation\">,</span>\n                TotalPrice <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">,</span>\n                TotalItemsCount <span class=\"token operator\">=</span> <span class=\"token number\">0</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">public</span> <span class=\"token return-type class-name\"><span class=\"token keyword\">void</span></span> <span class=\"token function\">Handle</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">EventEnvelope<span class=\"token punctuation\">&lt;</span>ProductItemAddedToShoppingCart<span class=\"token punctuation\">></span></span> @<span class=\"token keyword\">event</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n        database<span class=\"token punctuation\">.</span><span class=\"token generic-method\"><span class=\"token function\">GetAndStore</span><span class=\"token generic class-name\"><span class=\"token punctuation\">&lt;</span>ShoppingCartShortInfo<span class=\"token punctuation\">></span></span></span><span class=\"token punctuation\">(</span>@<span class=\"token keyword\">event</span><span class=\"token punctuation\">.</span>Data<span class=\"token punctuation\">.</span>ShoppingCartId<span class=\"token punctuation\">,</span> item <span class=\"token operator\">=></span>\n        <span class=\"token punctuation\">{</span>\n            <span class=\"token class-name\"><span class=\"token keyword\">var</span></span> productItem <span class=\"token operator\">=</span> @<span class=\"token keyword\">event</span><span class=\"token punctuation\">.</span>Data<span class=\"token punctuation\">.</span>ProductItem<span class=\"token punctuation\">;</span>\n\n            item<span class=\"token punctuation\">.</span>TotalPrice <span class=\"token operator\">+=</span> productItem<span class=\"token punctuation\">.</span>TotalAmount<span class=\"token punctuation\">;</span>\n            item<span class=\"token punctuation\">.</span>TotalItemsCount <span class=\"token operator\">+=</span> productItem<span class=\"token punctuation\">.</span>Quantity<span class=\"token punctuation\">;</span>\n\n            <span class=\"token keyword\">return</span> item<span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">public</span> <span class=\"token return-type class-name\"><span class=\"token keyword\">void</span></span> <span class=\"token function\">Handle</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">EventEnvelope<span class=\"token punctuation\">&lt;</span>ProductItemRemovedFromShoppingCart<span class=\"token punctuation\">></span></span> @<span class=\"token keyword\">event</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n        database<span class=\"token punctuation\">.</span><span class=\"token generic-method\"><span class=\"token function\">GetAndStore</span><span class=\"token generic class-name\"><span class=\"token punctuation\">&lt;</span>ShoppingCartShortInfo<span class=\"token punctuation\">></span></span></span><span class=\"token punctuation\">(</span>@<span class=\"token keyword\">event</span><span class=\"token punctuation\">.</span>Data<span class=\"token punctuation\">.</span>ShoppingCartId<span class=\"token punctuation\">,</span> item <span class=\"token operator\">=></span>\n        <span class=\"token punctuation\">{</span>\n            <span class=\"token class-name\"><span class=\"token keyword\">var</span></span> productItem <span class=\"token operator\">=</span> @<span class=\"token keyword\">event</span><span class=\"token punctuation\">.</span>Data<span class=\"token punctuation\">.</span>ProductItem<span class=\"token punctuation\">;</span>\n\n            item<span class=\"token punctuation\">.</span>TotalPrice <span class=\"token operator\">-=</span> productItem<span class=\"token punctuation\">.</span>TotalAmount<span class=\"token punctuation\">;</span>\n            item<span class=\"token punctuation\">.</span>TotalItemsCount <span class=\"token operator\">-=</span> productItem<span class=\"token punctuation\">.</span>Quantity<span class=\"token punctuation\">;</span>\n\n            <span class=\"token keyword\">return</span> item<span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">public</span> <span class=\"token return-type class-name\"><span class=\"token keyword\">void</span></span> <span class=\"token function\">Handle</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">EventEnvelope<span class=\"token punctuation\">&lt;</span>ShoppingCartConfirmed<span class=\"token punctuation\">></span></span> @<span class=\"token keyword\">event</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n        database<span class=\"token punctuation\">.</span><span class=\"token generic-method\"><span class=\"token function\">Delete</span><span class=\"token generic class-name\"><span class=\"token punctuation\">&lt;</span>ShoppingCartShortInfo<span class=\"token punctuation\">></span></span></span><span class=\"token punctuation\">(</span>@<span class=\"token keyword\">event</span><span class=\"token punctuation\">.</span>Data<span class=\"token punctuation\">.</span>ShoppingCartId<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n\n    <span class=\"token keyword\">public</span> <span class=\"token return-type class-name\"><span class=\"token keyword\">void</span></span> <span class=\"token function\">Handle</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">EventEnvelope<span class=\"token punctuation\">&lt;</span>ShoppingCartCanceled<span class=\"token punctuation\">></span></span> @<span class=\"token keyword\">event</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n        database<span class=\"token punctuation\">.</span><span class=\"token generic-method\"><span class=\"token function\">Delete</span><span class=\"token generic class-name\"><span class=\"token punctuation\">&lt;</span>ShoppingCartShortInfo<span class=\"token punctuation\">></span></span></span><span class=\"token punctuation\">(</span>@<span class=\"token keyword\">event</span><span class=\"token punctuation\">.</span>Data<span class=\"token punctuation\">.</span>ShoppingCartId<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p><strong>As you see, projections use using simple handler that takes the upcoming events, loads the current state, runs the projection logic and stores the result.</strong> So, doing left-fold.</p>\n<p>That’s also how <a href=\"https://martendb.io/events/projections/\">Marten projections</a> are working. Yet, they’re doing much more internally. So batching, parallelising etc.</p>\n<p>Of course, as explained earlier, you don’t need to do left fold; you could even just run an SQL statement, e.g.:</p>\n<div class=\"gatsby-highlight\" data-language=\"csharp\"><pre class=\"language-csharp\"><code class=\"language-csharp\"><span class=\"token keyword\">public</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">UserDashboardProjection</span> <span class=\"token punctuation\">:</span> <span class=\"token type-list\"><span class=\"token class-name\">Projection</span></span>\n<span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">private</span> <span class=\"token keyword\">readonly</span> <span class=\"token class-name\">NpgsqlConnection</span> databaseConnection<span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">public</span> <span class=\"token function\">UserDashboardProjection</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">NpgsqlConnection</span> databaseConnection<span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>databaseConnection <span class=\"token operator\">=</span> databaseConnection<span class=\"token punctuation\">;</span>\n\n        <span class=\"token generic-method\"><span class=\"token function\">Projects</span><span class=\"token generic class-name\"><span class=\"token punctuation\">&lt;</span>UserCreated<span class=\"token punctuation\">></span></span></span><span class=\"token punctuation\">(</span>Apply<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token generic-method\"><span class=\"token function\">Projects</span><span class=\"token generic class-name\"><span class=\"token punctuation\">&lt;</span>UserNameUpdated<span class=\"token punctuation\">></span></span></span><span class=\"token punctuation\">(</span>Apply<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token generic-method\"><span class=\"token function\">Projects</span><span class=\"token generic class-name\"><span class=\"token punctuation\">&lt;</span>OrderCreated<span class=\"token punctuation\">></span></span></span><span class=\"token punctuation\">(</span>Apply<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token return-type class-name\"><span class=\"token keyword\">void</span></span> <span class=\"token function\">Apply</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">UserCreated</span> @<span class=\"token keyword\">event</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">{</span>\n        databaseConnection<span class=\"token punctuation\">.</span><span class=\"token function\">Execute</span><span class=\"token punctuation\">(</span>\n            <span class=\"token string\">@\"INSERT INTO UserDashboards (Id, UserName, OrdersCount, TotalAmount)\n                VALUES (@UserId, @UserName, 0, 0)\"</span><span class=\"token punctuation\">,</span>\n            @<span class=\"token keyword\">event</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token return-type class-name\"><span class=\"token keyword\">void</span></span> <span class=\"token function\">Apply</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">UserNameUpdated</span> @<span class=\"token keyword\">event</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">{</span>\n        databaseConnection<span class=\"token punctuation\">.</span><span class=\"token function\">Execute</span><span class=\"token punctuation\">(</span>\n            <span class=\"token string\">@\"UPDATE UserDashboards\n                SET UserName = @UserName\n                WHERE Id = @UserId\"</span><span class=\"token punctuation\">,</span>\n            @<span class=\"token keyword\">event</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token return-type class-name\"><span class=\"token keyword\">void</span></span> <span class=\"token function\">Apply</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">OrderCreated</span> @<span class=\"token keyword\">event</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">{</span>\n        databaseConnection<span class=\"token punctuation\">.</span><span class=\"token function\">Execute</span><span class=\"token punctuation\">(</span>\n            <span class=\"token string\">@\"UPDATE UserDashboards\n                SET OrdersCount = OrdersCount + 1,\n                    TotalAmount = TotalAmount + @Amount\n                WHERE Id = @UserId\"</span><span class=\"token punctuation\">,</span>\n            @<span class=\"token keyword\">event</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>Or do batch processing.</p>\n<p>Projections should be as close as possible to end storage to be efficient. Create abstractions when needed, but beware to avoid ending up with the lowest common denominator.</p>\n<h2 id=\"summing-up\" style=\"position:relative;\"><a href=\"#summing-up\" aria-label=\"summing up permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Summing up</h2>\n<p><strong>Projections are powerful mechanism.</strong> In a nutshell, they’re <em>just</em> transformations of information we got from events into other data. We can look at the past and analyse the data, finding even new business models. Thanks to that, we can get business insights and view data from different perspectives.</p>\n<p>To implement projections efficiently and benefit fully from their superpowers, we need to take a lot into consideration.</p>\n<p>I hope this article is a decent starting point for you to know how to deal with projections and what to watch for. I showed foundational building blocks from a big-picture view, challenges and potential solutions.</p>\n<p>There’s plenty more to discuss at each stop on this city bus tour. Feel free to post your questions and concerns in the comments!</p>\n<p><strong><a href=\"/en/training/\">Consider my training</a> as a way to delve more into the specific bits relevant to your project needs.</strong> A real workshop is the best way to learn, discuss and get hands-on experience.</p>\n<p>Cheers!</p>\n<p>Oskar</p>\n<p>p.s. <strong>Ukraine is still under brutal Russian invasion. A lot of Ukrainian people are hurt, without shelter and need help.</strong> You can help in various ways, for instance, directly helping refugees, spreading awareness, putting pressure on your local government or companies. You can also support Ukraine by donating e.g. to <a href=\"https://www.icrc.org/en/donate/ukraine\">Red Cross</a>, <a href=\"https://savelife.in.ua/en/donate/\">Ukraine humanitarian organisation</a> or <a href=\"https://www.gofundme.com/f/help-to-save-the-lives-of-civilians-in-a-war-zone\">donate Ambulances for Ukraine</a>.</p>","excerpt":"If I had to choose the killer feature of Event Sourcing, I’d select projections. Why? I’ll explain that in detail in this article. Events…","fields":{"slug":"/projections_and_read_models_in_event_driven_architecture/","prefix":"2023-01-20","langKey":"en"},"frontmatter":{"title":"Guide to Projections and Read Models in Event-Driven Architecture","author":"oskar dudycz","category":"Event Sourcing","disqusId":null,"useDefaultLangCanonical":null,"cover":{"childImageSharp":{"resize":{"src":"/static/505e8cec2615da883c59ff655ba4437d/4fe8c/2023-01-20-cover.jpg"}}}}},"authornote":{"id":"f1fb8a26-256d-5b24-9b06-ad19a0c2961b","html":"<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; height: auto\"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/f748655e118b2b9d5ce6b7dd6f9f4f85/d2429/2021-10-13-cover.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 55.99999999999999%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAA6cAAAOnAEHlFPdAAAChUlEQVQozyXOTU/acBwA4J+FFmgp/5RCX2AlFVgUgxTCYZNA6KZMooPSiAoaZ8bLjTmEbALRePAygyaLBEl0lyWePGji1YsnT8YDBxMTE+9+iiXu+QQPPDw8nJ2dtVqtZrNZr9cbjUYul8tkMvl8vt1un5yc3Nzc3N7eDofDu7u74+Pj+/v7x8fH6+vr8/NzGA6HOzs73W53f3+/0+mUSqVisbi3t3d0dNTv9w8PDy8uLnq93sHBQblcrtfrp6enl5eXrVarUqnA8/OzruvLy8uDwUDTtJmZmWw22+12Nzc3q9VqKpUqFovT09OFQsHr9SaTyZWVlVgspmlaoVCAl5cXRVE4jotGowghiqJIkpybm/N4PAsLCwghRVF0Xff5fHa7HQDi8Xg6nQaASCQCT09PwWAQXrEsa7VaZVlOp9NjY2Nut1uWZYIgWJYVRZEkSYZhLBaL0WgkCCIUCkGv1xMEAcMwo9EIABRFEQQhSVI0GhUEwWAw0DQ9OjqK4zhFUQzD2O12HMc5jhMEAa6urmRZRgglEgmHwzEyMuJyuXZ3d2u12tTUFAAQBOF0Onmep2mae2V6heM4bG1tIYR4nk8kEqIoAkAgEOh0OvF4XJIkADCZTDzPu1wuk8lks9kQQhiGmc1mkiTB65U5zun3+xFCPp+P5znaRquqGg6HcdyIYRiO4+VyeWlp6f+CpmmDwWAxm0VRAFX9kM3mVlfXJidDPv/bN24pHI4kkyrDMCRJjo8HJMnz6dNsq9W2WmmzhWRZB2W1BSaCmqbDxrfK9naj/r2az2fWvxTfvY98La2pakxRJuY/zy4u5jLZtPox/uPnxvp6YTalLurz7WZt8PvX3z/9f7APp/oT02NdAAAAAElFTkSuQmCC'); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"cover\"\n        title=\"cover\"\n        src=\"/static/f748655e118b2b9d5ce6b7dd6f9f4f85/a331c/2021-10-13-cover.png\"\n        srcset=\"/static/f748655e118b2b9d5ce6b7dd6f9f4f85/36ca5/2021-10-13-cover.png 200w,\n/static/f748655e118b2b9d5ce6b7dd6f9f4f85/a3397/2021-10-13-cover.png 400w,\n/static/f748655e118b2b9d5ce6b7dd6f9f4f85/a331c/2021-10-13-cover.png 800w,\n/static/f748655e118b2b9d5ce6b7dd6f9f4f85/d2429/2021-10-13-cover.png 805w\"\n        sizes=\"(max-width: 800px) 100vw, 800px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n  </a>\n    </span></p>\n<p>Through my window, I see the result of good plans but poor execution. Opposite my flat, there is a partially completed construction place. Buildings were supposed to be eye-catching Mediterranean style apartments.  Delivery date? Two years ago. Actual? More and more unknown.</p>\n<p>Some time ago, I heard that using Event Sourcing makes creating Event-Driven Architecture easier. The arguments were correct, that if we’re already publishing events to trigger business workflows, then at some point, we may want to also store events to not lose information. Agreed. However, I also heard that keeping the state as events will simplify things. We’ll have a source of truth with a record of the system behaviour. This will allow, e.g. to confront the results of the operations with the recorded state. I’d agree with that, with one distinction. It’s easier as long as you already know Event Sourcing.</p>\n<p>Many people in the DDD community claim that the essential is to properly break down the system into autonomous parts called bounded contexts. Once we have it, the rest is secondary and will sort itself out. For sure.</p>\n<p>Many seasoned programmers speak similarly about new technologies. They claim that they can translate past experience into new technologies. That’s true that by analogy, they can catch the big picture quicker. But isn’t it a bold assumption to say that Win.Forms specialist will learn Angular quickly?</p>\n<p>The end result may differ a lot from the initial ideas. I saw the plan of those buildings next to me. Now I can see the effects of the execution. Or actually, the lack.</p>\n<p>I believe that we should carefully acknowledge not only the point of view of our authorities but also their seating point. If we want to find out how to form a wall, do we ask an architect or a foreman? An architect may know the theory, but the practice is what we’re looking for. On the other hand, if you want to know where to put the wall, you prefer the architect to do measurements. At least if you don’t want to have the roof falling to your head.</p>\n<p>After I had torn a ligament in my knee, I went to two qualified orthopedists. One said I should have surgery and do a reconstruction. The second stated that there is no need for that; rehabilitation should be enough. Guess which one had a specialization in surgery and which in rehabilitation?</p>\n<p>People usually give us advice from the point where they’re currently standing. They are entitled to a biased view. An architect who rarely does programming will tend to downplay the value of implementation and tactical patterns. Midlevel developers will focus on technicalities instead of the global system impact. The team manager or consultant will emphasize the importance of soft skills (or esoteric techniques known only to them).</p>\n<p>The truth is that we need all of them. The excellent plan will fall on the bad execution. The best execution for the wrong case will be just a waste of time. We should carefully evaluate the advice considering what we need and what an expert can give us.</p>\n<p>Therefore, when we’re reading an article, watching a talk, let’s also pay attention to the place where the person is standing. The perspective from there may be much different from where we are right now. That can be good, as it may push us in the right direction. But it may also be misleading, as we accidentally take biases of this person without understanding the tradeoffs. Personally, I prefer to follow not only people from pedestal but also those that are closer to my position. A bit further in the journey, but not too far. That helps me to calibrate my view as those people are more relative to my daily struggles.</p>\n<p>Polish historical leader <a href=\"https://en.wikipedia.org/wiki/J%C3%B3zef_Pi%C5%82sudski\">Józef Piłsudzki</a> reportedly used to say: <em>“Right is like an ass, everyone has its own”</em>.</p>\n<p>Cheers!</p>\n<p>Oskar</p>"},"site":{"siteMetadata":{"facebook":{"appId":""}}}},"pageContext":{"slug":"/projections_and_read_models_in_event_driven_architecture/","lang":"en","langKey":"en","prev":{"id":"cbd757de-9e1c-5670-bc4c-2766593b259f","fields":{"slug":"/how_to_validate_business_logic/","prefix":"2023-01-15","source":"posts","langKey":"en"},"frontmatter":{"title":"How to validate business logic","category":"Event Sourcing"}},"next":{"id":"de486c79-7268-55c7-ac18-c703428647a8","fields":{"slug":"/stacking_the_bricks/","prefix":"2023-01-29","source":"posts","langKey":"en"},"frontmatter":{"title":"Stacking the bricks in the software development process","category":"Coding Life"}},"source":"posts"}},
    "staticQueryHashes": ["2742854296"]}