{
    "componentChunkName": "component---src-templates-post-template-js",
    "path": "/pl/throw-result-or-neither/",
    "result": {"data":{"post":{"id":"2a7f0c7e-d3f5-50d5-9017-7e2be56b2771","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/5eca6803bd7de5fa9ebc4a238f9b0745/17127/2026-07-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: 54.49999999999999%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAwAE/8QAFQEBAQAAAAAAAAAAAAAAAAAAAQD/2gAMAwEAAhADEAAAAWI3hLVJ/8QAGxAAAgIDAQAAAAAAAAAAAAAAAQMCEgARITP/2gAIAQEAAQUCM6gt1FbBUnIcCfP/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAbEAEBAAEFAAAAAAAAAAAAAAABABARMTKBof/aAAgBAQAGPwLbWAC4+SXWP//EABkQAQADAQEAAAAAAAAAAAAAAAEAESFBMf/aAAgBAQABPyE7BNaojmZrvsAbe7DXZhODs+E//9oADAMBAAIAAwAAABAIH//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABYRAQEBAAAAAAAAAAAAAAAAAAARAf/aAAgBAgEBPxC4j//EABwQAQEBAAIDAQAAAAAAAAAAAAERACFBUWGRof/aAAgBAQABPxAHER+mCquSr8Zye9VS9Y5okAcecSBhV9vOICBB3//Z'); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"cover\"\n        title=\"cover\"\n        src=\"/static/5eca6803bd7de5fa9ebc4a238f9b0745/c60e9/2026-07-20-cover.jpg\"\n        srcset=\"/static/5eca6803bd7de5fa9ebc4a238f9b0745/37402/2026-07-20-cover.jpg 200w,\n/static/5eca6803bd7de5fa9ebc4a238f9b0745/4cda9/2026-07-20-cover.jpg 400w,\n/static/5eca6803bd7de5fa9ebc4a238f9b0745/c60e9/2026-07-20-cover.jpg 800w,\n/static/5eca6803bd7de5fa9ebc4a238f9b0745/6c738/2026-07-20-cover.jpg 1200w,\n/static/5eca6803bd7de5fa9ebc4a238f9b0745/17127/2026-07-20-cover.jpg 1408w\"\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>There is one question I keep getting about my event-sourced code: why don’t I use</strong> <code class=\"language-text\">Result</code><strong>?</strong></p>\n<p>It usually comes after someone sees a decision throwing an exception. They would put the failure in a <code class=\"language-text\">Result</code>, leave <code class=\"language-text\">try/catch</code> at the application boundary, and make the distinction explicit in the return type. That is a fair question, but the return type is only part of the answer.</p>\n<p><strong>I don’t mind using exceptions. I also return business failures as events.</strong> Which one I choose depends on what the failure means and what the application needs to do with it. What’s more, I don’t mind Result either, as long as I’m using a language where it’s a native and idiomatic way to use it. Stil…</p>\n<p><strong>Some failures are worth retaining as business data.</strong> An out-of-stock request can contribute to an unmet-demand report. A declined payment can start another process or be grouped by its reason. An item limit can explain why customers abandon an operation.</p>\n<p>Gojko Adzic calls the systematic use of unexpected or marginal usage patterns to improve a product <strong><a href=\"https://www.architecture-weekly.com/p/webinar-23-gojko-adzic-on-designing\">lizard optimization</a></strong>. Repeated out-of-stock requests can reveal demand we do not serve or a user experience that encourages impossible quantities. Turning every attempt into only a response or an exception log makes that pattern harder to find. An event gives the application data it can query, project, and use in another process.</p>\n<p>Event Sourcing can help take advantage of unwanted and unexpected failures. We can model negative outcomes as events, just like regular ones. Our business logic informs us of what happened, not whether it’s a success or an error. It’s just an outcome of our decision.</p>\n<p>So essentially we’re getting three options to deal with an unexpected scenario.</p>\n<ul>\n<li>throw an error (e.g. <code class=\"language-text\">OutOfStockError)</code> and catch it at the boundary;</li>\n<li>return a <code class=\"language-text\">Result</code> with the failure on its error track;</li>\n<li>return an event (e.g. <code class=\"language-text\">ProductItemOutOfStock</code>) and decide separately what we do with it.</li>\n</ul>\n<p>Having that, my answer is rarely “just throw” or “just return <code class=\"language-text\">Result</code>.” Before choosing either, I need to know what should be persisted, what the caller should receive, and whether the failure belongs in the application’s exception path.</p>\n<h2 id=\"an-error-as-a-business-fact\" style=\"position:relative;\"><a href=\"#an-error-as-a-business-fact\" aria-label=\"an error as a business fact 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><strong>An error as a business fact</strong></h2>\n<p>Let’s say that the customer asked for a quantity we cannot supply. Recording that outcome can be useful even though the cart did not change. The cart may also reach its item limit or fail payment authorisation. These are expected business outcomes, and the decision can describe each one with a distinct event:</p>\n<div class=\"gatsby-highlight\" data-language=\"typescript\"><pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">const</span> addProductItem <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>\n  command<span class=\"token operator\">:</span> AddProductItem<span class=\"token punctuation\">,</span>\n  state<span class=\"token operator\">:</span> ShoppingCart<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> ProductItemAdded <span class=\"token operator\">|</span> ProductItemOutOfStock <span class=\"token operator\">|</span> ShoppingCartItemLimitReached <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>state<span class=\"token punctuation\">.</span>status <span class=\"token operator\">===</span> <span class=\"token string\">\"Confirmed\"</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">throw</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">IllegalStateError</span><span class=\"token punctuation\">(</span>\n      <span class=\"token string\">\"Cannot add a product to a confirmed shopping cart\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> shoppingCartId<span class=\"token punctuation\">,</span> productItem<span class=\"token punctuation\">,</span> availableQuantity<span class=\"token punctuation\">,</span> maximumItems<span class=\"token punctuation\">,</span> now <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span>\n    command<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>productItem<span class=\"token punctuation\">.</span>quantity <span class=\"token operator\">></span> availableQuantity<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span>\n      type<span class=\"token operator\">:</span> <span class=\"token string\">\"ProductItemOutOfStock\"</span><span class=\"token punctuation\">,</span>\n      data<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n        shoppingCartId<span class=\"token punctuation\">,</span>\n        productId<span class=\"token operator\">:</span> productItem<span class=\"token punctuation\">.</span>productId<span class=\"token punctuation\">,</span>\n        requestedQuantity<span class=\"token operator\">:</span> productItem<span class=\"token punctuation\">.</span>quantity<span class=\"token punctuation\">,</span>\n        availableQuantity<span class=\"token punctuation\">,</span>\n        attemptedAt<span class=\"token operator\">:</span> now<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>state<span class=\"token punctuation\">.</span>productItems<span class=\"token punctuation\">.</span>length <span class=\"token operator\">>=</span> maximumItems<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span>\n      type<span class=\"token operator\">:</span> <span class=\"token string\">\"ShoppingCartItemLimitReached\"</span><span class=\"token punctuation\">,</span>\n      data<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n        maximumItems<span class=\"token punctuation\">,</span>\n        requestedProductId<span class=\"token operator\">:</span> productItem<span class=\"token punctuation\">.</span>productId<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span>\n    type<span class=\"token operator\">:</span> <span class=\"token string\">\"ProductItemAdded\"</span><span class=\"token punctuation\">,</span>\n    data<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> shoppingCartId<span class=\"token punctuation\">,</span> productItem <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p><code class=\"language-text\">ProductItemOutOfStock</code> carries the requested and available quantities. <code class=\"language-text\">ShoppingCartItemLimitReached</code> carries the configured limit and the product that crossed it.</p>\n<p>We could also model a single <code class=\"language-text\">ProductItemAddingFailed</code> event instead. It could have a <code class=\"language-text\">reason</code> string, but then we’d use the granularity and precision of the information. With distinct event types, a projection, workflow, endpoint, or test can check the relevant outcome directly.</p>\n<p>What’s more, we could also add a FailedToAddProductToClosedShoppingCart and get rid of throwing entirely. We’ll get to that, but for now…</p>\n<h2 id=\"broken-rules-can-throw\" style=\"position:relative;\"><a href=\"#broken-rules-can-throw\" aria-label=\"broken rules can throw 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><strong>Broken rules can throw</strong></h2>\n<p>Not every failure needs a business event. A confirmed cart can no longer be modified, so <code class=\"language-text\">AddProductItem</code> is not a valid operation for that state. A command without required data is invalid as well. An unavailable event store, a stock-service timeout, or a serialisation error means the operation could not be completed reliably. I use exceptions for these cases.</p>\n<p>The throw happens before an event is returned, so the handler has nothing to append. The exception retains its stack and reaches the application’s error boundary.</p>\n<p>Some people prefer to use Result and hope for deterministic decision-making. Still, in most popular dev environments (TypeScript, Java, C#, Python, etc.), we can get exceptions. Even in strongly-typed Rust, we can get a panic or other cases. In the presented code <code class=\"language-text\">loadShoppingCart</code> can reject because of data unavailability, <code class=\"language-text\">appendToStream</code> can report a concurrency or connection error, and base code library code can throw on a random, stupid null pointer. The application still needs an error boundary somewhere:</p>\n<div class=\"gatsby-highlight\" data-language=\"typescript\"><pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> result <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">handleAddProductItem</span><span class=\"token punctuation\">(</span>command<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">return</span> <span class=\"token function\">mapResultToResponse</span><span class=\"token punctuation\">(</span>result<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span> <span class=\"token keyword\">catch</span> <span class=\"token punctuation\">(</span>error<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token function\">mapToErrorResponse</span><span class=\"token punctuation\">(</span>error<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>That boundary might be inside WebAPI route, a message handler callback, or wrapped with a conventional error middleware. The remaining choice is whether expected business outcomes should use the same path.</p>\n<h2 id=\"what-result-would-add\" style=\"position:relative;\"><a href=\"#what-result-would-add\" aria-label=\"what result would add 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><strong>What</strong> <code class=\"language-text\">Result</code> <strong>would add</strong></h2>\n<p><code class=\"language-text\">Result</code> puts the success value and the expected failures on separate branches, visible in the type like:</p>\n<div class=\"gatsby-highlight\" data-language=\"typescript\"><pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">type</span> <span class=\"token class-name\">Result<span class=\"token operator\">&lt;</span>Success<span class=\"token punctuation\">,</span> Failure<span class=\"token operator\">></span></span> <span class=\"token operator\">=</span>\n  <span class=\"token operator\">|</span> <span class=\"token punctuation\">{</span> success<span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span> value<span class=\"token operator\">:</span> Success <span class=\"token punctuation\">}</span>\n  <span class=\"token operator\">|</span> <span class=\"token punctuation\">{</span> success<span class=\"token operator\">:</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span> error<span class=\"token operator\">:</span> Failure <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>The cart decision becomes:</p>\n<div class=\"gatsby-highlight\" data-language=\"typescript\"><pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">type</span> <span class=\"token class-name\">AddProductItemFailure</span> <span class=\"token operator\">=</span>\n  <span class=\"token operator\">|</span> ProductItemOutOfStock\n  <span class=\"token operator\">|</span> ShoppingCartItemLimitReached<span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> addProductItem <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>\n  command<span class=\"token operator\">:</span> AddProductItem<span class=\"token punctuation\">,</span>\n  state<span class=\"token operator\">:</span> ShoppingCart<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> Result<span class=\"token operator\">&lt;</span>ProductItemAdded<span class=\"token punctuation\">,</span> AddProductItemFailure<span class=\"token operator\">></span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> shoppingCartId<span class=\"token punctuation\">,</span> productItem<span class=\"token punctuation\">,</span> availableQuantity<span class=\"token punctuation\">,</span> maximumItems<span class=\"token punctuation\">,</span> now <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span>\n    command<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>productItem<span class=\"token punctuation\">.</span>quantity <span class=\"token operator\">></span> availableQuantity<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span>\n      success<span class=\"token operator\">:</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span>\n      error<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n        type<span class=\"token operator\">:</span> <span class=\"token string\">\"ProductItemOutOfStock\"</span><span class=\"token punctuation\">,</span>\n        data<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n          shoppingCartId<span class=\"token punctuation\">,</span>\n          productId<span class=\"token operator\">:</span> productItem<span class=\"token punctuation\">.</span>productId<span class=\"token punctuation\">,</span>\n          requestedQuantity<span class=\"token operator\">:</span> productItem<span class=\"token punctuation\">.</span>quantity<span class=\"token punctuation\">,</span>\n          availableQuantity<span class=\"token punctuation\">,</span>\n          attemptedAt<span class=\"token operator\">:</span> now<span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>state<span class=\"token punctuation\">.</span>productItems<span class=\"token punctuation\">.</span>length <span class=\"token operator\">>=</span> maximumItems<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span>\n      success<span class=\"token operator\">:</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span>\n      error<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n        type<span class=\"token operator\">:</span> <span class=\"token string\">\"ShoppingCartItemLimitReached\"</span><span class=\"token punctuation\">,</span>\n        data<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n          maximumItems<span class=\"token punctuation\">,</span>\n          requestedProductId<span class=\"token operator\">:</span> productItem<span class=\"token punctuation\">.</span>productId<span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span>\n    success<span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n    value<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n      type<span class=\"token operator\">:</span> <span class=\"token string\">\"ProductItemAdded\"</span><span class=\"token punctuation\">,</span>\n      data<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> shoppingCartId<span class=\"token punctuation\">,</span> productItem <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>Technically it looks more or less the same: <code class=\"language-text\">ProductItemAdded</code> is returned on the success branch, while the other two errors are returned on the failure branch. The caller checks the branch before returning the event. In this application, adding the product changes the cart and produces a 204 response. Running out of stock or exceeding the limit leaves the cart unchanged and produces a 409 response. The handler appends only the success value:</p>\n<div class=\"gatsby-highlight\" data-language=\"typescript\"><pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">const</span> handleAddProductItem <span class=\"token operator\">=</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span>\n  command<span class=\"token operator\">:</span> AddProductItem<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> <span class=\"token builtin\">Promise</span><span class=\"token operator\">&lt;</span>Result<span class=\"token operator\">&lt;</span>ProductItemAdded<span class=\"token punctuation\">,</span> AddProductItemFailure<span class=\"token operator\">>></span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> state <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">loadShoppingCart</span><span class=\"token punctuation\">(</span>command<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 keyword\">const</span> result <span class=\"token operator\">=</span> <span class=\"token function\">addProductItem</span><span class=\"token punctuation\">(</span>command<span class=\"token punctuation\">,</span> state<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>result<span class=\"token punctuation\">.</span>success<span class=\"token punctuation\">)</span> \n    <span class=\"token keyword\">return</span> <span class=\"token function\">PreconditionFailed</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  \n  <span class=\"token keyword\">await</span> eventStore<span class=\"token punctuation\">.</span><span class=\"token function\">appendToStream</span><span class=\"token punctuation\">(</span>\n    <span class=\"token function\">shoppingCartStreamName</span><span class=\"token punctuation\">(</span>command<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>result<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token function\">NoContent</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>If we used event, it could look like that:</p>\n<div class=\"gatsby-highlight\" data-language=\"typescript\"><pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">const</span> handleAddProductItem <span class=\"token operator\">=</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span>\n  command<span class=\"token operator\">:</span> AddProductItem<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> <span class=\"token builtin\">Promise</span><span class=\"token operator\">&lt;</span>Result<span class=\"token operator\">&lt;</span>ProductItemAdded<span class=\"token punctuation\">,</span> AddProductItemFailure<span class=\"token operator\">>></span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> state <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">loadShoppingCart</span><span class=\"token punctuation\">(</span>command<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 keyword\">const</span> event <span class=\"token operator\">=</span> <span class=\"token function\">addProductItem</span><span class=\"token punctuation\">(</span>command<span class=\"token punctuation\">,</span> state<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">.</span>type <span class=\"token operator\">===</span> <span class=\"token string\">\"ProductItemOutOfStock\"</span> <span class=\"token operator\">||</span> event<span class=\"token punctuation\">.</span>type <span class=\"token operator\">===</span> <span class=\"token string\">\"ShoppingCartItemLimitReached\"</span><span class=\"token punctuation\">)</span> \n    <span class=\"token keyword\">return</span> <span class=\"token function\">PreconditionFailed</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  \n  <span class=\"token keyword\">await</span> eventStore<span class=\"token punctuation\">.</span><span class=\"token function\">appendToStream</span><span class=\"token punctuation\">(</span>\n    <span class=\"token function\">shoppingCartStreamName</span><span class=\"token punctuation\">(</span>command<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>result<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token function\">NoContent</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>Both versions preserve the same three event types. <code class=\"language-text\">Result</code> adds a success-or-failure classification around them. Which can be fine, but if you look again at the sample, it’s still pretty easy to ignore the different consequences of the events. We’re just one check on success further from it.</p>\n<p>I saw many codebases where people were just blindly muting all errors by default, getting no benefit from it, but just polluting the codebase. Also keeping in mind that they still need to wrap the codebase with try/catch and have a conventional mapping layer.</p>\n<p>We can also see that the Result wrapper in our case doesn’t add much besides success/error classification. If we’d like to return different response status based on the <em>failure</em> scenario, we’d need to add the same switch. So we’re not removing the decision; we’re just making it easier to ignore.</p>\n<p>Of course, many languages made it streamlined by adding pipe operator etc. Yet, again, many popular environments don’t have it. TypeScript has no pipe operator; the <a href=\"https://github.com/tc39/proposal-pipeline-operator\">TC39 proposal</a> is not (yet?) part of the language. Repeating the branch adds checks throughout the call chain; introducing a helper library adds vocabulary that is not native to the language. Those libraries are extremely noisy and require tribal knowledge and onboarding.</p>\n<p>Scott Wlaschin, who popularised railway-oriented programming, <a href=\"https://fsharpforfunandprofit.com/posts/against-railway-oriented-programming/\">makes the same qualification</a>: <code class=\"language-text\">Result</code> models expected alternatives, but it is not intended to wrap every function or replace exceptions.</p>\n<p><strong>Same for events; we should always discuss with the business if this information is important for them and if they want to keep it.</strong> We should also ask what should happen and if we can and should recover in our business application when this scenario happens.</p>\n<p><code class=\"language-text\">ProductItemAdded</code>, <code class=\"language-text\">ProductItemOutOfStock</code>, and <code class=\"language-text\">ShoppingCartItemLimitReached</code> just tell what has happened. Domain doesn’t need to care how the application handles it. It can mean failure for the current request and still be worth recording because a projection or workflow consumes it. Adding those rules to <code class=\"language-text\">Result</code> would move persistence concerns into the domain decision and add premature classification. When we change the behaviour in the application and record one of those scenarios, we would need to change domain code, even if business rules are the same. Just to formally move one case from failure to success.</p>\n<p>Adding <code class=\"language-text\">Result</code> changes every calling layer without replacing the persistence rules, batch handling, or exception boundary. I do not see much of a gain here. Of course, a codebase that already uses <code class=\"language-text\">Result</code> as its error channel may judge it differently, especially when the alternatives are not events. Thus…</p>\n<h2 id=\"returning-an-event-doesnt-mean-persisting-it\" style=\"position:relative;\"><a href=\"#returning-an-event-doesnt-mean-persisting-it\" aria-label=\"returning an event doesnt mean persisting it 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><strong>Returning an event doesn’t mean persisting it</strong></h2>\n<p><code class=\"language-text\">ProductItemOutOfStock</code> might be needed after the request. Measuring unmet demand, suggesting alternatives, or showing failed attempts to support staff requires a durable event. Still, if our business doesn’t care about the fact and we just want to log it, or forward to the HTTP Response, then we may not need to record it. Appending it would retain data the application does not need. Omitting it from the decision would instead force the endpoint to reread the outcome.</p>\n<p>For this example, the decision returns <code class=\"language-text\">ProductItemOutOfStock</code> to the caller and the stream remains unchanged. I call this <strong>selective persistence</strong>: the decision describes the outcome, while the application decides whether it should be durable.</p>\n<p>Command Handling can be described by the following steps:</p>\n<ol>\n<li>\n<p><strong><a href=\"/pl/how_to_get_the_current_entity_state_in_event_sourcing/\">Read events from the stream and build the state from them</a></strong> (in other words <em>aggregate stream</em>).</p>\n</li>\n<li>\n<p><strong>Run the business logic using the command and the state.</strong> Use the default (<em>initial</em>) state if the stream does not exist.</p>\n</li>\n<li>\n<p><strong>Append the result of the business logic (so events) at the end of the stream</strong> from which you’ve read events. Use the read version (or the one provided by the user) for an <a href=\"/pl/optimistic_concurrency_for_pessimistic_times/\">optimistic concurrency check</a>.</p>\n</li>\n</ol>\n<p>In a nutshell, we could generalise it as:</p>\n<div class=\"gatsby-highlight\" data-language=\"typescript\"><pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">function</span> <span class=\"token generic-function\"><span class=\"token function\">handleCommand</span><span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span>State<span class=\"token punctuation\">,</span> Command<span class=\"token punctuation\">,</span> Event<span class=\"token operator\">></span></span></span><span class=\"token punctuation\">(</span>\n  eventStore<span class=\"token operator\">:</span> EventStore<span class=\"token punctuation\">,</span> \n  streamId<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function-variable function\">decide</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>state<span class=\"token operator\">:</span> State<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> Event<span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function-variable function\">evolve</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>event<span class=\"token operator\">:</span> Event<span class=\"token punctuation\">,</span> state<span class=\"token operator\">:</span> State<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> State<span class=\"token punctuation\">,</span>\n  <span class=\"token function-variable function\">initialState</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> State<span class=\"token punctuation\">,</span>\n  <span class=\"token comment\">// 👇 See what I did here</span>\n  skipEvent<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>event<span class=\"token operator\">:</span> Event<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token builtin\">boolean</span>\n  <span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// 1. Get the state</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> state<span class=\"token punctuation\">,</span> currentStreamVersion <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> eventStore<span class=\"token punctuation\">.</span><span class=\"token function\">aggregateStream</span><span class=\"token punctuation\">(</span>streamName<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n    evolve<span class=\"token punctuation\">,</span>\n    initialState<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 comment\">// 2. Run business logic</span>\n  <span class=\"token keyword\">const</span> events <span class=\"token operator\">=</span> <span class=\"token function\">decide</span><span class=\"token punctuation\">(</span>state<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// 3. Filter out \"failure\" events </span>\n  <span class=\"token keyword\">const</span> eventsToAppend <span class=\"token operator\">=</span> skipEvent <span class=\"token operator\">?</span> events<span class=\"token punctuation\">.</span><span class=\"token function\">filter</span><span class=\"token punctuation\">(</span>skipEvent<span class=\"token punctuation\">)</span> <span class=\"token operator\">:</span> events<span class=\"token punctuation\">;</span>\n  \n  <span class=\"token comment\">// 4. Append events</span>\n  <span class=\"token keyword\">const</span> appendResult <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> eventStore<span class=\"token punctuation\">.</span><span class=\"token function\">appendToStream</span><span class=\"token punctuation\">(</span>streamName<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>event<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n    expectedStreamVersion<span class=\"token operator\">:</span> currentStreamVersion<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 comment\">// 5. Return result</span>\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token operator\">...</span>appendResult<span class=\"token punctuation\">,</span>\n    events<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 👈 all events</span>\n    appendedEvents<span class=\"token punctuation\">,</span> <span class=\"token comment\">// 👈 only those that were appended</span>\n    newState<span class=\"token operator\">:</span> <span class=\"token function\">evolve</span><span class=\"token punctuation\">(</span>aggregation<span class=\"token punctuation\">.</span>state<span class=\"token punctuation\">,</span> event<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>A command handler reads a stream, rebuilds its state, runs a decision, applies the produced events, and appends them with an optimistic concurrency check. <a href=\"https://github.com/event-driven-io/emmett\">Emmett</a> takes care of that flow. The application provides <code class=\"language-text\">evolve</code> to apply an event to state and <code class=\"language-text\">initialState</code> for a new stream.</p>\n<p>We can pass <code class=\"language-text\">skipEvent</code> function that inspects the decision’s outcome before anything is appended. The decision still returns the same events; the function only changes how the handler treats them.</p>\n<p>In Emmett, I allow it to encapsulate the Command Handler setup by:</p>\n<div class=\"gatsby-highlight\" data-language=\"typescript\"><pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token comment\">// 1. Command handler setup</span>\n<span class=\"token keyword\">const</span> handle <span class=\"token operator\">=</span> <span class=\"token generic-function\"><span class=\"token function\">CommandHandler</span><span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span>ShoppingCart<span class=\"token punctuation\">,</span> ShoppingCartEvent<span class=\"token operator\">></span></span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  evolve<span class=\"token punctuation\">,</span>\n  initialState<span class=\"token punctuation\">,</span>\n  middleware<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n    <span class=\"token function\">skipOn</span><span class=\"token punctuation\">(</span>\n      <span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> event<span class=\"token punctuation\">.</span>type <span class=\"token operator\">===</span> <span class=\"token string\">\"ProductItemOutOfStock\"</span> \n        <span class=\"token operator\">||</span> event<span class=\"token punctuation\">.</span>type <span class=\"token operator\">===</span> <span class=\"token string\">\"ShoppingCartItemLimitReached\"</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">]</span><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 comment\">// 2. Usage</span>\n<span class=\"token keyword\">const</span> result <span class=\"token operator\">=</span> <span class=\"token function\">handle</span><span class=\"token punctuation\">(</span>eventStore<span class=\"token punctuation\">,</span> shoppingCartId<span class=\"token punctuation\">,</span> addProductItem<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>I think that this setup clearly shows that we’re separating the business logic from how the business logic handles that, by explicitly using <code class=\"language-text\">skipOn</code> middleware, we’re telling that when such an event happens, we don’t want to store it, but we’ll still return it in the result.</p>\n<p>This gives us the option to still do e.g. error mapping to HTTP status as:</p>\n<div class=\"gatsby-highlight\" data-language=\"typescript\"><pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span>\n  Conflict<span class=\"token punctuation\">,</span>\n  NoContent<span class=\"token punctuation\">,</span>\n  on<span class=\"token punctuation\">,</span>\n  ResponseFromEvents<span class=\"token punctuation\">,</span>\n  toWeakETag<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">\"@event-driven-io/emmett-expressjs\"</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> <span class=\"token function-variable function\">addProductItemApi</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>router<span class=\"token operator\">:</span> Router<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n  router<span class=\"token punctuation\">.</span><span class=\"token function\">post</span><span class=\"token punctuation\">(</span>\n    <span class=\"token string\">\"/shopping-carts/:shoppingCartId/product-items\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">on</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span>request<span class=\"token operator\">:</span> AddProductItemRequest<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">const</span> shoppingCartId <span class=\"token operator\">=</span> request<span class=\"token punctuation\">.</span>params<span class=\"token punctuation\">.</span>shoppingCartId<span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">const</span> productId <span class=\"token operator\">=</span> <span class=\"token function\">String</span><span class=\"token punctuation\">(</span>request<span class=\"token punctuation\">.</span>body<span class=\"token punctuation\">.</span>productId<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">const</span> availableQuantity <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> inventory<span class=\"token punctuation\">.</span><span class=\"token function\">getAvailableQuantity</span><span class=\"token punctuation\">(</span>productId<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n      <span class=\"token keyword\">const</span> command <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n        type<span class=\"token operator\">:</span> <span class=\"token string\">\"AddProductItem\"</span><span class=\"token punctuation\">,</span>\n        data<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n          shoppingCartId<span class=\"token punctuation\">,</span>\n          productItem<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n            productId<span class=\"token punctuation\">,</span>\n            quantity<span class=\"token operator\">:</span> <span class=\"token function\">Number</span><span class=\"token punctuation\">(</span>request<span class=\"token punctuation\">.</span>body<span class=\"token punctuation\">.</span>quantity<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          availableQuantity<span class=\"token punctuation\">,</span>\n          maximumItems<span class=\"token operator\">:</span> shoppingCartPolicy<span class=\"token punctuation\">.</span>maximumItems<span class=\"token punctuation\">,</span>\n          now<span class=\"token operator\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Date</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n\n      <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> events<span class=\"token punctuation\">,</span> nextExpectedStreamVersion <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">handle</span><span class=\"token punctuation\">(</span>\n        eventStore<span class=\"token punctuation\">,</span> \n        shoppingCartId<span class=\"token punctuation\">,</span> \n        <span class=\"token punctuation\">(</span>state<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">addProductItem</span><span class=\"token punctuation\">(</span>state<span class=\"token punctuation\">,</span> command<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n      <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span>events<span class=\"token punctuation\">.</span><span class=\"token function\">some</span><span class=\"token punctuation\">(</span>event <span class=\"token operator\">=></span> event<span class=\"token punctuation\">.</span>type <span class=\"token operator\">!==</span> <span class=\"token string\">\"ProductItemAdded\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">return</span> <span class=\"token function\">PreconditionFailed</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n      <span class=\"token keyword\">return</span> <span class=\"token function\">NoContent</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n        eTag<span class=\"token operator\">:</span> <span class=\"token function\">toWeakETag</span><span class=\"token punctuation\">(</span>result<span class=\"token punctuation\">.</span>nextExpectedStreamVersion<span class=\"token punctuation\">)</span><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><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>We could, of course, do more advanced error or status mapping by returning exact data, etc. But this example proves that we’re not losing anything compared to the Result scenario. The code is still pretty simple, as with throwing, but more explicit and straightforward.</p>\n<h2 id=\"several-decisions-on-one-stream\" style=\"position:relative;\"><a href=\"#several-decisions-on-one-stream\" aria-label=\"several decisions on one stream 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><strong>Several decisions on one stream</strong></h2>\n<p>You may be wondering why I added middleware, which sounds posh for simple filtering. But let’s discuss one more scenario: a batch import from the external e-commerce system.</p>\n<p>A batch import often contains the contents of multiple shopping carts. Passing all of them to one command handler would be the wrong boundary here: each cart has its own stream and its own concurrency check.</p>\n<p>Even a single imported shopping cart can still require several operations. Especially if we’re trying to deduce what has happened to a shopping cart in the external system, or we should add some specific operations on top (e.g., discounts, etc.).</p>\n<p>The common scenario for imports is translating state changes (e.g., products in the shopping cart) into commands. This gives at least a chance to make our domain context meaningful. Not perfect, but we’ve got to save ourselves sometimes.</p>\n<p>After translation, we may end up with a sequence of granular business operations. The mapping could look like that:</p>\n<div class=\"gatsby-highlight\" data-language=\"typescript\"><pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">const</span> importCartCommands<span class=\"token operator\">:</span> AddProductItem<span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> importedCart<span class=\"token punctuation\">.</span>productItems<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span>\n  <span class=\"token punctuation\">(</span>productItem<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    type<span class=\"token operator\">:</span> <span class=\"token string\">\"AddProductItem\"</span><span class=\"token punctuation\">,</span>\n    data<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n      shoppingCartId<span class=\"token operator\">:</span> importedCart<span class=\"token punctuation\">.</span>shoppingCartId<span class=\"token punctuation\">,</span>\n      productItem<span class=\"token punctuation\">,</span>\n      availableQuantity<span class=\"token operator\">:</span> stockByProductId<span class=\"token punctuation\">[</span>productItem<span class=\"token punctuation\">.</span>productId<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      maximumItems<span class=\"token punctuation\">,</span>\n      now<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><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><span class=\"token punctuation\">;</span></code></pre></div>\n<p>And feed it into our command handler:</p>\n<div class=\"gatsby-highlight\" data-language=\"typescript\"><pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> events<span class=\"token punctuation\">,</span> nextExpectedStreamVersion <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">handle</span><span class=\"token punctuation\">(</span>\n  eventStore<span class=\"token punctuation\">,</span> \n  shoppingCartId<span class=\"token punctuation\">,</span> \n  importCartCommands<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span>command <span class=\"token operator\">=></span> <span class=\"token punctuation\">(</span>state<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">addProductItem</span><span class=\"token punctuation\">(</span>state<span class=\"token punctuation\">,</span> command<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>Every command uses the existing <code class=\"language-text\">addProductItem</code> decision and sees the provisional state produced by the previous accepted products. That allows us to apply our own rules and also handle <a href=\"/pl/idempotent_command_handling/\">idempotence correctly</a>. Persistence waits until every command has been accepted.</p>\n<p>After batch processing, we can gather the result events. What’s more, each operation should use the state with the applied event from the previous command. We store the result in the shopping carts as a single atomic batch once the last command has been handled.</p>\n<p>Technically it’s the same command logic, just wrapped with foreach, as we now have a sequence of decisions instead of the single one.</p>\n<p>What should happen if we encounter unwanted scenarios? So e.g. <code class=\"language-text\">ProductItemOutOfStock</code> or <code class=\"language-text\">ShoppingCartItemLimitReached</code>? Should we stop processing or continue? Let me be a consultant for a moment: It depends! Fine, but on what? On your business logic.</p>\n<p>In our case, we probably wouldn’t want to stop adding product items if one of them was out of stock. Tho, we may decide that there’s no point in processing it further if we already reached the items limit.</p>\n<p>How to do it? We’d need to define a mapping function with different possible outcomes.</p>\n<div class=\"gatsby-highlight\" data-language=\"typescript\"><pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">type</span> <span class=\"token class-name\">MapDecision</span> <span class=\"token operator\">=</span> <span class=\"token operator\">&lt;</span>Event<span class=\"token operator\">></span><span class=\"token punctuation\">(</span>event<span class=\"token operator\">:</span> Event<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token keyword\">void</span> <span class=\"token operator\">|</span> <span class=\"token string\">'APPEND'</span> <span class=\"token operator\">|</span> <span class=\"token string\">'SKIP'</span> <span class=\"token operator\">|</span> <span class=\"token string\">'STOP'</span> <span class=\"token operator\">|</span> <span class=\"token string\">'REJECT'</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>Where:</p>\n<ul>\n<li>APPEND or no result - success, append this event,</li>\n<li>SKIP - ignore this outcome, don’t append it, but continue processing,</li>\n<li>STOP - ignore this outcome, stop processing and append only those that were already accepted,</li>\n<li>REJECT - reject the whole batch, even if there were some successful events.</li>\n</ul>\n<p>The example could be defined for all shopping cart events not to need repeat it in all functions:</p>\n<div class=\"gatsby-highlight\" data-language=\"javascript\"><pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">mapShoppingCartDecision</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\"><span class=\"token literal-property property\">event</span><span class=\"token operator\">:</span> ShoppingCartEvent</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">switch</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">.</span>type<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">case</span> <span class=\"token string\">\"ProductItemOutOfStock\"</span><span class=\"token operator\">:</span>\n      <span class=\"token keyword\">return</span> <span class=\"token string\">\"SKIP\"</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">case</span> <span class=\"token string\">\"ShoppingCartItemLimitReached\"</span><span class=\"token operator\">:</span>\n      <span class=\"token keyword\">return</span> <span class=\"token string\">\"STOP\"</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">case</span> <span class=\"token string\">\"ShoppingCartWasAlreadyConfirmed\"</span><span class=\"token operator\">:</span>\n      <span class=\"token keyword\">return</span> <span class=\"token string\">\"REJECT\"</span><span class=\"token punctuation\">;</span>    \n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>We could pass it to our command handler and decide what to do with our processing.</p>\n<p>In Emmett, I wrapped it as:</p>\n<div class=\"gatsby-highlight\" data-language=\"typescript\"><pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">const</span> handle <span class=\"token operator\">=</span> <span class=\"token generic-function\"><span class=\"token function\">CommandHandler</span><span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span>ShoppingCart<span class=\"token punctuation\">,</span> ShoppingCartEvent<span class=\"token operator\">></span></span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  evolve<span class=\"token punctuation\">,</span>\n  initialState<span class=\"token punctuation\">,</span>\n  middleware<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n    <span class=\"token function\">skipOn</span><span class=\"token punctuation\">(</span>\n      <span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> event<span class=\"token punctuation\">.</span>type <span class=\"token operator\">===</span> <span class=\"token string\">\"ProductItemOutOfStock\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">stopOn</span><span class=\"token punctuation\">(</span>\n      <span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> event<span class=\"token punctuation\">.</span>type <span class=\"token operator\">===</span> <span class=\"token string\">\"ShoppingCartItemLimitReached\"</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function\">rejectOn</span><span class=\"token punctuation\">(</span>\n      <span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> event<span class=\"token punctuation\">.</span>type <span class=\"token operator\">===</span> <span class=\"token string\">\"ShoppingCartWasAlreadyConfirmed\"</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>Still, as you see, we’re building on top of our business logic. It remains the same; we’re just handling persistence, statuses, etc.</p>\n<p>Returned events show which command handling passed before the rejection and which event stopped the import. The importer can report that outcome against the source record and continue with the next cart, which has a different stream.</p>\n<p>For one product, rejecting meant leaving the stream unchanged. For an imported cart, it may also mean discarding the accepted events produced earlier for the same record.</p>\n<p>If every command is accepted, the handler appends their events together to that cart stream. If one decision returns <code class=\"language-text\">ProductItemOutOfStock</code> or <code class=\"language-text\">ShoppingCartItemLimitReached</code>, <code class=\"language-text\">result.events</code> contains the outcomes produced up to that point, the imported record makes no change to the cart, and later commands are not considered.</p>\n<p>The import job can map those events to a record-level outcome. It can report the exact business issue to the source system and continue with the next cart:</p>\n<div class=\"gatsby-highlight\" data-language=\"typescript\"><pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">const</span> validatedProductIds <span class=\"token operator\">=</span> result<span class=\"token punctuation\">.</span>events<span class=\"token punctuation\">.</span><span class=\"token function\">flatMap</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n  event<span class=\"token punctuation\">.</span>type <span class=\"token operator\">===</span> <span class=\"token string\">\"ProductItemAdded\"</span> <span class=\"token operator\">?</span> <span class=\"token punctuation\">[</span>event<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">.</span>productItem<span class=\"token punctuation\">.</span>productId<span class=\"token punctuation\">]</span> <span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> failure <span class=\"token operator\">=</span> result<span class=\"token punctuation\">.</span>events<span class=\"token punctuation\">.</span><span class=\"token function\">find</span><span class=\"token punctuation\">(</span>\n  <span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n    event<span class=\"token punctuation\">.</span>type <span class=\"token operator\">===</span> <span class=\"token string\">\"ProductItemOutOfStock\"</span> <span class=\"token operator\">||</span>\n    event<span class=\"token punctuation\">.</span>type <span class=\"token operator\">===</span> <span class=\"token string\">\"ShoppingCartItemLimitReached\"</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> importOutcome <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">switch</span> <span class=\"token punctuation\">(</span>failure<span class=\"token operator\">?.</span>type<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">case</span> <span class=\"token string\">\"ProductItemOutOfStock\"</span><span class=\"token operator\">:</span>\n      <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span>\n        status<span class=\"token operator\">:</span> <span class=\"token string\">\"WaitingForStock\"</span> <span class=\"token keyword\">as</span> <span class=\"token keyword\">const</span><span class=\"token punctuation\">,</span>\n        validatedProductIds<span class=\"token punctuation\">,</span>\n        productId<span class=\"token operator\">:</span> failure<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">.</span>productId<span class=\"token punctuation\">,</span>\n        requestedQuantity<span class=\"token operator\">:</span> failure<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">.</span>requestedQuantity<span class=\"token punctuation\">,</span>\n        availableQuantity<span class=\"token operator\">:</span> failure<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">.</span>availableQuantity<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">case</span> <span class=\"token string\">\"ShoppingCartItemLimitReached\"</span><span class=\"token operator\">:</span>\n      <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span>\n        status<span class=\"token operator\">:</span> <span class=\"token string\">\"Rejected\"</span> <span class=\"token keyword\">as</span> <span class=\"token keyword\">const</span><span class=\"token punctuation\">,</span>\n        validatedProductIds<span class=\"token punctuation\">,</span>\n        productId<span class=\"token operator\">:</span> failure<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">.</span>requestedProductId<span class=\"token punctuation\">,</span>\n        maximumItems<span class=\"token operator\">:</span> failure<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">.</span>maximumItems<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">default</span><span class=\"token operator\">:</span>\n      <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span>\n        status<span class=\"token operator\">:</span> <span class=\"token string\">\"Imported\"</span> <span class=\"token keyword\">as</span> <span class=\"token keyword\">const</span><span class=\"token punctuation\">,</span>\n        importedProductIds<span class=\"token operator\">:</span> validatedProductIds<span class=\"token punctuation\">,</span>\n        streamVersion<span class=\"token operator\">:</span> result<span class=\"token punctuation\">.</span>nextExpectedStreamVersion<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">await</span> importStatusStore<span class=\"token punctuation\">.</span><span class=\"token function\">record</span><span class=\"token punctuation\">(</span>importedCart<span class=\"token punctuation\">.</span>externalId<span class=\"token punctuation\">,</span> importOutcome<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">switch</span> <span class=\"token punctuation\">(</span>importOutcome<span class=\"token punctuation\">.</span>status<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">case</span> <span class=\"token string\">\"Imported\"</span><span class=\"token operator\">:</span>\n    <span class=\"token keyword\">await</span> salesChannel<span class=\"token punctuation\">.</span><span class=\"token function\">confirmImport</span><span class=\"token punctuation\">(</span>\n      importedCart<span class=\"token punctuation\">.</span>externalId<span class=\"token punctuation\">,</span>\n      importOutcome<span class=\"token punctuation\">.</span>streamVersion<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">case</span> <span class=\"token string\">\"WaitingForStock\"</span><span class=\"token operator\">:</span>\n    <span class=\"token keyword\">await</span> pendingImports<span class=\"token punctuation\">.</span><span class=\"token function\">waitForStock</span><span class=\"token punctuation\">(</span>\n      importedCart<span class=\"token punctuation\">.</span>externalId<span class=\"token punctuation\">,</span>\n      importOutcome<span class=\"token punctuation\">.</span>productId<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">case</span> <span class=\"token string\">\"Rejected\"</span><span class=\"token operator\">:</span>\n    <span class=\"token keyword\">await</span> salesChannel<span class=\"token punctuation\">.</span><span class=\"token function\">rejectImport</span><span class=\"token punctuation\">(</span>importedCart<span class=\"token punctuation\">.</span>externalId<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n      productId<span class=\"token operator\">:</span> importOutcome<span class=\"token punctuation\">.</span>productId<span class=\"token punctuation\">,</span>\n      maximumItems<span class=\"token operator\">:</span> importOutcome<span class=\"token punctuation\">.</span>maximumItems<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 keyword\">break</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>A saved-list restore uses different rules. Available products return to the cart, unavailable ones are reported, and reaching the cart limit ends the restore. Products added before either outcome remain valid, so this operation is not atomic.</p>\n<p>The additions run in one call because every decision needs the cart state produced by the accepted products before it. Otherwise, each decision would evaluate the item limit against an outdated cart.</p>\n<p>The loop stages and applies available products. It returns an unavailable product without staging it and continues. It also returns the item-limit event without staging it, then stops because the cart cannot accept another distinct product</p>\n<p>And here we’re getting back to the essential question.</p>\n<h2 id=\"so-should-we-throw-or-not\" style=\"position:relative;\"><a href=\"#so-should-we-throw-or-not\" aria-label=\"so should we throw or not 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>So should we throw or not?</h2>\n<p>If we’re getting the request from our UI, we’re the authority, so throwing could be fine; we return the status information telling the computer says no. (Read more in <a href=\"/pl/how_to_validate_business_logic/\">How to validate business logic</a>).</p>\n<p>Still, should we throw on an import scenario? Who’s the authority here? What we’re doing is internalising decisions that someone else already made and is just informing us about, or telling us to do our job based on this information.</p>\n<p>So throwing is not a valid option here. The external system told us that its cart contains these products. That is true about the source system, but it does not mean our cart can accept them. The importer translates the external record into commands, and the returned events describe how our local model understood each products.</p>\n<p>For an accepted record, the <code class=\"language-text\">ProductItemAdded</code> events identify the imported products and the resulting stream version is recorded. If stock is missing, the importer records the checked products and the missing quantity. It can continue with other records, then rebuild this cart’s commands with current availability after an inventory update. Retrying the unchanged commands immediately would only reproduce the same outcome. If the cart exceeds its item limit, waiting for stock cannot help; the source record needs correcting, so the importer marks it as rejected.</p>\n<p>Of course we could still throw if this operation is synchronous and we want to give quick feedback to the caller. Still, typically, we run batch operations as a background process; also, to have proper resilience, you already saw above how much integration could happen during such an import. Services we integrate with can be unavailable, have transient issues, time out, etc.</p>\n<p>Those failures enter the import job’s retry or dead-letter policy because there is no reliable business outcome to report yet. The returned events let the application choose between continuing, waiting for a relevant business change, and rejecting one record. Exceptions would just stop processing and not allow recovery and continue with next steps (Read more in <a href=\"/pl/no_it_can_never_happen/\">No, it can never happen!</a>)</p>\n<p>There can be various error cases also on our side.</p>\n<p>If another request changes the cart before our append, <a href=\"https://github.com/event-driven-io/emmett\">Emmett</a> handles the concurrency conflict by loading the stream again and rerunning the decisions against its new state. Decision middleware runs again as well.</p>\n<p>An out-of-stock import is not a concurrency conflict. The cart state did not become stale; the input said that only two items were available. Running the same command again still returns <code class=\"language-text\">ProductItemOutOfStock</code>. The importer waits for an inventory change and then builds a new command with the current availability.</p>\n<p>A custom retry policy can inspect a completed attempt, but it runs after that attempt’s append phase. Retrying a result is safe only when nothing was appended, and useful only when state or input can change.</p>\n<p>A decision and its middleware can therefore run several times during one handler call. Code that only reads the command and state can be rerun. Sending an email or charging a card inside that code would repeat the external effect. In these examples, required external data is fetched before invoking the handler and included in the command. Effects caused by appended events run later in processors, after persistence.</p>\n<p>Middleware could also be useful for authorisation, logging, and measurements. Some of that work belongs around each decision; some should run only once for the complete handler call. Its position relative to the retry boundary determines whether it sees aggregate state and whether a retry repeats it:</p>\n<div class=\"gatsby-highlight\" data-language=\"typescript\"><pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">await</span> <span class=\"token function\">beforeAll</span><span class=\"token punctuation\">(</span>input<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> result <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">retry</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> state <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">aggregate</span><span class=\"token punctuation\">(</span>streamName<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">const</span> decision <span class=\"token keyword\">of</span> decisions<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">await</span> <span class=\"token function\">runWithDecisionMiddleware</span><span class=\"token punctuation\">(</span>decision<span class=\"token punctuation\">,</span> state<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token function\">appendSelectedEvents</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><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\">await</span> <span class=\"token function\">afterAll</span><span class=\"token punctuation\">(</span>result<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">return</span> result<span class=\"token punctuation\">;</span></code></pre></div>\n<p>The callback before the retry boundary receives the complete input once. It can authorise the command batch without repeating that work after a concurrency conflict. State is not available yet because the stream has not been loaded.</p>\n<p>Middleware inside the boundary receives each decision, and the state is rebuilt for the current attempt. A concurrency retry runs it again.</p>\n<p>The callback after the boundary receives the final result once, after persistence. It can record measurements based on appended events or the new stream version. If it throws, already appended events remain committed. Validation belongs earlier; notifications that must be delivered need a durable handler or outbox.</p>\n<h2 id=\"never-throw-in-asynchronous-handlers\" style=\"position:relative;\"><a href=\"#never-throw-in-asynchronous-handlers\" aria-label=\"never throw in asynchronous handlers 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>Never Throw in Asynchronous Handlers</h2>\n<p>As you see, message processing can end up being pretty complex. That’s also why we use Event-Driven Architecture, as it helps to make that explicit and reason about it.</p>\n<p>The import we discussed could be (and probably should be) triggered by an event (e.g. through a webhook), as we’re informed that those items were already processed and we should just accept or ignore it. What has been seen cannot be unseen.</p>\n<p>Whether throwing is safe depends on what sits above the code to catch it. As discussed, a command handler behind an HTTP endpoint, its usual home, has the request: a thrown error unwinds into a response.</p>\n<p>Asynchronous handlers, the projections, reactors, and workflows that react to events, have nothing above them. A throw propagates up, stops the processor, and leaves later events unhandled. The endpoint is only the usual home, not a rule: a command handler run from a reactor loses the same safety net. Instead of throwing, we need to turn the problem into data, so return an event.</p>\n<p><strong>A message handler can turn a declined shopping cart operation into a compensating workflow.</strong> For instance, for <code class=\"language-text\">ProductItemOutOfStock</code> order more products from the producer. It can, of course, just skip a message that should not block processing, or stop without advancing further.</p>\n<p><strong>The same for <a href=\"http://event-driven.io/en/projections_and_read_models_in_event_driven_architecture/#idempotency\">read models</a>.</strong> A projection cannot prevent the source event from being recorded because that event is already in the stream. Throwing only prevents the read model from advancing. If an event carries a value the read model did not expect, throwing does not undo it. The check that should have stopped it belongs in the business logic, before the event was ever recorded; by the time the projection runs, it is too late.</p>\n<p>It builds a read model from events that are already recorded, and a recorded event is a fact: the projection only interprets it, so it has nothing to reject. If an event carries a value the read model did not expect, throwing does not undo it. The check that should have stopped it belongs in the business logic, before the event was ever recorded; by the time the projection runs, it is too late.</p>\n<p>So accept the event and build the best read model you can from it. Skip a duplicate, fall back to a default, clamp a value back into range, whatever keeps the read model sensible and moving. Its error handling must account for delivery and recovery rather than reuse the rules of a command decision.</p>\n<h2 id=\"conclusion\" style=\"position:relative;\"><a href=\"#conclusion\" aria-label=\"conclusion 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><strong>Conclusion</strong></h2>\n<p>The decision returns <code class=\"language-text\">ProductItemAdded</code>, <code class=\"language-text\">ProductItemOutOfStock</code>, or <code class=\"language-text\">ShoppingCartItemLimitReached</code> because the application needs to know which one happened. Out of stock is an unfavourable outcome, but that alone says nothing about storage. During an import it cancels the current source record. During a saved-list restore it is skipped while earlier additions remain. At the HTTP endpoint it becomes a conflict response. The event stays the same; its handling depends on the use case.</p>\n<p>I do not try to remove exceptions from this design. Adding a product to a confirmed cart is an invalid operation. An unavailable event store prevents the handler from producing a reliable result. Both go to the application’s error boundary.</p>\n<p><code class=\"language-text\">Result</code> could label some of the returned events as failures, but persistence, continuation, and retry still need their own decisions. The exception path remains as well. The event types already distinguish the business outcomes, so I prefer to leave them as they are.</p>\n<p><strong>So when someone asks me whether they should throw an error or use a</strong> <code class=\"language-text\">Result</code> <strong>type, I sometimes tell: actually, you can do neither.</strong></p>\n<p>Cheers!</p>\n<p>Oskar</p>\n<p>p.s. if you liked this one, check also:</p>\n<ul>\n<li><a href=\"/pl/idempotent_command_handling/\">Idempotent Command Handling</a></li>\n<li><a href=\"https://fsharpforfunandprofit.com/posts/against-railway-oriented-programming/\">Against Railway-Oriented Programming</a> by Scott Wlaschin</li>\n<li><a href=\"https://file+.vscode-resource.vscode-cdn.net/home/oskar/Repos/emmett/src/docs/guides/error-handling.md\">Emmett: Error Handling</a></li>\n<li><a href=\"https://file+.vscode-resource.vscode-cdn.net/home/oskar/Repos/emmett/src/docs/guides/command-handling.md\">Emmett: Command Handling</a></li>\n</ul>\n<p><strong>p.s.2. 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, and putting pressure on your local government or companies. You can also support Ukraine by donating, e.g. to the <a href=\"https://savelife.in.ua/en/donate/\">Ukraine humanitarian</a> organisation, <a href=\"https://www.gofundme.com/f/help-to-save-the-lives-of-civilians-in-a-war-zone\">Ambulances for Ukraine</a> or <a href=\"https://redcross.org.ua/en/\">Red Cross</a>.</p>","excerpt":"There is one question I keep getting about my event-sourced code: why don’t I use ? It usually comes after someone sees a decision throwing…","fields":{"slug":"/throw-result-or-neither/","prefix":"2026-07-20","langKey":"pl"},"frontmatter":{"title":"Throw, Result, or neither?","author":"oskar dudycz","category":"Event Sourcing","disqusId":null,"useDefaultLangCanonical":true,"cover":{"childImageSharp":{"resize":{"src":"/static/5eca6803bd7de5fa9ebc4a238f9b0745/4fe8c/2026-07-20-cover.jpg"}}}}},"authornote":{"id":"295c4649-a20b-5147-9ca5-5cb77fbcfe66","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":"/throw-result-or-neither/","lang":"pl","langKey":"pl","prev":{"id":"c573de20-1e46-5771-8132-39db8b3b0935","fields":{"slug":"/on-swimming-learning-and-my-journey/","prefix":"2026-07-13","source":"posts","langKey":"pl"},"frontmatter":{"title":"On swimming, learning and my journey to being the last one","category":"Motivational BS"}},"source":"posts"}},
    "staticQueryHashes": ["2742854296"]}