<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
      <title>Nordfjord&#x27;s blog</title>
      <link>https://nordfjord.io</link>
      <description></description>
      <generator>Zola</generator>
      <language>en</language>
      <atom:link href="https://nordfjord.io/rss.xml" rel="self" type="application/rss+xml"/>
      <lastBuildDate>Wed, 28 Feb 2024 00:00:00 +0000</lastBuildDate>
      <item>
          <title>Transitioning to OpenTelemetry</title>
          <pubDate>Wed, 28 Feb 2024 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://nordfjord.io/opentelemetry/</link>
          <guid>https://nordfjord.io/opentelemetry/</guid>
          <description xml:base="https://nordfjord.io/opentelemetry/">&lt;p&gt;This article touches on how we at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;birdie.care&quot;&gt;birdie&lt;&#x2F;a&gt; handled our
transition from logs towards using OpenTelemetry as the primary mechanism for
achieving world-class observability of our systems.&lt;&#x2F;p&gt;
&lt;p&gt;We had bet on structured logging using
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.npmjs.com&#x2F;package&#x2F;bunyan&quot;&gt;bunyan&lt;&#x2F;a&gt;. This meant our logging platform
had an easy time parsing our logs and making them queryable on the different
dimensions we were emitting. However, our logging strategy could be best
summarised as &quot;Yes.&quot; We shipped request logs from ALB, nginx ingresses and istio
sidecars, and the applications themselves as well. We logged each SQS message
handling in triplicate (&lt;code&gt;Received SQS Message&lt;&#x2F;code&gt;, &lt;code&gt;Started processing SQS Message&lt;&#x2F;code&gt;, &lt;code&gt;Finished processing SQS message&lt;&#x2F;code&gt;). We tended to log method calls in
duplicate (&lt;code&gt;Doing X&lt;&#x2F;code&gt;, &lt;code&gt;Successfully did X&lt;&#x2F;code&gt;). Our log levels were not well
thought out and we&#x27;d often log warnings for things that were entirely expected.&lt;&#x2F;p&gt;
&lt;p&gt;It turned out that a lot of this information was already captured automatically
by OpenTelemetry with the added benefit of causal linking between spans. In
essence we were effectively doubling our telemetry cost by emitting the same
data in slightly different formats.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;what-is-the-difference-between-a-log-and-a-span&quot;&gt;What is the difference between a log and a span?&lt;&#x2F;h1&gt;
&lt;p&gt;Since we were already using structured logging the difference between a log and
a span was actually fairly small. You can think of a structured log line as an
arbitrarily wide structured datablob. A span is that with some additional
conventions layered on top. A span will contain a:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Name&lt;&#x2F;strong&gt;: The name of the operation&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Timestamp&lt;&#x2F;strong&gt;: When did the operation occur?&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Duration&lt;&#x2F;strong&gt;: How long did the operation take?&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Kind&lt;&#x2F;strong&gt;: Is it a server, client, consumer, producer, or internal span?&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Trace ID&lt;&#x2F;strong&gt;: Which unit of work (e.g. request) is this span a part of?&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Span ID&lt;&#x2F;strong&gt;: Unique identifier for this span&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Parent ID&lt;&#x2F;strong&gt;: The Span ID of the parent (empty for root spans)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Had we added all of the above to our logging infrastructure it&#x27;d have been
indistinguishable from OpenTelemetry spans. Of course, there&#x27;s more to
OpenTelemetry than just spans. For one, it has robust context-propagation.
Propagation is the act of encoding and sending the &quot;context&quot; (trace id, span id,
&lt;em&gt;other baggage&lt;&#x2F;em&gt;) to a downstream service in order to allow us to stitch together
the full path of a request through our system. We had built some custom tooling
for log context propagation by attaching request IDs as http headers.
OpenTelemetry does this automatically meaning we&#x27;d have less code to maintain.&lt;&#x2F;p&gt;
&lt;p&gt;Another notable difference is that OpenTelemetry serialises the spans to the
OTLP format and can send them over GRPC or HTTP. These protocols are highly
optimised. Bunyan meanwhile serialises via &lt;code&gt;JSON.stringify&lt;&#x2F;code&gt; and ships over
&lt;code&gt;stdout&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The biggest difference however, is in terms of mental model. When logging we&#x27;re
appending a &quot;point in time&quot; information to a log. Traces on the other hand are
more than a list of what happened. They&#x27;re connected spans in a causal tree,
drawn on a timeline.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;logs-vs-traces&quot;&gt;Logs vs. Traces&lt;&#x2F;h1&gt;
&lt;p&gt;To better understand this let&#x27;s look at two examples of logging and tracing in
different contexts.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;sqs-message-handler-logging&quot;&gt;SQS Message Handler (Logging)&lt;&#x2F;h2&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;async function&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; handleMessage&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Message&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;  log&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;info&lt;&#x2F;span&gt;&lt;span&gt;({&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;message_id&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; message&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; tenant_id&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; message&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;tenant_id&lt;&#x2F;span&gt;&lt;span&gt;},&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;Processing message&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; startTime&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Date&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;now&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  try&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;      &#x2F;&#x2F; do all sorts of stuff&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; catch&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;err&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;    log&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;err&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; message_id&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; message&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; tenant_id&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; message&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;tenant_id&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; duration&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Date&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;now&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; startTime&lt;&#x2F;span&gt;&lt;span&gt; }, &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;      &amp;#39;Failed to process message&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    throw&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; err&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;  log&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;info&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; message_id&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; message&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; tenant_id&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; message&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;tenant_id&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; duration&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Date&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;now&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; startTime&lt;&#x2F;span&gt;&lt;span&gt; }, &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;    &amp;#39;Message processed&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;sqs-message-handler-tracing&quot;&gt;SQS Message Handler (Tracing)&lt;&#x2F;h2&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;async function&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; handleMessage&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;message&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Message&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;  &#x2F;&#x2F; the SQS auto instrumentation will already have created a root span&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;  &#x2F;&#x2F; instead of creating more spans, we add additional context to the current one&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; span&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; trace&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;getActiveSpan&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;  span&lt;&#x2F;span&gt;&lt;span&gt;?.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;setAttributes&lt;&#x2F;span&gt;&lt;span&gt;({&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;app.message_id&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; message&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;app.tenant_id&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; message&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;tenant_id&lt;&#x2F;span&gt;&lt;span&gt; })&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;  &#x2F;&#x2F; do all sorts of stuff&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;  &#x2F;&#x2F; note that we do not need to manually record the exception here because the&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;  &#x2F;&#x2F; auto instrumentation will have wrapped our handler with a catch that does&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;  &#x2F;&#x2F; `span.recordException(err)`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;decider-transaction-logging&quot;&gt;Decider Transaction (Logging)&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;ve written on the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;nordfjord.io&#x2F;equinox&quot;&gt;Equinox programming model&lt;&#x2F;a&gt;
before where deciders are a primary abstraction.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;async function&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; transact&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;store&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; decider&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; streamName&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; command&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;category&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; streamId&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; StreamName&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;parse&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;streamName&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;  log&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;info&lt;&#x2F;span&gt;&lt;span&gt;({&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; category&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; streamId&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; type&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; command&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt; },&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;Handling command&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  try&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    const&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;version&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; previousEvents&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; store&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;readStream&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;streamName&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;    log&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;info&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; category&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; streamId&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; type&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; command&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; version&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; events&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; previousEvents&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;length&lt;&#x2F;span&gt;&lt;span&gt; },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;      &amp;#39;Loaded events&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; state&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; previousEvents&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;reduce&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;decider&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;evolve&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; decider&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;initialState&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; newEvents&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; decider&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;decide&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; command&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;    log&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;info&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; category&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; streamId&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; type&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; command&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; version&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; events&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; newEvents&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;length&lt;&#x2F;span&gt;&lt;span&gt; },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;      &amp;#39;Appending events&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; result&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; store&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;append&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;streamName&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; newEvents&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; version&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;    log&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;info&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; category&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; streamId&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; type&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; command&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; result&lt;&#x2F;span&gt;&lt;span&gt; },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;      &amp;#39;Handled command&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; result&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; catch&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;err&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;    log&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; err&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; category&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; streamId&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; type&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; command&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt; },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;      &amp;#39;Failed to handle command&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    throw&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; err&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;decider-transaction-tracing&quot;&gt;Decider Transaction (Tracing)&lt;&#x2F;h2&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;function&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; transact&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;store&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; decider&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; streamName&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; command&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;category&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; streamId&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; StreamName&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;parse&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;streamName&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; tracer&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;startActiveSpan&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;#39;transact&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;    kind&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; SpanKind&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;CLIENT&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;    attributes&lt;&#x2F;span&gt;&lt;span&gt;: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;      &amp;#39;app.category&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; category&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;      &amp;#39;app.stream_id&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; streamId&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;      &amp;#39;app.stream_name&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; streamName&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;      &amp;#39;app.command_type&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; command&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;type&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  },&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; async&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; span&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    try&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      const&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;version&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; previousEvents&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; store&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;readStream&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;streamName&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;      span&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;setAttributes&lt;&#x2F;span&gt;&lt;span&gt;({ &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;        &amp;#39;app.event_count&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; previousEvents&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;length&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;        &amp;#39;app.version&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; version&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      })&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; state&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; previousEvents&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;reduce&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;decider&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;evolve&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; decider&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;initialState&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; newEvents&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; decider&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;decide&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; command&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;      span&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;setAttribute&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;#39;app.append_count&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; newEvents&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;length&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; result&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; store&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;append&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;streamName&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; newEvents&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; version&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;      span&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;setAttribute&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;#39;app.result_version&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; result&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;version&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; result&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; catch&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;err&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;      span&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;recordException&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;err&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      throw&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; err&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; finally&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;      span&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;end&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  })&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;These examples hopefully illustrate two things:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;The difference between logging and tracing isn&#x27;t that big&lt;&#x2F;li&gt;
&lt;li&gt;Yet, traces are vastly superior to structured logs&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;That second point likely needs some elaboration. Why exactly are traces
superior here? To answer that we need to contrast the actual outputs of each
approach.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;logs&quot;&gt;Logs&lt;&#x2F;h3&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;json&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;name&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;test&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;hostname&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;localhost&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;pid&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 166860&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;level&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 30&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;category&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;test&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;streamId&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;1&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;type&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;command&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;msg&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;Handling command&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;time&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;2024-02-25T20:52:24.407Z&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;v&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;name&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;test&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;hostname&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;localhost&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;pid&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 166860&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;level&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 30&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;category&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;test&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;streamId&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;1&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;type&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;command&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;version&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;events&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;msg&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;Loaded events&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;time&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;2024-02-25T20:52:24.408Z&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;v&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;name&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;test&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;hostname&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;localhost&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;pid&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 166860&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;level&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 30&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;category&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;test&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;streamId&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;1&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;type&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;command&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;version&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;events&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;msg&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;Appending events&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;time&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;2024-02-25T20:52:24.408Z&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;v&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;name&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;test&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;hostname&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;localhost&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;pid&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 166860&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;level&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 30&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;category&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;test&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;streamId&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;1&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;type&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;command&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;result&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; true&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;msg&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;Handled command&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;time&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;2024-02-25T20:52:24.408Z&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;v&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;traces&quot;&gt;Traces&lt;&#x2F;h3&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;json&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;duration_ms&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 1.556816&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;time&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;2024-02-25T21:04:16.052Z&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;name&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;transact&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;app.category&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;test&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;app.stream_id&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;1&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;app.stream_name&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;test-1&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;app.command_type&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;command&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;app.event_count&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;app.version&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;app.append_count&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;service.name&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;test_service&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;telemetry.sdk.language&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;nodejs&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;telemetry.sdk.name&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;opentelemetry&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  &amp;quot;telemetry.sdk.version&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;1.21.0&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The big difference here is the span represents the operation as a single wide
event where the logs represent the operation as 4 distinct events with
differing attributes. The general strategy and mindset when tracing is to start
with an empty dictionary and populate it with whatever interesting context
appears along the way, it&#x27;s not uncommon to see a single event with 300-400
different attributes.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;visualising-traces&quot;&gt;Visualising Traces&lt;&#x2F;h1&gt;
&lt;p&gt;Because traces form a causation&#x2F;correlation tree it is possible to visualise
them as a diagram.&lt;&#x2F;p&gt;
&lt;div id=&quot;figure-1&quot; style=&quot;margin: 1rem auto; max-width: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center&quot;&gt;
  &lt;img alt=&quot;Visualisation of a trace diagram&quot; src=&quot;&#x2F;opentelemetry&#x2F;decider-trace.png&quot; style=&quot;max-width: 100%&quot; &#x2F;&gt;
  &lt;span style=&quot;margin-top: 1rem&quot;&gt;Figure 1. Decider Trace Visualisation&lt;&#x2F;span&gt;
&lt;&#x2F;div&gt;
&lt;h1 id=&quot;the-importance-of-vendor-choice&quot;&gt;The Importance of Vendor Choice&lt;&#x2F;h1&gt;
&lt;p&gt;The real win of what might not seem like a major shift in mental model comes
once you have a way to extract insights from that data. You need a good tool
that at minimum allows you to drill down&#x2F;filter by any dimension, and
preferably without requiring post-hoc configuration by you.&lt;&#x2F;p&gt;
&lt;p&gt;To illustrate let&#x27;s look at some visualisations we take for granted these days.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;percentiles&quot;&gt;Percentiles&lt;&#x2F;h2&gt;
&lt;p&gt;Visualising the P99, P75, or P999 of your durations is extremely valuable to get
a quick idea of how your system is behaving.&lt;&#x2F;p&gt;
&lt;div id=&quot;figure-2&quot; style=&quot;margin: 1rem auto; max-width: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center&quot;&gt;
  &lt;img alt=&quot;Visualisation of a trace diagram&quot; src=&quot;&#x2F;opentelemetry&#x2F;p99.png&quot; style=&quot;max-width: 100%&quot; &#x2F;&gt;
  &lt;span style=&quot;margin-top: 1rem&quot;&gt;Figure 2. P99 duration&lt;&#x2F;span&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;The above graph shows the impact of adding an index to a database query.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;heatmaps&quot;&gt;Heatmaps&lt;&#x2F;h2&gt;
&lt;p&gt;The number of times P99&#x27;s hid important information from us are too many to
count. Take a look at these two graphs:&lt;&#x2F;p&gt;
&lt;div id=&quot;figure-3&quot; style=&quot;margin: 1rem auto; max-width: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center&quot;&gt;
  &lt;img alt=&quot;Visualisation of a trace diagram&quot; src=&quot;&#x2F;opentelemetry&#x2F;heatmap_before.png&quot; style=&quot;max-width: 100%&quot; &#x2F;&gt;
  &lt;span style=&quot;margin-top: 1rem&quot;&gt;Figure 3. Heatmap before&lt;&#x2F;span&gt;
&lt;&#x2F;div&gt;
&lt;div id=&quot;figure-4&quot; style=&quot;margin: 1rem auto; max-width: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center&quot;&gt;
  &lt;img alt=&quot;Visualisation of a trace diagram&quot; src=&quot;&#x2F;opentelemetry&#x2F;heatmap_after.png&quot; style=&quot;max-width: 100%&quot; &#x2F;&gt;
  &lt;span style=&quot;margin-top: 1rem&quot;&gt;Figure 4. Heatmap after&lt;&#x2F;span&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;These two graphs are from the same service before and after we removed a GIN
index on a highly active table. P99s could not paint this story as vividly.
Those dark blue cells rising in waves on the first graph were the result of the
underlying implementation of GIN indices having an in memory representation that
is fast to append to, but slow to read. So periodically that structure gets
flushed to disk, resetting performance (note that these flushes caused highly
unpredictable insert latencies). We would not have been able to spot this
pattern without the heatmap visualisation.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;extra-bonuses&quot;&gt;Extra bonuses&lt;&#x2F;h2&gt;
&lt;p&gt;If your vendor can automatically surface what is different between two groups of
spans (e.g. those who meet an SLI and those that don&#x27;t). This can drastically
shorten time to resolution when you can immediately see that an issue is
affecting User 1, or Tenant 3 in particular. Or that it is localised to
a particular avaliability zone.&lt;&#x2F;p&gt;
&lt;p&gt;The vendor we ended up going with is called &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;honeycomb.io&quot;&gt;Honeycomb&lt;&#x2F;a&gt;
and I could not recommend them more highly.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;resistance&quot;&gt;Resistance&lt;&#x2F;h1&gt;
&lt;p&gt;I won&#x27;t lie and say this was all smooth sailing, one drawback that the team
noticed fairly early was that their console was empty. They were very used to
seeing what was happening locally by scanning the logs. It&#x27;s entirely possible
to pipe your local traces to Honeycomb and use it during development, but we&#x27;re
creatures of habit and giving up the scrolling wall of text can be a high price
to pay. So instead we met our devs where they were.&lt;&#x2F;p&gt;
&lt;p&gt;It turns out you can derive logs from traces, so we did just that. I created a
package called
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nordfjord&#x2F;opentelemetry-exporter-console-pretty&quot;&gt;opentelemetry-exporter-console-pretty&lt;&#x2F;a&gt;.
This package outputs the spans to the console with fancy colours. Our devs can
enable this exporter while developing to regain the comfy snuggle blanket of a
scrolling wall of text.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;takeaways&quot;&gt;Takeaways&lt;&#x2F;h1&gt;
&lt;ul&gt;
&lt;li&gt;Structured logging and tracing aren&#x27;t very different&lt;&#x2F;li&gt;
&lt;li&gt;OpenTelemetry takes care of a lot of the hard parts of distributed tracing&lt;&#x2F;li&gt;
&lt;li&gt;You can derive logs (and metrics) from traces.&lt;&#x2F;li&gt;
&lt;li&gt;Tracing requires a shift in mindset&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
      <item>
          <title>The Equinox Programming Model</title>
          <pubDate>Mon, 05 Dec 2022 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://nordfjord.io/equinox/</link>
          <guid>https://nordfjord.io/equinox/</guid>
          <description xml:base="https://nordfjord.io/equinox/">&lt;p&gt;My team at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.birdie.care&#x2F;join-us&quot;&gt;birdie&lt;&#x2F;a&gt; recently moved from a home-rolled event-sourcing library to
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jet&#x2F;equinox&quot;&gt;Equinox&lt;&#x2F;a&gt; in one of our services. Equinox describes itself as a
set of low-dependency libraries allowing event-sourced processing against stream-based stores.
While this description is technically accurate, a cursory look at the codebase reveals that a
programming model has formed around the library over time. This article seeks to explore that programming
model.&lt;&#x2F;p&gt;
&lt;p&gt;In this article, I will show you the building blocks at play in the equinox model and how they fit together.
We&#x27;ll go through Events, Folds, Decisions, Domain Services, how to test them, and how to wire them up.
A base-level knowledge of event-sourcing is assumed.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;terminology&quot;&gt;Terminology&lt;&#x2F;h1&gt;
&lt;p&gt;As usual, it is best to get some terminology out of the way before diving in.&lt;&#x2F;p&gt;
&lt;dl&gt;
  &lt;dt&gt;&lt;strong&gt;Stream&lt;&#x2F;strong&gt;&lt;&#x2F;dt&gt;
  &lt;dd&gt;A sequence of ordered events, typically for a single process or entity e.g. &lt;code&gt;User-1&lt;&#x2F;code&gt;&lt;&#x2F;dd&gt;
  &lt;dt&gt;&lt;strong&gt;Category&lt;&#x2F;strong&gt;&lt;&#x2F;dt&gt;
  &lt;dd&gt;A grouping of related streams e.g. &lt;code&gt;User-1&lt;&#x2F;code&gt; belongs to the &lt;code&gt;User&lt;&#x2F;code&gt; category&lt;&#x2F;dd&gt;
  &lt;dt&gt;&lt;strong&gt;Optimistic Concurrency Control&lt;&#x2F;strong&gt;&lt;&#x2F;dt&gt;
  &lt;dd&gt;
    the ability to request that a write be rejected if a specified precondition no longer holds. 
    In a message stream, this is achieved by checking that the last event considered when deriving the state 
    is still the last event
  &lt;&#x2F;dd&gt;
  &lt;dt&gt;&lt;strong&gt;DU&lt;&#x2F;strong&gt;&lt;&#x2F;dt&gt;
  &lt;dd&gt;Discriminated Union. A data type that allows you to represent one of a fixed set of choices.&lt;br&#x2F;&gt;
  also called a &lt;strong&gt;tagged union&lt;&#x2F;strong&gt;, &lt;strong&gt;variant&lt;&#x2F;strong&gt;, &lt;strong&gt;choice type&lt;&#x2F;strong&gt;, 
  &lt;strong&gt;disjoint union&lt;&#x2F;strong&gt;, &lt;strong&gt;sum type&lt;&#x2F;strong&gt;, or &lt;strong&gt;coproduct&lt;&#x2F;strong&gt;&lt;&#x2F;dd&gt;
&lt;&#x2F;dl&gt;
&lt;h1 id=&quot;the-domain&quot;&gt;The Domain&lt;&#x2F;h1&gt;
&lt;p&gt;Because this article&#x27;s purpose is pedagogical, we must choose a simple domain. The goal is to explain the
different parts that make up an event-sourced application under Equinox, not to learn the intricacies of a domain.
The process we&#x27;ll be modeling is a simplified invoicing process for which we&#x27;ll use four events.&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;InvoiceRaised&lt;&#x2F;code&gt; - the invoice was raised&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;InvoiceEmailed&lt;&#x2F;code&gt; - the invoice was emailed to the payer&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;PaymentReceived&lt;&#x2F;code&gt; - The payer has posted payment for the invoice&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;InvoiceFinalized&lt;&#x2F;code&gt; - The invoice has been finalised&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h1 id=&quot;the-events&quot;&gt;The Events&lt;&#x2F;h1&gt;
&lt;p&gt;In an event-sourced system, a Category can be considered analogous to a class. A key insight of Equinox is
that all relevant events for a category can be united as a single Event DU. By convention, we place the Event DU
into an &lt;code&gt;Events&lt;&#x2F;code&gt; module.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;fsharp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;module&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; Events &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; InvoiceRaised&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    {&lt;&#x2F;span&gt;&lt;span&gt; InvoiceNumber&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; int&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      Payer&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; string&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      Amount&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; decimal &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Payment&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; = {&lt;&#x2F;span&gt;&lt;span&gt; Amount&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; decimal &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; EmailReceipt&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    {&lt;&#x2F;span&gt;&lt;span&gt; IdempotencyKey&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; string&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      Recipient&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; string&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      SentAt&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; DateTimeOffset &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Event&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span&gt; InvoiceRaised &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;of&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; InvoiceRaised&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span&gt; InvoiceEmailed &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;of&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; EmailReceipt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span&gt; PaymentReceived &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;of&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Payment&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span&gt; InvoiceFinalized&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Using a DU to represent a given category&#x27;s set of events is beneficial because the F# compiler can
warn us when we forget to handle an event. F# will warn any time it notices a match expression doesn&#x27;t
match its inputs totally. While a sensible choice, it is not without drawbacks. With this model, the common practise
of utilising global &lt;code&gt;TypeMap&lt;&#x2F;code&gt; containing a mapping from an event type to a class is a non-starter. A follow-on
consequence is that we must handle encoding and decoding for each category separately.&lt;&#x2F;p&gt;
&lt;p&gt;Many practitioners choose to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;thinkbeforecoding&#x2F;crazyeights.workshop&#x2F;blob&#x2F;7c0d8f554ea0bb191d481c8b5abad8d315ebdd5d&#x2F;src&#x2F;crazyeights&#x2F;Serialization.fs#L128-L144&quot;&gt;hand-roll&lt;&#x2F;a&gt; serialisation and deserialisation functions.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;fsharp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;module&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; Serialization &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  open&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; System.Text.Json&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; serialize&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; x &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; JsonSerializer.SerializeToUtf8Bytes&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;x&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;) |&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; ReadOnlyMemory&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; encode&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; event &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    match&lt;&#x2F;span&gt;&lt;span&gt; event &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;with&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span&gt; InvoiceRaised   data &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;InvoiceRaised&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt;    serialize data&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span&gt; InvoiceEmailed  data &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;InvoiceEmailed&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt;   serialize data&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span&gt; PaymentReceived data &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;PaymentReceived&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt;  serialize data&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span&gt; InvoiceFinalized     &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;InvoiceFinalized&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; serialize &lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;null&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; decode&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;eventType&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;) =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    match&lt;&#x2F;span&gt;&lt;span&gt; eventType &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;with&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;InvoiceRaised&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; InvoiceRaised    &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;JsonSerializer.Deserialize&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;data.Span&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;InvoiceEmailed&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;   -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; InvoiceEmailed   &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;JsonSerializer.Deserialize&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;data.Span&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;PaymentReceived&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; PaymentReceived  &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;JsonSerializer.Deserialize&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;data.Span&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;InvoiceFinalized&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; InvoiceFinalized&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Hand-rolled functions have the advantage of giving complete control over the serialisation format, which can be necessary in cases where
you&#x27;re migrating to F#. On the other hand, the typical method in equinox is to use
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jet&#x2F;FsCodec&#x2F;&quot;&gt;FsCodec&lt;&#x2F;a&gt;. FsCodec can generate codecs for events as long as you constrain yourself
to use types that work with &lt;code&gt;System.Text.Json&lt;&#x2F;code&gt;. This codec typically lives in the same module as the events.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;fsharp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;module&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; Events &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Event&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span&gt; InvoiceRaised &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;of&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; InvoiceRaised&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span&gt; InvoiceEmailed &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;of&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; EmailReceipt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span&gt; PaymentReceived &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;of&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Payment&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span&gt; InvoiceFinalized&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;    &#x2F;&#x2F; TypeShape&amp;#39;s UnionEncoder, which does the codec generation, insists on this marker interface being present&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;    &#x2F;&#x2F; to remind us that this is a long term storage contract that needs to be versionable&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    interface&lt;&#x2F;span&gt;&lt;span&gt; TypeShape.UnionContract.IUnionContract&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; codec&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; FsCodec.SystemTextJson.Codec.Create&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span&gt;Event&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;&amp;gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h1 id=&quot;the-fold&quot;&gt;The Fold&lt;&#x2F;h1&gt;
&lt;blockquote&gt;
&lt;p&gt;Current state is a left &lt;strong&gt;fold&lt;&#x2F;strong&gt; of previous events&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;What exactly does the above quote mean? The concept of folding has different names in different languages.
You may have heard a JavaScripter talk about &lt;code&gt;reduce&lt;&#x2F;code&gt; and reducers. A C# practitioner might talk about
calling &lt;code&gt;Aggregate&lt;&#x2F;code&gt; on an &lt;code&gt;IEnumerable&lt;&#x2F;code&gt;. In F#, we call it &lt;code&gt;fold&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In Equinox parlance, the Fold is a module responsible for defining the state and how it evolves in response to events.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;fsharp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;module&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; Fold &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  open&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; Events&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; InvoiceState&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    {&lt;&#x2F;span&gt;&lt;span&gt; Amount&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; decimal&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      InvoiceNumber&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; int&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      Payer&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; string&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      EmailedTo&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Set&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      Payments&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Set&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      AmountPaid&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; decimal &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; State&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span&gt; Initial&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span&gt; Raised &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;of&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; InvoiceState&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span&gt; Finalized &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;of&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; InvoiceState&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; initial&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; Initial&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; evolve&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; state event &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    match&lt;&#x2F;span&gt;&lt;span&gt; state &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;with&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span&gt; Initial &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      match&lt;&#x2F;span&gt;&lt;span&gt; event &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;with&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      |&lt;&#x2F;span&gt;&lt;span&gt; InvoiceRaised data &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        Raised&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;          {&lt;&#x2F;span&gt;&lt;span&gt; Amount &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; data.Amount&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            InvoiceNumber &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; data.InvoiceNumber&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            Payer &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; data.Payer&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            EmailedTo &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Set.empty&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            Payments &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Set.empty&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            AmountPaid &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;m &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;      &#x2F;&#x2F; We&amp;#39;re guaranteed to not have two InvoiceRaised events and that it is the first event in the stream&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      |&lt;&#x2F;span&gt;&lt;span&gt; e &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; failwithf &lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;quot;Unexpected &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;%A&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt; e&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span&gt; Raised state &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      match&lt;&#x2F;span&gt;&lt;span&gt; event &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;with&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      |&lt;&#x2F;span&gt;&lt;span&gt; InvoiceRaised &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;_ as&lt;&#x2F;span&gt;&lt;span&gt; e &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; failwithf &lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;quot;Unexpected &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;%A&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt; e&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      |&lt;&#x2F;span&gt;&lt;span&gt; InvoiceEmailed r &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Raised &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt; state &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;with&lt;&#x2F;span&gt;&lt;span&gt; EmailedTo &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; state.EmailedTo &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;|&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Set.add r.Recipient &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      |&lt;&#x2F;span&gt;&lt;span&gt; PaymentReceived p &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        Raised&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;          {&lt;&#x2F;span&gt;&lt;span&gt; state &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;with&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              AmountPaid &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; state.AmountPaid &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span&gt; p.Amount&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              Payments &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; state.Payments &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;|&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Set.add p.PaymentId &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      |&lt;&#x2F;span&gt;&lt;span&gt; InvoiceFinalized &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Finalized state&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;    &#x2F;&#x2F; A Finalized invoice is terminal. No further events should be appended&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span&gt; Finalized &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;_ -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; failwithf &lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;quot;Unexpected &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;%A&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt; event&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; fold&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; State &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Event seq &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; State &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Seq.fold evolve&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h1 id=&quot;the-decisions&quot;&gt;The Decisions&lt;&#x2F;h1&gt;
&lt;p&gt;The Command pattern can be considered the standard way to implement decisions. Create a &lt;code&gt;Command&lt;&#x2F;code&gt; DU, and a &lt;code&gt;decide : Command -&amp;gt; State -&amp;gt; Event list&lt;&#x2F;code&gt;
function. This pattern has benefits in that it forces you to enumerate all the different actions you&#x27;d like to take on the state in a single place.
In practice, I&#x27;ve found the pattern more of a hindrance than a help and usually end up refactoring it away in favour of a &lt;code&gt;Decisions&lt;&#x2F;code&gt; module.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;fsharp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;module&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; Decisions &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; raiseInvoice&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; data state &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    match&lt;&#x2F;span&gt;&lt;span&gt; state &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;with&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span&gt; Fold.Initial &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;-&amp;gt; [&lt;&#x2F;span&gt;&lt;span&gt; Events.InvoiceRaised data &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;    &#x2F;&#x2F; This is known as an idempotency check. We could be receiving the same&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;    &#x2F;&#x2F; command due to a retry, in which case it is not considered a failure&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;    &#x2F;&#x2F; since the Fold will already be in the state that this command should put it in&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span&gt; Fold.Raised state &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;when&lt;&#x2F;span&gt;&lt;span&gt; state.Amount &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; data.Amount &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;&amp;amp;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt; state.Payer &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; data.Payer &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;-&amp;gt; []&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span&gt; Fold.Raised &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;_ -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; failwith &lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;quot;Invoice is already raised&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span&gt; Fold.Finalized &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;_ -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; failwith &lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;quot;Invoice is finalized&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  let private&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; hasSentEmailToRecipient&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; recipient &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Fold.InvoiceState&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;) =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    state.EmailedTo &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;|&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Set.contains recipient&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; recordEmailReceipt&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Events.EmailReceipt&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; state &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    match&lt;&#x2F;span&gt;&lt;span&gt; state &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;with&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span&gt; Fold.Raised state &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;when not (&lt;&#x2F;span&gt;&lt;span&gt;hasSentEmailToRecipient data.Recipient state&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;) -&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      [&lt;&#x2F;span&gt;&lt;span&gt; Events.InvoiceEmailed data &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span&gt; Fold.Raised &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;_ -&amp;gt; []&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span&gt; Fold.Initial &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; failwith &lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;quot;Invoice not found&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span&gt; Fold.Finalized &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;_ -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; failwith &lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;quot;Invoice is finalized&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; recordPayment&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Events.Payment&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; state &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    match&lt;&#x2F;span&gt;&lt;span&gt; state &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;with&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span&gt; Fold.Raised state &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;when&lt;&#x2F;span&gt;&lt;span&gt; state.Payments &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;|&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Set.contains data.PaymentId &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;-&amp;gt; []&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span&gt; Fold.Raised &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;_ -&amp;gt; [&lt;&#x2F;span&gt;&lt;span&gt; Events.PaymentReceived data &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span&gt; Fold.Finalized &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;_ -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; failwith &lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;quot;Invoice is finalized&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span&gt; Fold.Initial &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; failwith &lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;quot;Invoice not found&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; finalize&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; state &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    match&lt;&#x2F;span&gt;&lt;span&gt; state &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;with&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span&gt; Fold.Finalized &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;_ -&amp;gt; []&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span&gt; Fold.Raised &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;_ -&amp;gt; [&lt;&#x2F;span&gt;&lt;span&gt; Events.InvoiceFinalized &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span&gt; Fold.Initial &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; failwith &lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;quot;Invoice not found&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h1 id=&quot;testing&quot;&gt;Testing&lt;&#x2F;h1&gt;
&lt;p&gt;Everything we&#x27;ve written so far is pure and easily testable. For an exceptional testing
experience, I recommend using &lt;code&gt;FsCheck&lt;&#x2F;code&gt;, &lt;code&gt;FsCheck.Xunit&lt;&#x2F;code&gt;, and &lt;code&gt;Unquote&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;An important principle when testing event-sourced applications is to not assert against the state of a decider.
Doing so leads to brittle tests and hampers your ability to evolve the state as time goes on.
It is generally advisable to write tests in the form of&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Given these events have occured&lt;br&#x2F;&gt;
When I interpret this command&lt;br&#x2F;&gt;
Then I expect these new events&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;I commonly write an operator &lt;code&gt;(=&amp;gt;)&lt;&#x2F;code&gt; to encapsulate this such that the tests read as&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;previous events =&amp;gt; command = new events&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;fsharp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;module&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; Tests&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;open&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; Invoice&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;open&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; Swensen.Unquote&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;open&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; FsCheck.Xunit&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;open&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; FsCodec.Core&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt;[&amp;lt;Property&amp;gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; ``The event codec round&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;trips cleanly`` event &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; encoded&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; Events.codec.Encode&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;((),&lt;&#x2F;span&gt;&lt;span&gt; event&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; saved&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; TimelineEvent.Create&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;L&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; encoded.EventType&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; encoded.Data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; decoded&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; Events.codec.TryDecode&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;saved&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  test &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;&amp;lt;@&lt;&#x2F;span&gt;&lt;span&gt; ValueSome event &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; decoded &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;@&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;let (=&amp;gt;)&lt;&#x2F;span&gt;&lt;span&gt; events interpret &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Fold.fold Fold.initial events &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;|&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; interpret&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;open&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; Events&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt;[&amp;lt;Property&amp;gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; ``Raising an invoice`` data &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  test &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;&amp;lt;@ [] =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Decisions.raiseInvoice data &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;= [&lt;&#x2F;span&gt;&lt;span&gt; InvoiceRaised data &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;] @&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;  &#x2F;&#x2F; test the idempotency&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  test &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;&amp;lt;@ [&lt;&#x2F;span&gt;&lt;span&gt; InvoiceRaised data &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;] =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Decisions.raiseInvoice data &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;= [] @&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;  &#x2F;&#x2F; A finalized invoice will throw&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  raises &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;&amp;lt;@ [&lt;&#x2F;span&gt;&lt;span&gt; InvoiceRaised data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;;&lt;&#x2F;span&gt;&lt;span&gt; InvoiceFinalized &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;] =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Decisions.raiseInvoice data &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;@&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt;[&amp;lt;Property&amp;gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; ``Recording payments`` raised data &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  raises &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;&amp;lt;@ [] =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Decisions.recordPayment data &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;@&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  test &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;&amp;lt;@ [&lt;&#x2F;span&gt;&lt;span&gt; InvoiceRaised raised &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;] =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Decisions.recordPayment data &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;= [&lt;&#x2F;span&gt;&lt;span&gt; PaymentReceived data &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;] @&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  test &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;&amp;lt;@ [&lt;&#x2F;span&gt;&lt;span&gt; InvoiceRaised raised&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;;&lt;&#x2F;span&gt;&lt;span&gt; PaymentReceived data &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;] =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Decisions.recordPayment data &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;= [] @&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  raises &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;&amp;lt;@ [&lt;&#x2F;span&gt;&lt;span&gt; InvoiceRaised raised&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;;&lt;&#x2F;span&gt;&lt;span&gt; InvoiceFinalized &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;] =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Decisions.recordPayment data &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;@&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt;[&amp;lt;Property&amp;gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; ``Recording email receipts`` raised data &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  raises &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;&amp;lt;@ [] =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Decisions.recordEmailReceipt data &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;@&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  test &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;&amp;lt;@ [&lt;&#x2F;span&gt;&lt;span&gt; InvoiceRaised raised &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;] =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Decisions.recordEmailReceipt data &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;= [&lt;&#x2F;span&gt;&lt;span&gt; InvoiceEmailed data &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;] @&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  test &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;&amp;lt;@ [&lt;&#x2F;span&gt;&lt;span&gt; InvoiceRaised raised&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;;&lt;&#x2F;span&gt;&lt;span&gt; InvoiceEmailed data &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;] =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Decisions.recordEmailReceipt data &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;= [] @&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  raises &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;&amp;lt;@ [&lt;&#x2F;span&gt;&lt;span&gt; InvoiceRaised raised&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;;&lt;&#x2F;span&gt;&lt;span&gt; InvoiceFinalized &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;] =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Decisions.recordEmailReceipt data &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;@&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt;[&amp;lt;Property&amp;gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; ``Finalizing`` raised &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  raises &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;&amp;lt;@ [] =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Decisions.finalize &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;@&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  test &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;&amp;lt;@ [&lt;&#x2F;span&gt;&lt;span&gt; InvoiceRaised raised &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;] =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Decisions.finalize &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;= [&lt;&#x2F;span&gt;&lt;span&gt; InvoiceFinalized &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;] @&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  test &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;&amp;lt;@ [&lt;&#x2F;span&gt;&lt;span&gt; InvoiceRaised raised&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;;&lt;&#x2F;span&gt;&lt;span&gt; InvoiceFinalized &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;] =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Decisions.finalize &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;= [] @&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h1 id=&quot;identity&quot;&gt;Identity&lt;&#x2F;h1&gt;
&lt;p&gt;It is important to note that we have not mentioned identity until this point. There is
no &lt;code&gt;InvoiceId&lt;&#x2F;code&gt; on any of the events! The reason for this is that the aggregate should not be concerned
with its own identity. Identity in the Equinox programming model should be considered an
infrastructural routing concern. In an event-sourced system, we store the events in streams,
and these streams have names of the format &lt;code&gt;{Category}-{streamId}&lt;&#x2F;code&gt;.
The identity of the aggregate lives exclusively within the stream name.&lt;&#x2F;p&gt;
&lt;p&gt;Events without identity may sound strange, but it does make sense. A functional programming idiom is to
&quot;make illegal states unrepresentable.&quot; Imagine for a second a stream with these events:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;StreamName&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; Invoice&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;1234&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;InvoiceRaised&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; InvoiceId&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 5678&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The above stream is an absurdity! To make the illegal state unrepresentable, we must apply the DRY principle.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;That single, unambiguous, authoritative representation is the stream name. Treating identity this way has numerous benefits.
Imagine if you later wanted to run a multi-tenanted version of your system.
You could spin up an event store per tenant or include the tenant&#x27;s ID in the stream name so
they can all share one database. Either way, we would not force the tenant&#x27;s ID on any event,
and the domain logic and schema remain unchanged.&lt;&#x2F;p&gt;
&lt;p&gt;Though identity exists exclusively in the stream name, we would like to avoid passing around unbranded &lt;code&gt;string&lt;&#x2F;code&gt;s and &lt;code&gt;Guid&lt;&#x2F;code&gt;s.
To that end, it is common practice to use &lt;code&gt;FSharp.UMX&lt;&#x2F;code&gt; to create a type-safe, branded identifier type.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;fsharp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;open&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; FSharp.UMX&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;open&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; System&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; InvoiceId&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; Guid&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span&gt;invoiceId&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;and&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; [&amp;lt;Measure&amp;gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; invoiceId&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;module InvoiceId &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  let inline&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; ofGuid&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;g&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Guid&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;) :&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; InvoiceId &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;= %&lt;&#x2F;span&gt;&lt;span&gt;g&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  let inline&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; parse&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;s&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; string&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;) =&lt;&#x2F;span&gt;&lt;span&gt; Guid.Parse s &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;|&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; ofGuid&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  let inline&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; toGuid&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; InvoiceId&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;) :&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Guid &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;= %&lt;&#x2F;span&gt;&lt;span&gt;id&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;  &#x2F;&#x2F; We choose the dashless N format to make the distinct parts of the stream&amp;#39;s ID&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;  &#x2F;&#x2F; easier for humans to read&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  let inline&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; toString&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; InvoiceId&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;) = (&lt;&#x2F;span&gt;&lt;span&gt;toGuid id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span&gt;.ToString&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;quot;N&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Equinox exposes a helper to create well-formed stream IDs that, when used with branded identifiers, can ensure we don&#x27;t
resolve the wrong stream at runtime.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;fsharp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt;[&amp;lt;Literal&amp;gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; Category&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;Invoice&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; streamId&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; Equinox.StreamId.gen InvoiceId.toString &lt;&#x2F;span&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;&#x2F;&#x2F; InvoiceId -&amp;gt; StreamId&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;&#x2F;&#x2F; The example above of adding a tenant to the stream id would look like so:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;&#x2F;&#x2F; let streamId = Equinox.StreamId.gen2 TenantId.toString InvoiceId.toString&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h1 id=&quot;the-service&quot;&gt;The Service&lt;&#x2F;h1&gt;
&lt;p&gt;To recap.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;We&#x27;ve created an &lt;code&gt;Events&lt;&#x2F;code&gt; module defining the events in play and their storage format.&lt;&#x2F;li&gt;
&lt;li&gt;We&#x27;ve created a &lt;code&gt;Fold&lt;&#x2F;code&gt; representing the state and how it evolves in response to events.&lt;&#x2F;li&gt;
&lt;li&gt;We&#x27;ve created a &lt;code&gt;Decisions&lt;&#x2F;code&gt; module exposing the actions we can take on a given invoice.&lt;&#x2F;li&gt;
&lt;li&gt;We&#x27;ve written tests that combine all three into a cohesive whole.&lt;&#x2F;li&gt;
&lt;li&gt;We&#x27;ve created a branded &lt;code&gt;InvoiceId&lt;&#x2F;code&gt; type.&lt;&#x2F;li&gt;
&lt;li&gt;We&#x27;ve created a &lt;code&gt;streamId&lt;&#x2F;code&gt; helper and defined the &lt;code&gt;Category&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;One way to expose the behaviour we&#x27;ve programmed to the rest of the application would be
for consumers to wire it up, similar to how we did in the tests. That
would ultimately be a bad idea. Alternatively, we can represent the operations relevant
to this aggregate as a cohesive Domain Service. The convention in equinox is to call this type &lt;code&gt;Service&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;fsharp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Service&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; internal (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;resolve&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; InvoiceId &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Equinox.Decider&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;Events.Event&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Fold.State&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;&amp;gt;) =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  member&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; _.Raise&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;) =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; decider&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; resolve id&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    decider.Transact&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;Decisions.raiseInvoice data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  member&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; _.RecordEmailReceipt&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;) =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; decider&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; resolve id&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    decider.Transact&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;Decisions.recordEmailReceipt data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  member&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; _.RecordPayment&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;) =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; decider&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; resolve id&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    decider.Transact&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;Decisions.recordPayment data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  member&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; _.Finalize&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;) =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; decider&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; resolve id&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    decider.Transact&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;Decisions.finalize&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;There are a couple of things to call out here.&lt;&#x2F;p&gt;
&lt;p&gt;First, we&#x27;ve made the service&#x27;s constructor &lt;code&gt;internal&lt;&#x2F;code&gt; because we&#x27;ll expose it via a factory later.
Second, this is our first encounter with Equinox&#x27;s Decider concept. This concept encapsulates the retrieving of events,
their fold, and writing to storage. The main API is &lt;code&gt;Transact&lt;&#x2F;code&gt;, which performs the actions necessary to get the current
state of the aggregate, calls the supplied function, and appends the resulting events to the underlying storage.
It does this with multiple layers of retries and can, in addition, do caching and snapshotting transparently.&lt;&#x2F;p&gt;
&lt;p&gt;A simplified version of what Transact does would look something like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;fsharp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;let!&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; version&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; events &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; store.ReadStream&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;streamName&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; newEvents&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; events &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;|&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Seq.choose tryDecode &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;|&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; fold initial &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;|&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; decide&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;do!&lt;&#x2F;span&gt;&lt;span&gt; store.AppendEvents&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;streamName&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; newEvents &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;|&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Seq.map encode&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; version&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;There&#x27;s a second API called &lt;code&gt;Query&lt;&#x2F;code&gt;. This method will load the current state,
call the supplied transformation function and return the result. It is considered a bad idea to expose
anything inside the &lt;code&gt;Fold&lt;&#x2F;code&gt; module to the outside. Therefore, for querying purposes, we&#x27;ll add an &lt;code&gt;InvoiceModel&lt;&#x2F;code&gt; type.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;fsharp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; InvoiceModel&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  {&lt;&#x2F;span&gt;&lt;span&gt; InvoiceNumber&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; int&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    Amount&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; decimal&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    Payer&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; string&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    EmailedTo&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; string&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;[]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    Finalized&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; bool &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;module&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; InvoiceModel &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; fromState&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; finalized &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Fold.InvoiceState&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;) =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    {&lt;&#x2F;span&gt;&lt;span&gt; InvoiceNumber &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; state.InvoiceNumber&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      Amount &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; state.Amount&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      Payer &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; state.Payer&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      EmailedTo &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; state.EmailedTo &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;|&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Set.toArray&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      Finalized &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; finalized &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;module&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; Queries &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; summary&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    function&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span&gt; Fold.Initial &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; None&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span&gt; Fold.Raised invoice &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Some&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;InvoiceModel.fromState &lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt; invoice&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span&gt; Fold.Finalized invoice &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Some&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;InvoiceModel.fromState &lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt; invoice&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;With this in place, we can update our service to expose the current state of the aggregate.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;fsharp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Service&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; internal (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;resolve&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; InvoiceId &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Equinox.Decider&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;Events.Event&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Fold.State&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;&amp;gt;) =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  member&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; _.Raise&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;) =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; decider&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; resolve id&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    decider.Transact&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;Decisions.raiseInvoice data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  member&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; _.RecordEmailReceipt&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;) =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; decider&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; resolve id&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    decider.Transact&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;Decisions.recordEmailReceipt data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  member&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; _.RecordPayment&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;) =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; decider&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; resolve id&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    decider.Transact&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;Decisions.recordPayment data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  member&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; _.Finalize&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;) =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; decider&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; resolve id&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    decider.Transact&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;Decisions.finalize&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  member&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; _.ReadInvoice&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;) =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; decider&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; resolve id&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    decider.Query&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;Queries.summary&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Because of the aforementioned internal constructor, we&#x27;ll need to create a factory to expose our service to the outside.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;fsharp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; create&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; resolve &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Service&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;streamId &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;&amp;gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; resolve Category&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This code looks confusing at first glance, so I think it&#x27;s worth spelling out what&#x27;s going on.&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;The &lt;code&gt;create&lt;&#x2F;code&gt; function receives a &lt;code&gt;resolve: string -&amp;gt; StreamId -&amp;gt; Decider&amp;lt;_, _&amp;gt;&lt;&#x2F;code&gt; where the first argument is the Category&lt;&#x2F;li&gt;
&lt;li&gt;The Service expects a &lt;code&gt;resolve&lt;&#x2F;code&gt; function of type &lt;code&gt;InvoiceId -&amp;gt; Decider&amp;lt;_, _&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;To get from the first to the second type, we must create a third function&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;fsharp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; resolveInvoiceDecider&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; resolve invoiceId &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; streamId invoiceId&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  resolve Category id&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;&#x2F;&#x2F; this is the same as&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; resolveInvoiceDecider&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; resolve invoiceId &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  streamId invoiceId &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;|&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; resolve Category&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;&#x2F;&#x2F; which in turn is the same as&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; resolveInvoiceDecider&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; resolve &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  streamId &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;&amp;gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; resolve Category&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;&#x2F;&#x2F; we could use it as such&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; create&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; resolve &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Service&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;resolveInvoiceDecider resolve&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;&#x2F;&#x2F; but the preference is to inline it&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; create&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; resolve &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Service&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;streamId &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;&amp;gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; resolve Category&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h1 id=&quot;the-api&quot;&gt;The API&lt;&#x2F;h1&gt;
&lt;p&gt;To use the &lt;code&gt;Service&lt;&#x2F;code&gt;, we&#x27;ll finally need to wire it up to one of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jet&#x2F;equinox#currently-supported-data-stores&quot;&gt;Equinox&#x27;s many available stores&lt;&#x2F;a&gt;.
For this illustration, We&#x27;ll use &lt;code&gt;Equinox.MessageDb&lt;&#x2F;code&gt; as the backing store.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;fsharp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;module&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; Program&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;open&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; System&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;open&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; Equinox.MessageDb&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;open&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; Serilog&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;module&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; Environment &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; tryGetEnv&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; Environment.GetEnvironmentVariable &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;&amp;gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Option.ofObj&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; log&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; LoggerConfiguration&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;()&lt;&#x2F;span&gt;&lt;span&gt;.WriteTo.Console&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;()&lt;&#x2F;span&gt;&lt;span&gt;.CreateLogger&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; cache&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; Equinox.Cache&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;quot;test&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; sizeMb &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 50&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; defaultConnString&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;  &amp;quot;Host=localhost; Database=message_store; Username=message_store&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; writeUrl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Environment.tryGetEnv &lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;quot;MESSAGE_DB_URL&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; |&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Option.defaultValue defaultConnString&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; readUrl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Environment.tryGetEnv &lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;quot;MESSAGE_DB_REPLICA_URL&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; |&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Option.defaultValue writeUrl&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; connection&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; MessageDbConnector&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;writeUrl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; readUrl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span&gt;.Establish&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; context&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; MessageDbContext&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;connection&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; caching&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; CachingStrategy.SlidingWindow&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;cache&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; TimeSpan.FromMinutes&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; service&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  MessageDbCategory&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;context&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; Invoice.Events.codec&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; Invoice.Fold.fold&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; Invoice.Fold.initial&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; caching&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  |&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Equinox.Decider.resolve log&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  |&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Invoice.create&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;With the service in place, we can finally expose it over the wire. To achieve this, we&#x27;ll use a Minimal API.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;fsharp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;open&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; Microsoft.AspNetCore.Builder&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;open&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; Microsoft.Extensions.Hosting&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; builder&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; WebApplication.CreateBuilder&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; app&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; builder.Build&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; raiseInvoice&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; body &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  task {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; Guid.NewGuid&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;() |&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Invoice.InvoiceId.ofGuid&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    do!&lt;&#x2F;span&gt;&lt;span&gt; service.Raise&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; body&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span&gt; id&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;app.MapPost&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;quot;&#x2F;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; Func&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;&amp;lt;_, _&amp;gt;(&lt;&#x2F;span&gt;&lt;span&gt;raiseInvoice&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;)) |&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; ignore&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; finalizeInvoice&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; id &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  task {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    do!&lt;&#x2F;span&gt;&lt;span&gt; service.Finalize&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;OK&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;app.MapPost&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;quot;&#x2F;{id}&#x2F;finalize&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; Func&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;&amp;lt;_, _&amp;gt;(&lt;&#x2F;span&gt;&lt;span&gt;finalizeInvoice&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;)) |&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; ignore&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; recordPayment&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; id payment &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  task {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    do!&lt;&#x2F;span&gt;&lt;span&gt; service.RecordPayment&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; payment&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;OK&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;app.MapPost&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;quot;&#x2F;{id}&#x2F;record-payment&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; Func&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;&amp;lt;_, _, _&amp;gt;(&lt;&#x2F;span&gt;&lt;span&gt;recordPayment&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;)) |&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; ignore&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; readInvoice&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; id &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  task { return!&lt;&#x2F;span&gt;&lt;span&gt; service.ReadInvoice&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;) }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;app.MapGet&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;quot;&#x2F;{id}&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; Func&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;&amp;lt;_, _&amp;gt;(&lt;&#x2F;span&gt;&lt;span&gt;readInvoice&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;)) |&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; ignore&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;app.Run&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You should now be able to run the API&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; curl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; -XPOST&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; localhost:5244&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; -H&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;Content-Type: application&#x2F;json&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; --data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;{&amp;quot;InvoiceNumber&amp;quot;: 1, &amp;quot;Payer&amp;quot;: &amp;quot;1&amp;quot;, &amp;quot;amount&amp;quot;: 1230}&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;&amp;quot;932c5ca2-3870-468d-85c9-5e5f406d5c7d&amp;quot;⏎&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; curl localhost:5244&#x2F;932c5ca2-3870-468d-85c9-5e5f406d5c7d&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;&amp;quot;invoiceNumber&amp;quot;:1,&amp;quot;amount&amp;quot;:1230,&amp;quot;payer&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;&amp;quot;1&amp;quot;,&amp;quot;emailedTo&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;[],&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;&amp;quot;finalized&amp;quot;:false}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; curl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; -XPOST&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; localhost:5244&#x2F;932c5ca2-3870-468d-85c9-5e5f406d5c7d&#x2F;finalize&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;OK&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; curl localhost:5244&#x2F;f3bec945-2cbd-4a57-84fa-16481d394490&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;&amp;quot;amount&amp;quot;:1230,&amp;quot;invoiceNumber&amp;quot;:1,&amp;quot;emailedTo&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;[],&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;&amp;quot;amountPaid&amp;quot;:0,&amp;quot;finalized&amp;quot;:true}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h1&gt;
&lt;p&gt;We&#x27;ve reached the end of our journey. The programming model described in this article is
one with a heavy emphasis on composition. We compose larger behaviours from smaller ones.
I hope that through this post, I&#x27;ve demonstrated its merits.&lt;&#x2F;p&gt;
&lt;p&gt;Part two of this series will focus on Reactions. We&#x27;ll set up a reactor that, when an invoice
is raised, will reserve a unique invoice number for it and another reactor that, when an invoice
is raised and numbered, will send it to the payer via email and record the receipt.&lt;&#x2F;p&gt;
&lt;!-- Big thanks to [@bartelink](https:&#x2F;&#x2F;github.com&#x2F;bartelink) for his help with this post;
his feedback was instrumental in getting this post written and submitted to the F# Advent.
He preferred not to be credited, so instead of you reading this in the body of the article,
you get to read it in an HTML comment. Why did you even look at the page source? --&gt;
&lt;p&gt;If you&#x27;d like to see the code in a runnable form, I&#x27;ve pushed it to a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nordfjord&#x2F;minimal-equinox&quot;&gt;GitHub repository&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>The Tyranny of Constructors</title>
          <pubDate>Sun, 24 Jul 2022 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://nordfjord.io/the-tyranny-of-constructors/</link>
          <guid>https://nordfjord.io/the-tyranny-of-constructors/</guid>
          <description xml:base="https://nordfjord.io/the-tyranny-of-constructors/">&lt;p&gt;The tyranny of constructors is what happens when you create collaborators inside object constructors.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; UserRepository&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  constructor&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;connectionString&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;    this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;pool&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; Pool&lt;&#x2F;span&gt;&lt;span&gt;({&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;connectionString&lt;&#x2F;span&gt;&lt;span&gt;})&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  async&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; getAllUsers&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    try&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; client&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;pool&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;connect&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; result&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; client&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;query&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;`select * from users`&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; result&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;rows&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;User&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;ofDatabaseRow&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; finally&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;      client&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;release&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This class has too many responsibilities. It is responsible for:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Constructing and managing a database pool&lt;&#x2F;li&gt;
&lt;li&gt;Managing a connection from the pool&lt;&#x2F;li&gt;
&lt;li&gt;Issuing the right query to the database connection&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;In reality it should only be responsible for the last item. Everything else is a distraction
for the class which makes it less testable, and more error prone.&lt;&#x2F;p&gt;
&lt;p&gt;let&#x27;s see what it looks like when we relieve it of these responsibilities&lt;&#x2F;p&gt;
&lt;h1 id=&quot;constructing-and-managing-a-pool&quot;&gt;Constructing and managing a pool&lt;&#x2F;h1&gt;
&lt;p&gt;When we create collaborators inside our constructor we sacrifice a lot. We can no longer substitute
the pool for a different object, we&#x27;re locked to a specific concrete implementation of the pool, etc.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; UserRepository&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  constructor&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;private readonly&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; pool&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Pool&lt;&#x2F;span&gt;&lt;span&gt;) {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  async&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; getAllUsers&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    try&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; client&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;pool&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;connect&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; result&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; client&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;query&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;`select * from users`&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; result&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;rows&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;User&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;ofDatabaseRow&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; finally&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;      client&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;release&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;With this change we can now construct our pool as a singleton for our application.
This in turn means we can share that pool across objects. Our startup file might now look like:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;export async function&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; main&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; config&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; readConfig&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; pool&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; Pool&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;config&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;pool&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; userService&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; UserRepository&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;pool&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; todoService&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; TodoService&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;pool&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;  &#x2F;&#x2F; ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;  app&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;start&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h1 id=&quot;managing-a-client-from-the-pool&quot;&gt;Managing a client from the pool&lt;&#x2F;h1&gt;
&lt;p&gt;The logic of getting a client connection with &lt;code&gt;pool.connect&lt;&#x2F;code&gt; and subsequently releasing it after use with &lt;code&gt;client.release&lt;&#x2F;code&gt;
is something we&#x27;ll likely want to do in a number of places. It really deserves its own object.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; UserRepository&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  constructor&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;private readonly&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; connection&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; IConnection&lt;&#x2F;span&gt;&lt;span&gt;) {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  async&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; getAllUsers&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; rows&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;connection&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;query&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;`select * from users`&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; rows&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;User&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;ofDatabaseRow&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;interface&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; IConnection&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;  query&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; values&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;?&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; any&lt;&#x2F;span&gt;&lt;span&gt;[]):&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Promise&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;any&lt;&#x2F;span&gt;&lt;span&gt;[]&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Connection&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; implements&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; IConnection&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  constructor&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;private readonly&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; pool&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Pool&lt;&#x2F;span&gt;&lt;span&gt;) {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  async&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; query&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; values&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;?&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; any&lt;&#x2F;span&gt;&lt;span&gt;[]) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    try&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; client&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;pool&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;connect&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; result&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; client&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;query&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; values&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; result&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;rows&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; finally&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;      client&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;release&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Where we used to have one object we now have two, and each object has a more defined responsibility.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;takeaway&quot;&gt;Takeaway&lt;&#x2F;h1&gt;
&lt;p&gt;Constructing collaborators in constructors is a fairly big code smell.
&lt;strong&gt;Do not&lt;&#x2F;strong&gt; mix object graph construction with your application logic.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;addendum&quot;&gt;Addendum&lt;&#x2F;h1&gt;
&lt;p&gt;An additional rule that is good to follow is&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Every object passed into a constructor should be fully initialized.&quot;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;async function&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; configure&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; config&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; readConfig&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; client&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; Client&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;config&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;client&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; client&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;initialize&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; connection&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; Connection&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;client&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; userService&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; UserService&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;connection&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;  &#x2F;&#x2F; ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;</description>
      </item>
      <item>
          <title>SQL Event Store. Maybe Yes?</title>
          <pubDate>Thu, 14 Jul 2022 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://nordfjord.io/sql-event-store-maybe-yes/</link>
          <guid>https://nordfjord.io/sql-event-store-maybe-yes/</guid>
          <description xml:base="https://nordfjord.io/sql-event-store-maybe-yes/">&lt;p&gt;Last year I published a post about how using a SQL based event store
might not be the best idea. My argument in that post boils down to two things.&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Building an event-store on top of a SQL database isn&#x27;t as easy as you&#x27;d think&lt;&#x2F;li&gt;
&lt;li&gt;Because of the above you should consider using a purpose built tool&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;What the post did not do a good job of was elaborating on the trade-offs of
using a purpose built event store. Just like building an event store on SQL isn&#x27;t easy,
using a purpose built event store as your source of truth datastore comes with some
interesting challenges. This post is an exploration of those challenges.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;terminology&quot;&gt;Terminology&lt;&#x2F;h1&gt;
&lt;p&gt;Before we go further it&#x27;s a good idea to get some terminology out of the way to make sure we&#x27;re speaking the
same language.&lt;&#x2F;p&gt;
&lt;dl&gt;
  &lt;dt&gt;&lt;strong&gt;Stream&lt;&#x2F;strong&gt;&lt;&#x2F;dt&gt;
  &lt;dd&gt;A sequence of ordered events, typically for a single process or entity e.g. &lt;code&gt;User-1&lt;&#x2F;code&gt;&lt;&#x2F;dd&gt;
  &lt;dt&gt;&lt;strong&gt;Category&lt;&#x2F;strong&gt;&lt;&#x2F;dt&gt;
  &lt;dd&gt;A grouping of related streams e.g. &lt;code&gt;User-1&lt;&#x2F;code&gt; belongs to the &lt;code&gt;User&lt;&#x2F;code&gt; category&lt;&#x2F;dd&gt;
  &lt;dt&gt;&lt;strong&gt;Optimistic Concurrency Control&lt;&#x2F;strong&gt;&lt;&#x2F;dt&gt;
  &lt;dd&gt;The ability to write to a stream &lt;em&gt;if and only if&lt;&#x2F;em&gt; it&#x27;s last event has a certain version&lt;&#x2F;dd&gt;
&lt;&#x2F;dl&gt;
&lt;h1 id=&quot;what-is-a-purpose-built-event-store&quot;&gt;What is a purpose built event store?&lt;&#x2F;h1&gt;
&lt;p&gt;Yves Lorphelin has created a fantastic enumeration of
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ylorph&#x2F;RandomThoughts&#x2F;blob&#x2F;master&#x2F;2019.08.09_expectations_for_an_event_store.md&quot;&gt;expected APIs for event stores&lt;&#x2F;a&gt;.
I&#x27;ll however say that in most scenarios you really only need three APIs.&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Appending events to a stream with optimistic concurrency control&lt;&#x2F;li&gt;
&lt;li&gt;Reading a stream&lt;&#x2F;li&gt;
&lt;li&gt;Reading across streams (on a per-category basis)&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;On top of these basics a purpose built event store like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;eventstore.com&quot;&gt;EventStoreDB&lt;&#x2F;a&gt; will give
you two killer features:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Real-time streaming as a first class citizen&lt;&#x2F;li&gt;
&lt;li&gt;A globally ordered $all stream&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h1 id=&quot;so-why-would-i-consider-not-using-a-purpose-built-event-store&quot;&gt;So why would I consider NOT using a purpose built event store?&lt;&#x2F;h1&gt;
&lt;p&gt;I think you&#x27;d be wise to consider at least two challenges&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Your cloud provider doesn&#x27;t have a managed offering&lt;&#x2F;li&gt;
&lt;li&gt;You might not need real-time streaming&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h1 id=&quot;your-cloud-provider-doesn-t-have-a-managed-offering&quot;&gt;Your cloud provider doesn&#x27;t have a managed offering&lt;&#x2F;h1&gt;
&lt;p&gt;If you&#x27;re building something, the last thing you want is more hardware&#x2F;infrastructure to manage. Most cloud providers
however do not have a cloud-native event store offering. This in turn means you can either look for a third party to
manage your event store, or manage it yourself.&lt;&#x2F;p&gt;
&lt;p&gt;Contrast this with the ubiquity of managed and cloud native PostgreSQL offerings. AWS Aurora, as an example,
is fully compatible with the postgres API and will likely cost you far less than a managed event store.
It has a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;aws.amazon.com&#x2F;rds&#x2F;aurora&#x2F;sla&#x2F;&quot;&gt;99.99% uptime SLA&lt;&#x2F;a&gt;, offers easy read-replicas, backups, and more.&lt;&#x2F;p&gt;
&lt;p&gt;Managed EventStoreDB on the other hand will give you a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.eventstore.com&#x2F;cloud-services-service-level-agreement&quot;&gt;99.5% uptime SLA&lt;&#x2F;a&gt;.
That&#x27;s almost 2 days of downtime a year! The last thing you want for your primary datastore is a system that is unreliable.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;you-might-not-need-real-time-streaming&quot;&gt;You might not need real-time streaming&lt;&#x2F;h1&gt;
&lt;p&gt;While real-time streaming sounds like an absolute must-have it comes with a set of challenges. Firstly it tends to be
push based. Push based systems are inherently less reliable than pull based systems.&lt;&#x2F;p&gt;
&lt;p&gt;The way these push-based real-time subscriptions work relies on having a connection open to the event store at any
given time. Because these connections are long-lived and the protocol might not be the simplest, the job of handling
network failures ends up in your application layer.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;what-happens-if-the-node-you-re-connected-to-goes-down-or-there-s-a-network-failure&quot;&gt;What happens if the node you&#x27;re connected to goes down, or there&#x27;s a network failure?&lt;&#x2F;h4&gt;
&lt;p&gt;In these cases your connection should drop. Maybe you had the foresight to implement a retry, so your application
will attempt to open a new connection subscribing from your current checkpoint. You may also get into a situation
where your application will never be able to connect to the event-store again. As an example, Alpine linux has some well
known issues with how DNS is handled, sometimes it&#x27;s better to let your process crash and leave it to your orchestrator
to do the work of recycling it.&lt;&#x2F;p&gt;
&lt;p&gt;A popular approach to handling network failures these days is to employ side-car proxies like Istio. Istio is a
man-in-the-middle attack you perpetrate on yourself because it promises you nice things like &quot;solving all your network issues.&quot;
In practice, it tends to break your network in very subtle ways. It tries to be &quot;smart&quot; so your application doesn&#x27;t have
to be. One way it tries to be helpful is by handling retries on your behalf. In reality this means your application won&#x27;t
know that the event store is down, from the application&#x27;s perspective it&#x27;s just taking a really, really long time to respond.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;takeaways&quot;&gt;Takeaways&lt;&#x2F;h1&gt;
&lt;p&gt;Using a purpose built event store is no bed of roses. All decisions have trade-offs. In many cases starting your journey
out on a SQL database is the right decision. It means fewer moving parts, and less time spent on maintaining infrastructure.
The ubiquity of relational databases has meant a lot of innovative things have popped up around them. These days if you
need real-time push-based streaming from Postgres then &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;superbase.com&quot;&gt;Superbase&lt;&#x2F;a&gt; has got you covered. Migrating
relational databases has long been a risky and hard endeavour, but services like Amazon&#x27;s &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;aws.amazon.com&#x2F;dms&quot;&gt;DMS&lt;&#x2F;a&gt;
have cropped up promising to take much of that burden away from you. The list goes on and on.&lt;&#x2F;p&gt;
&lt;p&gt;Ultimately, SQL gives you options, and options are something we shouldn&#x27;t throw away early in our journey.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>SQL Event Store. Maybe Not.</title>
          <pubDate>Sun, 21 Mar 2021 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://nordfjord.io/sql-event-store-maybe-not/</link>
          <guid>https://nordfjord.io/sql-event-store-maybe-not/</guid>
          <description xml:base="https://nordfjord.io/sql-event-store-maybe-not/">&lt;p&gt;Scenario: You just convinced management that event-sourcing is the way to go. One thing they were adamant about was that
you should not use a purpose built event-store.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;There&#x27;s broad knowledge of PostgreSQL in the organzation that we would be fools not to leverage!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Let&#x27;s run through this hypothetical yet typical scenario together.&lt;&#x2F;p&gt;
&lt;p&gt;The first step is usually adding an &quot;events&quot; table to your schema.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;CREATE TABLE&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; events&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    id uuid &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;PRIMARY KEY NOT NULL&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    data&lt;&#x2F;span&gt;&lt;span&gt; jsonb &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;NOT NULL&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That should do the trick! You&#x27;ve got a primary key for that sweet, sweet indexing for when you just need to grab that single message,
and since Postgres has jsonb, you can query your events six ways to Sunday.&lt;&#x2F;p&gt;
&lt;p&gt;With this infrastructure in place you decide to dabble with a single entity at first, so why not the users?&lt;&#x2F;p&gt;
&lt;p&gt;To make matters even simpler you decide to do away with eventual consistency by writing your events and projections
in a single transaction.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Eventual Consistency: The notion that if left alone to do its job for once, your system will become consistent... eventually&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;&#x2F;&#x2F; we assume the client we&amp;#39;re handed has started a transaction already&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;async function&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; confirmUserEmail&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;command&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; client&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;  &#x2F;&#x2F; write an event&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; client&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;query&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;`INSERT INTO events(id, data) VALUES ($1, $2)`&lt;&#x2F;span&gt;&lt;span&gt;, [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;    uuid&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;v4&lt;&#x2F;span&gt;&lt;span&gt;(),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;      type&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;UserConfirmedEmail&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;      email&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; command&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;      userId&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; command&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;userId&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  ])&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;  &#x2F;&#x2F; grab all events relating to the user&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; userEvents&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; client&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;query&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;    `SELECT * FROM events WHERE data -&amp;gt;&amp;gt; &amp;#39;userId&amp;#39; = $1`&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;command&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;userId&lt;&#x2F;span&gt;&lt;span&gt;],&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;  &#x2F;&#x2F; current state is a left fold of previous events&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; userState&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; userEvents&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;reduce&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;userReducer&lt;&#x2F;span&gt;&lt;span&gt;, {})&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;  &#x2F;&#x2F; update the projection&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; client&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;query&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;    `INSERT INTO users(id, email, email_confirmed)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;     VALUES ($1, $2, $3)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;     ON CONFLICT (id) DO UPDATE&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;     SET email = $2, email_confirmed = $3;`&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;userState&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; userState&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; userState&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;emailConfirmed&lt;&#x2F;span&gt;&lt;span&gt;],&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This will work fine for a long time, but it turns out that it&#x27;s really important that when you read events, you &lt;em&gt;always&lt;&#x2F;em&gt; get them in the same order.  &lt;em&gt;Always!&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Your table however doesn&#x27;t guarantee this. It&#x27;s time for an upgrade!&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;ALTER TABLE&lt;&#x2F;span&gt;&lt;span&gt; events &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;DROP CONSTRAINT&lt;&#x2F;span&gt;&lt;span&gt; events_pkey;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;ALTER TABLE&lt;&#x2F;span&gt;&lt;span&gt; events &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;ADD&lt;&#x2F;span&gt;&lt;span&gt; COLUMN position &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;bigserial NOT NULL&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;ALTER TABLE&lt;&#x2F;span&gt;&lt;span&gt; events &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;ADD PRIMARY KEY&lt;&#x2F;span&gt;&lt;span&gt; (position);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;CREATE UNIQUE INDEX&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; events_id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; ON&lt;&#x2F;span&gt;&lt;span&gt; events (id);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;async function&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; confirmUserEmail&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;command&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; client&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; client&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;query&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;`INSERT INTO events(id, data) VALUES ($1, $2)`&lt;&#x2F;span&gt;&lt;span&gt;, [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;    uuid&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;v4&lt;&#x2F;span&gt;&lt;span&gt;(),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;      type&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;UserConfirmedEmail&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;      email&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; command&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;      userId&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; command&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;userId&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  ])&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; userEvents&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; client&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;query&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;    `SELECT * FROM events WHERE data -&amp;gt;&amp;gt; &amp;#39;userId&amp;#39; = $1 ORDER BY position ASC`&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;command&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;userId&lt;&#x2F;span&gt;&lt;span&gt;],&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;  &#x2F;&#x2F; current state is a left fold of previous events&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; userState&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; userEvents&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;reduce&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;userReducer&lt;&#x2F;span&gt;&lt;span&gt;, {})&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; client&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;query&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;    `INSERT INTO users(id, email, email_confirmed)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;     VALUES ($1, $2, $3)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;     ON CONFLICT (id) DO UPDATE&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;     SET email = $2, email_confirmed = $3;`&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;userState&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; userState&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; userState&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;emailConfirmed&lt;&#x2F;span&gt;&lt;span&gt;],&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Great! You now have a repeatable order. A few (tens of) thousand events later your projections are slowing down.
You leverage your broad knowledge of relational databases and add an index!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;sql-store&#x2F;index-all-the-things.jpeg&quot; alt=&quot;Index All The Things&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;CREATE INDEX&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; idx_events_user_id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; ON&lt;&#x2F;span&gt;&lt;span&gt; events ((&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt;&amp;gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;userId&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;));&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Your database suddenly reduces CPU and DISK READs by orders of magnitude. This is going great! When the next projection
slows down you add another index to compensate.&lt;&#x2F;p&gt;
&lt;p&gt;At some point you&#x27;ll run into two users trying to update the same resource without knowing about each other. A common way to
maintain invariants is partitioning your events by &lt;em&gt;something&lt;&#x2F;em&gt; and using optimistic concurrency checks on that something.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s call that something a &quot;stream&quot;.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;ALTER TABLE&lt;&#x2F;span&gt;&lt;span&gt; events &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;ADD&lt;&#x2F;span&gt;&lt;span&gt; COLUMN stream_name &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;text NOT NULL&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;ALTER TABLE&lt;&#x2F;span&gt;&lt;span&gt; events &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;ADD&lt;&#x2F;span&gt;&lt;span&gt; COLUMN &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;version bigint NOT NULL&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;CREATE UNIQUE INDEX&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; events_stream&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; ON&lt;&#x2F;span&gt;&lt;span&gt; events (stream_name, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;version&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In order to implement the optimistic concurrency control you&#x27;ll supply an &quot;expected version&quot; to your function that writes events.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s a good idea to push the actual checking down to the database, so you implement a function that all writes should go through.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;-- simplified version of message-db&amp;#39;s write_message function&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;CREATE FUNCTION&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; write_event&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  id &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;varchar&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  stream_name &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;varchar&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  data&lt;&#x2F;span&gt;&lt;span&gt; jsonb,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  expected_version &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;bigint DEFAULT NULL&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;RETURNS bigint&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;AS&lt;&#x2F;span&gt;&lt;span&gt; $$&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;DECLARE&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  _event_id uuid;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  _stream_version &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;bigint&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  _next_version &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;bigint&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;BEGIN&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;  -- Lock the stream&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  PERFORM pg_advisory_xact_lock(hashtextextended(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;write_event&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;stream_name&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;));&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  _stream_version :&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;SELECT&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; MAX&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;version&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;FROM&lt;&#x2F;span&gt;&lt;span&gt; events &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;WHERE&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; events&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;stream_name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; write_event&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;stream_name&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  IF&lt;&#x2F;span&gt;&lt;span&gt; _stream_version &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;IS NULL THEN&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    _stream_version :&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  END IF&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  IF&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; write_event&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;expected_version&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; IS NOT NULL THEN&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    IF&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; write_event&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;expected_version&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span&gt; _stream_version &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;THEN&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      RAISE EXCEPTION &lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;#39;Wrong expected version&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    END IF&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  END IF&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  _next_version:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; _stream_version +&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  _event_id &lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; uuid(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;write_event&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  INSERT INTO&lt;&#x2F;span&gt;&lt;span&gt; events&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      id,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      stream_name,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      version&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      data&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  VALUES&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      _event_id,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;      write_event&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;stream_name&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      _next_version,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;      write_event&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;data&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  ;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  RETURN&lt;&#x2F;span&gt;&lt;span&gt; _next_version;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;END&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$$ &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;LANGUAGE&lt;&#x2F;span&gt;&lt;span&gt; plpgsql&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;VOLATILE;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You&#x27;ve now partitioned your events into streams and can prevent concurrent updates to the same stream! Good on you!&lt;&#x2F;p&gt;
&lt;p&gt;Your system keeps growing, and you&#x27;ve been adding new projections as it does. You&#x27;ve probably got 20-40 at this point.&lt;&#x2F;p&gt;
&lt;p&gt;Eventually you&#x27;ll release a buggy projection.
The decision to avoid eventual consistency now means no events can be written because a single projection has an error.&lt;&#x2F;p&gt;
&lt;p&gt;You frantically tell your customer:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Actually no! You didn&#x27;t confirm your email because there was an error in our document upload system.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;sql-store&#x2F;light-switch-meme.png&quot; alt=&quot;Please do not turn of the light switch, It also operates the elevator&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The more projections you have the more likely it is you&#x27;ll have a bug in one of them.&lt;&#x2F;p&gt;
&lt;p&gt;In order to isolate your system from single component failures, you decide to embrace eventual consistency.&lt;&#x2F;p&gt;
&lt;p&gt;You already have that global position on the table, so it should be easy to just poll the events table for changes.
You probably also want to store a checkpoint somewhere, so you don&#x27;t process the same event multiple times.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;ALTER TABLE&lt;&#x2F;span&gt;&lt;span&gt; users &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;ADD&lt;&#x2F;span&gt;&lt;span&gt; COLUMN &lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;checkpoint bigint NOT NULL DEFAULT&lt;&#x2F;span&gt;&lt;span&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;async function&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; appendEvent&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;client&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; streamId&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; event&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; expectedVersion&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; null&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; client&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;query&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;`SELECT write_event($1, $2, $3, $4, $5)`&lt;&#x2F;span&gt;&lt;span&gt;, [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;    event&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;    streamId&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;    event&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;    event&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;    expectedVersion&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  ])&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;async function&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; getEvents&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;client&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; streamId&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  return await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; client&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;query&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;    `SELECT * FROM events WHERE stream_name = $1 ORDER BY position`&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;streamId&lt;&#x2F;span&gt;&lt;span&gt;],&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;async function&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; confirmUserEmail&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;command&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; client&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; userEvents&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; getEvents&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;client&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; `User-&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;command&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; appendEvent&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;    client&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;    `User-&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;command&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;userId&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;      id&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; uuid&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;v4&lt;&#x2F;span&gt;&lt;span&gt;(),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;      type&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;UserConfirmedEmail&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;      email&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; command&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;      userId&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; command&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;userId&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;    userEvents&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;length&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;async function&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; userProjector&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;client&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; current_checkpoint&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; client&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;query&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;    `SELECT max(checkpoint) as current_checkpoint FROM users;`&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  if&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;current_checkpoint&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; null&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; current_checkpoint&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; = -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;n&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  while&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; eventBatch&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; client&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;query&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;      `SELECT id, data, position FROM events&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;       WHERE position &amp;gt; $1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;       ORDER BY position&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;       LIMIT 100;`&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;current_checkpoint&lt;&#x2F;span&gt;&lt;span&gt;],&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;    &#x2F;&#x2F; No events. We&amp;#39;re caught up. Let&amp;#39;s wait a bit and continue&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt;!&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;eventBatch&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;length&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      await new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Promise&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;res&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; setTimeout&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;res&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 1000&lt;&#x2F;span&gt;&lt;span&gt;))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      continue&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;    &#x2F;&#x2F; start a transaction and handle the events&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    try&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; client&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;query&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;#39;BEGIN&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; userProjection&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;eventBatch&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; client&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; client&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;query&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;#39;COMMIT&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;      current_checkpoint&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; eventBatch&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;eventBatch&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;length&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;position&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; catch&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;err&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; client&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;query&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;#39;ROLLBACK&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;With this change you should be able to drop every index that&#x27;s indexing inside the jsonb column&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;DROP INDEX&lt;&#x2F;span&gt;&lt;span&gt; idx_events_user_id;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;With those gone your write performance should go up considerably, and you&#x27;ll
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;HTTP&#x2F;Headers&#x2F;Retry-After&quot;&gt;devise clever ways&lt;&#x2F;a&gt; to hide the
eventual consistency of this approach.&lt;&#x2F;p&gt;
&lt;p&gt;You&#x27;re pretty happy for a while.&lt;&#x2F;p&gt;
&lt;p&gt;With more demand on the system, some hard to debug issues crop up.
It looks like your projections are skipping some events.&lt;&#x2F;p&gt;
&lt;div style=&quot;display: grid; grid-template-columns: 1fr 1fr; grid-column-gap: 1rem;&quot;&gt;
  &lt;pre className=&quot;whitespace-pre&quot; style=&quot;background: #282c34; border-radius: 1rem;&quot;&gt;
    BEGIN TRANSACTION
    &lt;br &#x2F;&gt;
    write_event() -- position 1
    &lt;br &#x2F;&gt;
    &lt;br &#x2F;&gt;
    &lt;br &#x2F;&gt;
    &lt;br &#x2F;&gt;
    COMMIT
  &lt;&#x2F;pre&gt;
  &lt;pre className=&quot;whitespace-pre&quot; style=&quot;background: #282c34; border-radius: 1rem;&quot;&gt;
    &lt;br &#x2F;&gt;
    &lt;br &#x2F;&gt;
    BEGIN TRANSACTION
    &lt;br &#x2F;&gt;
    write_event() -- position 2
    &lt;br &#x2F;&gt;
    COMMIT
    &lt;br &#x2F;&gt;
  &lt;&#x2F;pre&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;In the above case for a tiny amount of time, the global position will have a gap in the sequence. When your projector happens to
poll during that tiny interval, it will miss an event. This is fine until your application&#x27;s user base grows, and the likelihood of
skipped events increases. This is because sequences aren&#x27;t transactional.
There are plenty of strategies to solve this:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Use a separate singleton process to enumerate the event position&lt;&#x2F;li&gt;
&lt;li&gt;Perform gap detection and compensation&lt;&#x2F;li&gt;
&lt;li&gt;Write 1 event at a time (with the scale you&#x27;re at, this isn&#x27;t really an option)&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;There are more options, and they all make sense in different contexts.&lt;&#x2F;p&gt;
&lt;p&gt;I think now is a good time to stop this hypothetical and ask&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Why didn&#x27;t we just use a purpose built event store from the start?&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Using a purpose built event store prevents these issues from the start,
and that saves us from all of these headaches, extra work, and issues.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;addendum&quot;&gt;Addendum&lt;&#x2F;h1&gt;
&lt;h2 id=&quot;issues-relating-to-sequences-in-popular-projects&quot;&gt;Issues relating to sequences in popular projects&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;akka&#x2F;akka-persistence-jdbc&#x2F;issues&#x2F;96&quot;&gt;akka-persistence-jdbc#96&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;WegenenVerkeer&#x2F;akka-persistence-postgresql#write-strategies&quot;&gt;akka-persistence-postgresql#write-strategies&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;SQLStreamStore&#x2F;SQLStreamStore&#x2F;issues&#x2F;121&quot;&gt;SqlStreamStore#121&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;further-fun-issues&quot;&gt;Further &quot;fun&quot; issues&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;auto-vacuum&quot;&gt;Auto vacuum&lt;&#x2F;h3&gt;
&lt;p&gt;PostgreSQL anti-wraparound autovacuum runs once the oldest unfrozen table row is more than 200 million transactions old.
For an append only table (like our events table) this is normally the first time it&#x27;s vacuumed at all.&lt;&#x2F;p&gt;
&lt;p&gt;If the anti-wraparound auto-vacuum doesn&#x27;t finish in time, then 1 million transactions before it would suffer data
corruption postgres will stop accepting &lt;em&gt;any&lt;&#x2F;em&gt; new transactions.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;https:&#x2F;&#x2F;mailchimp.com&#x2F;what-we-learned-from-the-recent-mandrill-outage&#x2F;&lt;&#x2F;li&gt;
&lt;li&gt;https:&#x2F;&#x2F;www.joyent.com&#x2F;blog&#x2F;manta-postmortem-7-27-2015&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
      <item>
          <title>Using EventStore with NestJS</title>
          <pubDate>Mon, 12 Oct 2020 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://nordfjord.io/event-sourcing-in-nestjs-v2/</link>
          <guid>https://nordfjord.io/event-sourcing-in-nestjs-v2/</guid>
          <description xml:base="https://nordfjord.io/event-sourcing-in-nestjs-v2/">&lt;p&gt;A while ago I wrote a blog post about using EventStore with NestJS. I&#x27;ve regretted that post for a while because the
pattern I advocated for was overly complex and introduced abstractions for no good reason. I ended up de-listing
that post but I&#x27;m sure you&#x27;ll be able to find it if you look. This is meant as an updated version.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;event-sourcing&quot;&gt;Event sourcing&lt;&#x2F;h1&gt;
&lt;p&gt;Event sourcing is a very simple notion:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Every change to application state is an event. Instead of storing current state, state is stored a sequence of events&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;As software developers, we&#x27;ve been trained to think in terms of current state since the 1960&#x27;s.
It makes sense because memory was very expensive back then. Adding a megabyte of storage was a $1,000 decision, so we got good at optimizing storage.
Storage simply isn&#x27;t a major issue anymore and keeping a ledger of events represents a more natural way to think about systems.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;nestjs.com&quot;&gt;NestJS&lt;&#x2F;a&gt; is a backend javascript framework.
It builds on a solid foundation of patterns that enable you to write well factored code that&#x27;s easy to refactor.
It provides modules for working with common libraries like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.nestjs.com&#x2F;techniques&#x2F;database#typeorm-integration&quot;&gt;TypeORM&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.nestjs.com&#x2F;techniques&#x2F;database#sequelize-integration&quot;&gt;Sequelize&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.nestjs.com&#x2F;techniques&#x2F;authentication#authentication&quot;&gt;Passport&lt;&#x2F;a&gt;, and more.
In this post, we&#x27;re going to look at the command side of the CQRS equation using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;eventstore.com&quot;&gt;EventStore&lt;&#x2F;a&gt; to persist
our events.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;getting-started&quot;&gt;Getting Started&lt;&#x2F;h1&gt;
&lt;p&gt;Let&#x27;s start by creating a new Nest project with some wiring&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; npx @nestjs&#x2F;cli new cqrs-app&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; cd cqrs-app&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; pnpm add @eventstore&#x2F;db-client @nestjs&#x2F;cli bcrypt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; pnpm exec nest g module user&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; pnpm exec nest g module eventstore&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now that we have the essential boilerplate up, let&#x27;s start by wiring up the event store&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;&#x2F;&#x2F; eventstore&#x2F;eventstore.module.ts&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; EventStoreDBClient&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; from&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;@eventstore&#x2F;db-client&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; Module&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; Global&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; from&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;@nestjs&#x2F;common&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; EventStore&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  provide&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; EventStoreDBClient&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;  useFactory&lt;&#x2F;span&gt;&lt;span&gt;: ()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;    EventStoreDBClient&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;connectionString&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;      process&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;env&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;ESDB_CONN_STRING&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; ||&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;        &amp;#39;esdb:&#x2F;&#x2F;admin:changeit@localhost:2113?tls=false&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;Global&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;Module&lt;&#x2F;span&gt;&lt;span&gt;({&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; providers&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;EventStore&lt;&#x2F;span&gt;&lt;span&gt;],&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; exports&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;EventStore&lt;&#x2F;span&gt;&lt;span&gt;] })&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;export class&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; EventStoreModule&lt;&#x2F;span&gt;&lt;span&gt; {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We use a factory to instantiate our eventstore client when necessary and expose it in a Global module to ensure there&#x27;s
only 1 instance and that it is accessible from everywhere. Then we wire it up to the controller&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;&#x2F;&#x2F; app.module.ts&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;Module&lt;&#x2F;span&gt;&lt;span&gt;({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  imports&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;UserModule&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; EventStoreModule&lt;&#x2F;span&gt;&lt;span&gt;],&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  controllers&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;AppController&lt;&#x2F;span&gt;&lt;span&gt;],&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  providers&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;AppService&lt;&#x2F;span&gt;&lt;span&gt;],&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;})&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;export class&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; AppModule&lt;&#x2F;span&gt;&lt;span&gt; {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h1 id=&quot;the-user-module&quot;&gt;The User Module&lt;&#x2F;h1&gt;
&lt;p&gt;Next up we can start writing our user module. We begin by creating a decider. You can read more about the decider pattern
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;thinkbeforecoding.com&#x2F;post&#x2F;2021&#x2F;12&#x2F;17&#x2F;functional-event-sourcing-decider&quot;&gt;here&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;&#x2F;&#x2F; &#x2F;user&#x2F;user.decider.ts&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; DomainEvent&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;Type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Record&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; any&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  type&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Type&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  data&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Data&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;export type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; UserRegistered&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; email&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; passwordHash&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;export type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; RegisterUser&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; email&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; passwordHash&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;export type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Event&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; DomainEvent&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;#39;UserRegistered&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; UserRegistered&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;export type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Command&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; DomainEvent&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;#39;RegisterUser&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; RegisterUser&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;export type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; State&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  | {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; type&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;initial&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  | {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; type&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;registered&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; email&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; passwordHash&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;export const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; initialState&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; type&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;initial&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;export const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; evolve&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; State&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; event&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Event&lt;&#x2F;span&gt;&lt;span&gt;):&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; State&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  switch&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;event&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    case&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;UserRegistered&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      return&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;        type&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;registered&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;        email&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; event&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;        passwordHash&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; event&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;passwordHash&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;export const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; decide&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; State&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; command&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Command&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  switch&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;command&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    case&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;RegisterUser&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      if&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; ===&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;initial&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span&gt; [{&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; type&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;UserRegistered&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; data&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; command&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span&gt; }]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;      &#x2F;&#x2F; the practise of using nestjs exceptions inside the decider&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;      &#x2F;&#x2F; can be debated. It&amp;#39;s here for brevity. You may want to consider&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;      &#x2F;&#x2F; mapping domain exceptions to HTTP status codes instead&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      throw new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; ConflictException&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;#39;User already registered&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  return&lt;&#x2F;span&gt;&lt;span&gt; []&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h1 id=&quot;command-handler&quot;&gt;Command Handler&lt;&#x2F;h1&gt;
&lt;p&gt;The decider is and should be framework agnostic, it&#x27;s where your business logic lives after all. There&#x27;s actually multiple
ways we can wire this decider up, the following is a rudimentary event sourced command handler based on the decider. Just
keep in mind that you can wire it up to store state instead of events if you so choose.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;&#x2F;&#x2F; &#x2F;utils&#x2F;command-handler.ts&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Decider&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;S&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; E&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; C&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  initialState&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; S&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;  evolve&lt;&#x2F;span&gt;&lt;span&gt;: (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; event&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; E&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; S&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;  decide&lt;&#x2F;span&gt;&lt;span&gt;: (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; command&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; C&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; E&lt;&#x2F;span&gt;&lt;span&gt;[]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;async function*&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; handleEmpty&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;eventStream&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; AsyncIterable&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;ResolvedEvent&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  try&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    for await&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; resolved&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; of&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; eventStream&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      if&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt;!&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;resolved&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;event&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; continue&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      yield&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; resolved&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;event&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; catch&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;err&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;err&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; ===&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; ErrorType&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;STREAM_NOT_FOUND&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; return&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    throw&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; err&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Ctx&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; $correlationId&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;?&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; $causationId&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;?&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; userId&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;?&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Event&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; type&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; data&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; any&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;export const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; createCommandHandler&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;S&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; E&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Event&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; C&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;  client&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; EventStoreDBClient&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;  getStreamName&lt;&#x2F;span&gt;&lt;span&gt;: (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;command&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; C&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;  decider&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Decider&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;S&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; E&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; C&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt; async&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;command&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; C&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; context&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;?&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Ctx&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; streamName&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; getStreamName&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;command&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; state&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; decider&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;initialState&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; revision&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; ExpectedRevision&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; NO_STREAM&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  for await&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; event&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; of&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; handleEmpty&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;client&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;readStream&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;streamName&lt;&#x2F;span&gt;&lt;span&gt;))) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;    state&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; decider&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;evolve&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span&gt;, (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;event&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; as&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; any&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; as&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; E&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;    revision&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; event&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;revision&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; newEvents&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; decider&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;decide&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;state&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; command&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;event&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;    jsonEvent&lt;&#x2F;span&gt;&lt;span&gt;({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;      type&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; event&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;      data&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; event&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;      metadata&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; context&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; client&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;appendToStream&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;streamName&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; newEvents&lt;&#x2F;span&gt;&lt;span&gt;, {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;    expectedRevision&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; revision&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  })&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  return&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; success&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; true&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To finish this up we will create a NestJS controller to invoke the command handler.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; pnpm add class-validator class-transformer&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; pnpm exec nest g controller user&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;&#x2F;&#x2F; user&#x2F;user.controller.ts&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; Body&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; Controller&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; Post&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; from&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;@nestjs&#x2F;common&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; IsEmail&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; IsString&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; Matches&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; MinLength&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; from&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;class-validator&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; hash&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; genSalt&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; from&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;bcrypt&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; *&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; as&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; Decider&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; from&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;.&#x2F;user.decider&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; AppendResult&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; EventStoreDBClient&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; from&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;@eventstore&#x2F;db-client&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; createCommandHandler&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; from&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;..&#x2F;eventstore&#x2F;command-handler&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;class&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; RegisterUserDto&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  @&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;IsEmail&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  email&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; string&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  @&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;IsString&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  @&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;MinLength&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;8&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  @&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;Matches&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;&#x2F;((?=.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;\d)&lt;&#x2F;span&gt;&lt;span&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;(?=.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;\W&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;))(?!&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;.\n&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;)(?=.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;*[A-Z]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;)(?=.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;*[a-z]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;, {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;    message&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;password too weak&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  })&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  password&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; string&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;Controller&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;#39;user&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;export class&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; UserController&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  private readonly&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; handle&lt;&#x2F;span&gt;&lt;span&gt;: (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;command&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Decider&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;Command&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Promise&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;AppendResult&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  constructor&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;private readonly&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; client&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; EventStoreDBClient&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;    this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;handle&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; createCommandHandler&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;      client&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;      &#x2F;&#x2F; email is the primary ID of a user in our system&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;      cmd&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; `User-&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;cmd&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;      Decider&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  @&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;Post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;#39;register&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  async&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; register&lt;&#x2F;span&gt;&lt;span&gt;(@&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;Body&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; email&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; password&lt;&#x2F;span&gt;&lt;span&gt; }:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; RegisterUserDto&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; passwordHash&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; hash&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; genSalt&lt;&#x2F;span&gt;&lt;span&gt;())&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;handle&lt;&#x2F;span&gt;&lt;span&gt;({&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; type&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;RegisterUser&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; data&lt;&#x2F;span&gt;&lt;span&gt;: {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; email&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; passwordHash&lt;&#x2F;span&gt;&lt;span&gt; } })&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Finally let&#x27;s wire up the &lt;code&gt;ValidationPipe&lt;&#x2F;code&gt; in our main file&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;&#x2F;&#x2F; main.ts&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; NestFactory&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; from&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;@nestjs&#x2F;core&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; AppModule&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; from&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;.&#x2F;app.module&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; ValidationPipe&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; from&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;@nestjs&#x2F;common&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;async function&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; bootstrap&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; app&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; NestFactory&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;create&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;AppModule&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;  app&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;useGlobalPipes&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; ValidationPipe&lt;&#x2F;span&gt;&lt;span&gt;())&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; app&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;listen&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;8000&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;bootstrap&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This should be all that is required to have a working system. Let&#x27;s fire it up&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; pnpm run start:dev&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In another terminal let&#x27;s use curl to try it out&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;curl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; -X&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; POST&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;-H&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;Content-Type: application&#x2F;json&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;-H&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;Accept: application&#x2F;json&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;--data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;{&amp;quot;email&amp;quot;: &amp;quot;test@example.com&amp;quot;, &amp;quot;password&amp;quot;: &amp;quot;deADBEef12&amp;quot;}&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;http:&#x2F;&#x2F;localhost:3000&#x2F;user&#x2F;register&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We should see a&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;json&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; &amp;quot;success&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; true&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If we then open our browser at
&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;localhost:2113&#x2F;web&#x2F;index.html#&#x2F;streams&#x2F;User-test@example.com&quot;&gt;http:&#x2F;&#x2F;localhost:2113&#x2F;web&#x2F;index.html#&#x2F;streams&#x2F;User-test@example.com&lt;&#x2F;a&gt;,
we should see the event having been persisted to EventStoreDB.&lt;&#x2F;p&gt;
&lt;p&gt;If we try issuing the same request again though, we should see&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;json&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; &amp;quot;statusCode&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 409&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; &amp;quot;message&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;User already registered&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; &amp;quot;error&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;Conflict&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h1&gt;
&lt;p&gt;The previous iteration of this post used many concepts:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;AggregateRoot&lt;&#x2F;li&gt;
&lt;li&gt;AggregateRepository&lt;&#x2F;li&gt;
&lt;li&gt;EventBus&lt;&#x2F;li&gt;
&lt;li&gt;CommandBus&lt;&#x2F;li&gt;
&lt;li&gt;CommandHandler&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I hope that this updated version shows that you might not need all of that abstraction.&lt;&#x2F;p&gt;
&lt;p&gt;You can view the full example on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nordfjord&#x2F;nestjs-es-example-app&quot;&gt;GitHub&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;addendum&quot;&gt;Addendum&lt;&#x2F;h1&gt;
&lt;p&gt;Because deciders tend to not have any dependencies and function much like a state machine they are a great candidate
for a language like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rescript-lang.org&quot;&gt;ReScript&lt;&#x2F;a&gt;. The user decider for example could be written as:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;type user_registered = {email: string, passwordHash: string}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;type event = UserRegistered(user_registered)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;type command = RegisterUser(user_registered)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;type state = Initial | Registered(user_registered)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;let initialState = Initial&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;let evolve = (_state, event) =&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  switch event {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  | UserRegistered(data) =&amp;gt; Registered(data)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;exception User_already_registered&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;let decide = (state, command) =&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  switch (state, command) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  | (Initial, RegisterUser(data)) =&amp;gt; [UserRegistered(data)]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  | (Registered(_), RegisterUser(_)) =&amp;gt; raise(User_already_registered)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Which I&#x27;ve found easier to read and write than the typescript versions. ReScript compiles to very readable javascript
and if you&#x27;re comfortable making some assumptions you can very easily plug this version into the &lt;code&gt;createCommandHandler&lt;&#x2F;code&gt;
function. A full example with the necessary wiring exists on a branch on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nordfjord&#x2F;nestjs-es-example-app&#x2F;tree&#x2F;rescript&quot;&gt;GitHub&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Using EventStore with NestJS</title>
          <pubDate>Mon, 12 Oct 2020 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://nordfjord.io/event-sourcing-in-nestjs/</link>
          <guid>https://nordfjord.io/event-sourcing-in-nestjs/</guid>
          <description xml:base="https://nordfjord.io/event-sourcing-in-nestjs/">&lt;p&gt;Since writing this post my views have changed a lot, I describe my current way
of using EventStore with NestJS in a v2 of this post
&lt;a href=&quot;&#x2F;2022&#x2F;03&#x2F;27&#x2F;event-sourcing-in-nestjs-v2.html&quot;&gt;here&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Event sourcing is a pattern that gained traction recently. It can be boiled down to:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Every change to application state is an event. Instead of storing current state, state is stored a sequence of events&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;!-- prettier-ignore-start --&gt;
&lt;p&gt;As software developers, we&#x27;ve been trained to think in terms of current state since the 1960&#x27;s.
It makes sense because memory was very expensive back then. Adding a megabyte of storage was a $1,000 decision, so we got good at optimizing storage.
Storage simply isn&#x27;t a major issue anymore and keeping a ledger of events represents a more natural way to think about systems.&lt;&#x2F;p&gt;
&lt;!-- prettier-ignore-end --&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;nestjs.com&quot;&gt;NestJS&lt;&#x2F;a&gt; is a relatively new backend javascript framework.
It builds on a solid foundation of patterns that enable you to write well factored code that&#x27;s easy to refactor.
It provides modules for working with common libraries like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.nestjs.com&#x2F;techniques&#x2F;database#typeorm-integration&quot;&gt;TypeORM&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.nestjs.com&#x2F;techniques&#x2F;database#sequelize-integration&quot;&gt;Sequelize&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.nestjs.com&#x2F;techniques&#x2F;authentication#authentication&quot;&gt;Passport&lt;&#x2F;a&gt;, and more. On top of that
there&#x27;s a hidden gem of a package called &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.nestjs.com&#x2F;recipes&#x2F;cqrs&quot;&gt;&lt;code&gt;@nestjs&#x2F;cqrs&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; that provides a lightweight CQRS framework.
In this post, we&#x27;re going to look at the command side of the CQRS equation using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;eventstore.com&quot;&gt;EventStore&lt;&#x2F;a&gt; to persist
our events.&lt;&#x2F;p&gt;
&lt;p&gt;The high level conceptual overview of what we want to achieve is this:&lt;&#x2F;p&gt;
&lt;div id=&quot;figure-1&quot; style=&quot;margin: 1rem 0; display: flex; flex-direction: column; align-items: center; justify-content: center&quot;&gt;
  &lt;img alt=&quot;Command Architecture&quot; src=&quot;&#x2F;cqrs&#x2F;command.png&quot; &#x2F;&gt;
  &lt;span style=&quot;margin-top: 1rem&quot;&gt;Figure 1. Command Architecture&lt;&#x2F;span&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;The NestJS CQRS module has utilities to help us with &lt;code&gt;Commands&lt;&#x2F;code&gt;, &lt;code&gt;CommandHandlers&lt;&#x2F;code&gt;, and the domain concept of &lt;code&gt;AggregateRoots&lt;&#x2F;code&gt;. We&#x27;ll be using my pacakge &lt;code&gt;@nordfjord&#x2F;nestjs-cqrs-es&lt;&#x2F;code&gt; to provide the Repository layer.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;getting-started&quot;&gt;Getting Started&lt;&#x2F;h1&gt;
&lt;p&gt;Let&#x27;s start by creating a new Nest project with some wiring&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; npx @nestjs&#x2F;cli new cqrs-app&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; cd cqrs-app&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; yarn add @nestjs&#x2F;cqrs @nordfjord&#x2F;nestjs-cqrs-es @nestjs&#x2F;cli bcrypt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; yarn nest g module user&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now that we have the essential boilerplate up, let&#x27;s start by wiring up the event store&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;&#x2F;&#x2F; app.module.ts&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;Module&lt;&#x2F;span&gt;&lt;span&gt;({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  imports&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;    CqrsModule&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;    EventStoreModule&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;forRoot&lt;&#x2F;span&gt;&lt;span&gt;({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;      connection&lt;&#x2F;span&gt;&lt;span&gt;: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;        defaultUserCredentials&lt;&#x2F;span&gt;&lt;span&gt;: {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; username&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;admin&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; password&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;changeit&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt; },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;      tcpEndpoint&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;tcp:&#x2F;&#x2F;127.0.0.1:1113&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;    UserModule&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  ],&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  controllers&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;AppController&lt;&#x2F;span&gt;&lt;span&gt;],&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  providers&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;AppService&lt;&#x2F;span&gt;&lt;span&gt;],&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;})&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;export class&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; AppModule&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; implements&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; OnModuleInit&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  constructor&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    private readonly&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; eventStore&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; EventStore&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    private readonly&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; eventBus&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; EventBus&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;Event&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  ) {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  async&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; onModuleInit&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;    this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;eventBus&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;publisher&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;eventStore&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Here we&#x27;re adding a tcp connection to eventstore on localhost. If you don&#x27;t have eventstore already running on your machine, &lt;a href=&quot;&#x2F;docker-compose.eventstore.yml&quot;&gt;here&#x27;s&lt;&#x2F;a&gt; a docker-compose file you can use.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;the-user-module&quot;&gt;The User Module&lt;&#x2F;h1&gt;
&lt;p&gt;Next up we can start writing our user module. We begin by creating a command.
The command should create a user, with an email and a password.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;&#x2F;&#x2F; user&#x2F;dto&#x2F;register-user.dto.ts&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;export class&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; RegisterUserDto&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  email&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; string&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  password&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; string&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;&#x2F;&#x2F; user&#x2F;command&#x2F;impl&#x2F;register-user.command.ts&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;export class&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; RegisterUser&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  constructor&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;public readonly&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; data&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; RegisterUserDto&lt;&#x2F;span&gt;&lt;span&gt;) {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now that we have our command defined we need a way to handle it.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;&#x2F;&#x2F; user&#x2F;command&#x2F;handler&#x2F;register-user.handler.ts&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;CommandHandler&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;RegisterUser&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;export class&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; RegisterUserHandler&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; implements&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; ICommandHandler&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;RegisterUser&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  constructor&lt;&#x2F;span&gt;&lt;span&gt;() {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  async&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; execute&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;command&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; RegisterUser&lt;&#x2F;span&gt;&lt;span&gt;) {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The job of a command handler according to our &lt;a href=&quot;https:&#x2F;&#x2F;nordfjord.io&#x2F;event-sourcing-in-nestjs&#x2F;#figure-1&quot;&gt;diagram above&lt;&#x2F;a&gt; is to pass data on to the domain layer.
Since we don&#x27;t have a domain layer yet, we should pause development of the handler and create the User AggregateRoot first.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;user-aggregate-root&quot;&gt;User Aggregate Root&lt;&#x2F;h1&gt;
&lt;p&gt;Let&#x27;s start by defining what on earth an Aggregate Root is.&lt;&#x2F;p&gt;
&lt;dl&gt;
  &lt;dt className=&quot;font-semibold&quot;&gt;Aggregate&lt;&#x2F;dt&gt;
  &lt;dd&gt;Constituting or amounting to a whole; total.&lt;&#x2F;dd&gt;
  &lt;dt className=&quot;font-semibold&quot;&gt;Root&lt;&#x2F;dt&gt;
  &lt;dd&gt;Something that is an origin or source&lt;&#x2F;dd&gt;
&lt;&#x2F;dl&gt;
&lt;p&gt;An aggregate root guarantees the consistency of changes being made within
the aggregate by forbidding external objects from holding references to its members.&lt;&#x2F;p&gt;
&lt;p&gt;For example when driving a car, you are just driving a car.
You&#x27;re not moving the wheels in circular motion to move forward. &lt;em&gt;You&#x27;re just driving a car!&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Our User aggregate root has the job of enforcing our domain logic. For instance, a user cannot register twice.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;&#x2F;&#x2F; user&#x2F;model&#x2F;user.ts&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;export class&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; User&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; AggregateRoot&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;Event&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  private&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; isRegistered&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; false&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  private&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; email&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; string&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  private&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; passwordHash&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; string&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  constructor&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;private&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; id&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;font-style: italic;&quot;&gt;    super&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  async&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; register&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; password&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; string&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;isRegistered&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; throw new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; ConflictException&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;    this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;apply&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; UserRegistered&lt;&#x2F;span&gt;&lt;span&gt;({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;        email&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;        passwordHash&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; hash&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; genSalt&lt;&#x2F;span&gt;&lt;span&gt;()),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      }),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;  onUserRegistered&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;event&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; UserRegistered&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;    this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;isRegistered&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;    this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; event&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;email&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;    this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;passwordHash&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; event&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;passwordHash&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;NestJS aggregate roots will automatically call &lt;code&gt;on${EventType}&lt;&#x2F;code&gt; when we apply events. This is useful when hydrating our aggregates.&lt;&#x2F;p&gt;
&lt;p&gt;With this AggregateRoot in place it&#x27;s clear that the &lt;code&gt;CommandHandler&lt;&#x2F;code&gt; will instantiate the User AggregateRoot, and call the &lt;code&gt;register&lt;&#x2F;code&gt; method.
The &lt;code&gt;register&lt;&#x2F;code&gt; method will then apply a &lt;code&gt;UserRegistered&lt;&#x2F;code&gt; event or throw an exception.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s create the &lt;code&gt;UserRegistered&lt;&#x2F;code&gt; event.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;&#x2F;&#x2F; user&#x2F;dto&#x2F;user-registered.dto.ts&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;export class&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; UserRegisteredDto&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  email&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; string&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  passwordHash&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; string&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;&#x2F;&#x2F; user&#x2F;events&#x2F;user-registered.event.ts&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;export class&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; UserRegistered&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Event&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;UserRegisteredDto&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  constructor&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; UserRegisteredDto&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;font-style: italic;&quot;&gt;    super&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;`User-&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; data&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; ExpectedVersion&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;noStream&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;Event&lt;&#x2F;code&gt; constructor takes a few arguments.&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Argument&lt;&#x2F;th&gt;&lt;th&gt;Type&lt;&#x2F;th&gt;&lt;th&gt;Description&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;eventStreamId&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;string&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;The stream id the event belongs to&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;data&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;any&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;The payload of the event&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;expectedVersion&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;number&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;For optimistic concurrency checks. Here we&#x27;re using &lt;code&gt;noStream&lt;&#x2F;code&gt; as we don&#x27;t expect the stream to exist when the user registers.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;eventId&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;UUID&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Id of the event, auto generated uuid v4 by default&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;correlationId&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;UUID&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;CorrelationId of the event&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;causationId&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;UUID&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;CausationId of the event&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;Now that our event is defined, we wire up our User Module.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;&#x2F;&#x2F; user&#x2F;user.module.ts&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;Module&lt;&#x2F;span&gt;&lt;span&gt;({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  imports&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;    CqrsModule&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;    EventStoreModule&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;forFeature&lt;&#x2F;span&gt;&lt;span&gt;([&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;User&lt;&#x2F;span&gt;&lt;span&gt;], {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;      UserRegistered&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; event&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt; new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; UserRegistered&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;event&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span&gt;),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  ],&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  providers&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;RegisterUserHandler&lt;&#x2F;span&gt;&lt;span&gt;],&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;})&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;export class&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; UserModule&lt;&#x2F;span&gt;&lt;span&gt; {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;EventStoreModule.forFeature&lt;&#x2F;code&gt; takes 2 arguments.&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Argument&lt;&#x2F;th&gt;&lt;th&gt;Type&lt;&#x2F;th&gt;&lt;th&gt;Description&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;aggregateRoots&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;AggregateRootConstructor[]&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;A list of aggregate roots that will be made available as repositories&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;eventTransformers&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;Record&amp;lt;string, (event: EventStoreEvent)=&amp;gt; Event&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;A map of transfomers to map event store events to NestJS events&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;Now when reading events from the event store they&#x27;ll be transformed into classes that NestJS can deal with.&lt;&#x2F;p&gt;
&lt;p&gt;Before returning to the command handler, this is a summary of what we&#x27;ve done.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Commands are executed by Command Handlers&lt;&#x2F;li&gt;
&lt;li&gt;Command Handlers instantiate AggregateRoots&lt;&#x2F;li&gt;
&lt;li&gt;AggregateRoots apply Events&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h1 id=&quot;command-handler&quot;&gt;Command Handler&lt;&#x2F;h1&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;CommandHandler&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;RegisterUser&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;export class&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; RegisterUserHandler&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; implements&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; ICommandHandler&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;RegisterUser&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  constructor&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    @&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;InjectAggregateRepository&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;User&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    private readonly&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; userRepository&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; AggregateRepository&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;User&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  ) {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  async&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; execute&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;command&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; RegisterUser&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; user&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;userRepository&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;findOne&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;command&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; user&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;register&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;command&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;email&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; command&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;password&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;userRepository&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;save&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;user&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; success&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; true&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;@InjectAggregateRepository(User)&lt;&#x2F;code&gt; tells nest to inject the provider for the User AggregateRepository.&lt;&#x2F;p&gt;
&lt;p&gt;The execute method calls &lt;code&gt;userRepository.findOne(id)&lt;&#x2F;code&gt;. Behind the scenes &lt;code&gt;findOne&lt;&#x2F;code&gt; fetches all
events belonging to the user with that id and applies them to a newly instantiated User AggregateRoot.&lt;&#x2F;p&gt;
&lt;p&gt;then calling &lt;code&gt;user.register&lt;&#x2F;code&gt; applies a UserRegistered event or throws.&lt;&#x2F;p&gt;
&lt;p&gt;If the &lt;code&gt;register&lt;&#x2F;code&gt; call didn&#x27;t throw &lt;code&gt;userRepository.save(user)&lt;&#x2F;code&gt; will get all the uncommited events from the aggregate and persist them to the event store.&lt;&#x2F;p&gt;
&lt;p&gt;To finish this up we will create a NestJS controller to invoke the command handler.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; yarn nest g controller user&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;&#x2F;&#x2F; user&#x2F;user.controller.ts&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;Controller&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;#39;user&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;export class&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; UserController&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  constructor&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;private readonly&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; commandBus&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; CommandBus&lt;&#x2F;span&gt;&lt;span&gt;) {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  @&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;Post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;#39;register&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  async&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; register&lt;&#x2F;span&gt;&lt;span&gt;(@&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;Body&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; data&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; RegisterUserDto&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    return await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; this&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;commandBus&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;execute&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; RegisterUser&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span&gt;))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now we can run our code and test it out.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; yarn start:dev&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In another terminal let&#x27;s use curl to try it out&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;curl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; -X&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; POST&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;-H&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;Content-Type: application&#x2F;json&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;-H&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;Accept: application&#x2F;json&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;--data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;{&amp;quot;email&amp;quot;: &amp;quot;test@example.com&amp;quot;, &amp;quot;password&amp;quot;: &amp;quot;12345&amp;quot;}&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;http:&#x2F;&#x2F;localhost:3000&#x2F;user&#x2F;register&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We should see a&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;json&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; &amp;quot;success&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; true&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If we then open our browser at
&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;localhost:2113&#x2F;web&#x2F;index.html#&#x2F;streams&#x2F;User-test@example.com&quot;&gt;http:&#x2F;&#x2F;localhost:2113&#x2F;web&#x2F;index.html#&#x2F;streams&#x2F;User-test@example.com&lt;&#x2F;a&gt;,
we should see the event having been persisted to EventStoreDB.&lt;&#x2F;p&gt;
&lt;p&gt;If we try issuing the same request again though, we should see&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;json&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; &amp;quot;statusCode&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 409&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; &amp;quot;message&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;quot;Conflict&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h1&gt;
&lt;p&gt;In this post we&#x27;ve created a Command Side module in NestJS that can register users. You can view the full example on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nordfjord&#x2F;nestjs-cqrs-es-example-app&quot;&gt;GitHub&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The Query side is left as an excercise to the reader.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Thoughs on Async Redux</title>
          <pubDate>Mon, 31 Aug 2020 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://nordfjord.io/async-redux/</link>
          <guid>https://nordfjord.io/async-redux/</guid>
          <description xml:base="https://nordfjord.io/async-redux/">&lt;p&gt;Redux has been a go-to state management solution ever since it entered the mainstream. It takes a solid
functional programming approach to the Flux Architecture. It uses a reducer and action objects to maintain state.&lt;&#x2F;p&gt;
&lt;p&gt;Although Redux is fantastic for local state, the core library
does not provide a way to handle asynchronicity.&lt;&#x2F;p&gt;
&lt;p&gt;Many options exist, and I will compare three of them.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;reduxjs&#x2F;redux-thunk&quot;&gt;redux-thunk&lt;&#x2F;a&gt;,
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;redux-observable.js.org&#x2F;&quot;&gt;redux-observable&lt;&#x2F;a&gt;,
and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;redux-saga.js.org&#x2F;&quot;&gt;redux-saga&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h1 id=&quot;redux-thunk&quot;&gt;Redux Thunk&lt;&#x2F;h1&gt;
&lt;p&gt;Redux thunk is the minimum viable async middleware for redux. It
achieves its goals using (unsurprisingly) thunks, which are functions
returned from functions.
When dispatched, they look identical to regular action creators.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;&#x2F;&#x2F; fetchUser could be an action creator&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;&#x2F;&#x2F; or a thunk, we don&amp;#39;t care&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;dispatch&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;fetchUser&lt;&#x2F;span&gt;&lt;span&gt;())&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The basic example of fetching a user from an api would look like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;function&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; fetchUser&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  return async&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; dispatch&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;    dispatch&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;fetchUserStart&lt;&#x2F;span&gt;&lt;span&gt;())&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    try&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; user&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; fetch&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;#39;&#x2F;api&#x2F;user&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;then&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;x&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; x&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;())&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;      dispatch&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;fetchUserSuccess&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;user&lt;&#x2F;span&gt;&lt;span&gt;))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; catch&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;      dispatch&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;fetchUserError&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is great for most scenarios. It feels natural. In my opinion, redux-thunk should be preferred in 90% of cases. When I&#x27;m writing a redux application, I tend to keep redux-thunk around even if I adopt another async middleware later.&lt;&#x2F;p&gt;
&lt;p&gt;While redux-thunk is excellent at what it does well, it&#x27;s pretty terrible where it&#x27;s lacking. Things like debouncing inputs, canceling current requests, and action sequences are practically impossible to implement in a straight forward manner using thunks.&lt;&#x2F;p&gt;
&lt;p&gt;That&#x27;s where the other middlewares come in!&lt;&#x2F;p&gt;
&lt;h1 id=&quot;redux-observable&quot;&gt;Redux Observable&lt;&#x2F;h1&gt;
&lt;p&gt;It was while writing redux-observable code that I decided to make this post. RxJS Observables are immensely powerful for async. Redux-observables brings us this power in the form of Epics.&lt;&#x2F;p&gt;
&lt;p&gt;An epic is a mapping from an action to an action. In type terms:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; AppEpic&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;  action$&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Observable&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;Action&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;  state$&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Observable&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;AppState&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Observable&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;Action&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This type might look a bit weird, but let&#x27;s consider how to do the basic async example using observables.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; fetchUserEpic&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; AppEpic&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; action$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;  action$&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;    filter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;fetchUserStart&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;match&lt;&#x2F;span&gt;&lt;span&gt;),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;    switchMap&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;action&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;      fromPromise&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;fetch&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;#39;&#x2F;api&#x2F;user&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;then&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;x&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; x&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;())).&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;        map&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;payload&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; fetchUserFulfilled&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;payload&lt;&#x2F;span&gt;&lt;span&gt;)),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;        catchError&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; of&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;fetchUserError&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;))),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      ),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is more complicated than the redux-thunk example, in order to understand what&#x27;s going on you
need a working knowledge of Observables, what the operators &lt;code&gt;switchMap&lt;&#x2F;code&gt;, &lt;code&gt;map&lt;&#x2F;code&gt;, &lt;code&gt;filter&lt;&#x2F;code&gt;, and &lt;code&gt;catchError&lt;&#x2F;code&gt; do.
Not to mention needing to know what an operator even is!&lt;&#x2F;p&gt;
&lt;p&gt;Compared to observables, thunks require less knowledge to use. But as discussed before, thunks don&#x27;t deal well with debouncing inputs, canceling current requests, and action sequences.&lt;&#x2F;p&gt;
&lt;p&gt;Imagine a search box where as the user types, search results are shown.&lt;&#x2F;p&gt;
&lt;p&gt;This has some important constraints:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;we don&#x27;t want to send a request on every keystroke (that would choke the server)&lt;&#x2F;li&gt;
&lt;li&gt;we want to cancel in progress requests on a new keystroke because the one in progress is no longer relevant&lt;&#x2F;li&gt;
&lt;li&gt;we need to make sure that the last request corresponds to the last keystroke, otherwise we might display stale data&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Imagine this component:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;export function&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; RepoFetcher&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; dispatch&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; useDispatch&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; repos&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; useSelector&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;x&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; x&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;repos&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  return&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;lt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;input&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D19A66;font-style: italic;&quot;&gt;        type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;quot;text&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D19A66;font-style: italic;&quot;&gt;        onChange&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;ev&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; dispatch&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;fetchReposInput&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;ev&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;currentTarget&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span&gt;))&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;h3&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;        {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;repos&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; ?&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;Error!&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; :&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; repos&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;isLoading&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; ?&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;Loading...&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; :&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;Results&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;}{&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;#39; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;h3&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;ul&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;        {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;repos&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;repos&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;x&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;li&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;font-style: italic;&quot;&gt; key&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;x&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;font-style: italic;&quot;&gt; href&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;x&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;html_url&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;x&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;full_name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;li&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        ))&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;ul&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;lt;&#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If we used a thunk to handle the fetchReposInput action, it might look like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; fetchReposInput&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; input&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt; async&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; dispatch&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;  dispatch&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;fetchReposStart&lt;&#x2F;span&gt;&lt;span&gt;())&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  try&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; repos&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; fetch&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;      &amp;#39;https:&#x2F;&#x2F;api.github.com&#x2F;search&#x2F;repositories?per_page=10&amp;amp;q=&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; +&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;        encodeURIComponent&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;input&lt;&#x2F;span&gt;&lt;span&gt;),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ).&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;then&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;x&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; x&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;json&lt;&#x2F;span&gt;&lt;span&gt;())&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;    dispatch&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;fetchReposSuccess&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;repos&lt;&#x2F;span&gt;&lt;span&gt;))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; catch&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;    dispatch&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;fetchReposError&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We have broken all 3 of the constraints with this code.&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Every keystroke triggers a request&lt;&#x2F;li&gt;
&lt;li&gt;The old fetch is still alive when a new keystroke arrives&lt;&#x2F;li&gt;
&lt;li&gt;There&#x27;s no correlation between time of keystroke and time of response&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The errors these constraint violations cause can be hard to track down as well.&lt;&#x2F;p&gt;
&lt;p&gt;Using observables, we can write code that fits within the constraints fairly easily&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; searchRepos&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; q&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;  &#x2F;&#x2F; utility exported by rxjs&#x2F;ajax&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;  &#x2F;&#x2F; it returns an Observable&amp;lt;JSON&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;  &#x2F;&#x2F; and uses XMLHttpRequest under the hood&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;  &#x2F;&#x2F; to facilitate cancellations&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;  ajax&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;getJSON&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;    `https:&#x2F;&#x2F;api.github.com&#x2F;search&#x2F;repositories?per_page=10&amp;amp;q=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;encodeURIComponent&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;      q&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    )&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;`&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; fetchReposEpic&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; action$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;  action$&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;    filter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;fetchReposInput&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;match&lt;&#x2F;span&gt;&lt;span&gt;),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;    debounceTime&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;300&lt;&#x2F;span&gt;&lt;span&gt;),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;    switchMap&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;action&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;      of&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;fetchReposStart&lt;&#x2F;span&gt;&lt;span&gt;()).&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;        concat&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;          searchRepos&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;action&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;payload&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;            map&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;payload&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; fetchReposSuccess&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;payload&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;items&lt;&#x2F;span&gt;&lt;span&gt;)),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;            catchError&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; of&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;fetchReposError&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;))),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          ),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        ),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      ),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Let&#x27;s step through this:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;We get every action that&#x27;s a fetchReposInput action&lt;&#x2F;li&gt;
&lt;li&gt;We debounce by 300ms; this means that we don&#x27;t continue
the pipe until 300ms have passed since the last action&lt;&#x2F;li&gt;
&lt;li&gt;We &lt;code&gt;switchMap&lt;&#x2F;code&gt;, this is also known as a &lt;code&gt;flatMap&lt;&#x2F;code&gt;, &lt;code&gt;chain&lt;&#x2F;code&gt;, or &lt;code&gt;bind&lt;&#x2F;code&gt; in other languages,
but here we can think of it as going from &lt;code&gt;Observable&amp;lt;Observable&amp;lt;Action&amp;gt;&amp;gt;&lt;&#x2F;code&gt; to an &lt;code&gt;Observable&amp;lt;Action&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;In the switchMap we create a new observable that has a &lt;code&gt;fetchReposStart&lt;&#x2F;code&gt; action, and concatenate
the &lt;code&gt;searchRepos&lt;&#x2F;code&gt; request result&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This means our Epic will return in order:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;fetchReposStart()&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;fetchReposSuccess(payload.items)&lt;&#x2F;code&gt; | &lt;code&gt;fetchReposError(error)&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;&lt;code&gt;switchMap&lt;&#x2F;code&gt; has properties that help us adhere to our constraints.
When a new input action is dispatched while we&#x27;re still fetching, it automatically stops subscribing to the current observable and cancels it.&lt;&#x2F;p&gt;
&lt;p&gt;Since we&#x27;re &quot;just&quot; using RxJS we also gain access to a whole host of operators.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;debounceTime&lt;&#x2F;code&gt; for waiting&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;filter&lt;&#x2F;code&gt; to select actions&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;concat&lt;&#x2F;code&gt; to append an observable to another&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;map&lt;&#x2F;code&gt; to transform&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;catchError&lt;&#x2F;code&gt; to catch errors&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;There are literally over a hundred operators like these. So it&#x27;s very likely &lt;em&gt;There&#x27;s an operator for&lt;&#x2F;em&gt; &lt;strong&gt;&lt;em&gt;that&lt;&#x2F;em&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;thoughts-on-redux-observable&quot;&gt;Thoughts on Redux Observable&lt;&#x2F;h2&gt;
&lt;p&gt;I think observables have some benefits over thunks in cases where&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;You&#x27;re doing something more complicated than a set and forget async fetch&lt;&#x2F;li&gt;
&lt;li&gt;You already know observables, or are willing to put in the effort to learn them, and teach your team.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h1 id=&quot;redux-saga&quot;&gt;Redux Saga&lt;&#x2F;h1&gt;
&lt;p&gt;Redux saga is the final middleware we&#x27;ll be looking at here.&lt;&#x2F;p&gt;
&lt;p&gt;Redux saga aims to make side effects more natural to manage,
more efficient to execute, better at handling failures, and easy to test.&lt;&#x2F;p&gt;
&lt;p&gt;It does this while also maintaining an imperative syntax.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s take a look at how we would implement the observable example using sagas.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;function*&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; fetchRepos&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;action&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  yield&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; delay&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;300&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  yield&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; put&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;fetchReposStart&lt;&#x2F;span&gt;&lt;span&gt;())&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  try&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; response&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; yield&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; call&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;Api&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;fetchRepos&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; action&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;payload&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    yield&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; put&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;fetchReposSuccess&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;response&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;items&lt;&#x2F;span&gt;&lt;span&gt;))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; catch&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    yield&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; put&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;fetchReposError&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;function*&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; watchFetchReposInput&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  yield&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; takeLatest&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;fetchReposInput&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;match&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; fetchRepos&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If you squint a bit, the &lt;code&gt;fetchRepos&lt;&#x2F;code&gt; method almost looks familiar. It&#x27;s the same structure as
the basic async thunk. Swap out &lt;code&gt;put&lt;&#x2F;code&gt; for &lt;code&gt;dispatch&lt;&#x2F;code&gt;, remove the yields, and we&#x27;re there!
There&#x27;s some added noise, but in return, we get great benefits.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;takeLatest&lt;&#x2F;code&gt; also handles cancelling in progress effects.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-more-convoluted-example&quot;&gt;A more convoluted example&lt;&#x2F;h2&gt;
&lt;p&gt;For a basic async thunk, you &lt;strong&gt;do not need&lt;&#x2F;strong&gt; sagas. Where sagas shine are action sequences, especially with dependent actions.&lt;&#x2F;p&gt;
&lt;p&gt;Imagine a card game, where before you can play a card, a modal should be displayed where
you pick a mana distribution to pay for the card (think two blue, three white).&lt;&#x2F;p&gt;
&lt;p&gt;The sequence of actions would be&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;      Request Play Card&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt;              |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;        Show Mana Modal&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt;          &#x2F;&lt;&#x2F;span&gt;&lt;span&gt;          \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt;         &#x2F;&lt;&#x2F;span&gt;&lt;span&gt;            \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt;        &#x2F;&lt;&#x2F;span&gt;&lt;span&gt;              \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;Close Mana Modal    Pay Mana&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt;                        |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;                    Play Card&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To be clear this example is not something you &lt;em&gt;need&lt;&#x2F;em&gt; sagas for,
to illustrate let&#x27;s look at how we would implement this
sequence using observables.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;export const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; payMana&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;cost&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; action$&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; onPayAction&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;  of&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;manaModalSlice&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;actions&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;showModal&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;cost&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;    concat&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;      action$&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;        takeUntil&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;          actions$&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;filter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;manaModalSlice&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;actions&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;closeModal&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;match&lt;&#x2F;span&gt;&lt;span&gt;)),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        ),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;        filter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;manaSlice&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;actions&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;payMana&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;match&lt;&#x2F;span&gt;&lt;span&gt;),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;        mapTo&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;onPayAction&lt;&#x2F;span&gt;&lt;span&gt;),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      ),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;export const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; rootEpic&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;action$&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; state$&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;  action$&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;pipe&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;    filter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;requestPlayCard&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;match&lt;&#x2F;span&gt;&lt;span&gt;),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;    switchMap&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;action&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; action&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;payload&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;id&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; card&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; cardSelectors&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;selectById&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;)(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;state$&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; payMana&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;        card&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;manaCost&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;        action$&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;        playerSlice&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;actions&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;playCard&lt;&#x2F;span&gt;&lt;span&gt;({&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; id&lt;&#x2F;span&gt;&lt;span&gt; }),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is very declarative. A gripe I&#x27;ve had with redux-observable is just how
unintuitive it is to create dependent action sequences. It always requires a flatMap,
and this amount of code is for an event series with only one branch.&lt;&#x2F;p&gt;
&lt;p&gt;How would we handle this use case with a saga?&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;function*&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; payMana&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;cost&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  yield&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; put&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;manaModalSlice&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;actions&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;showModal&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;cost&lt;&#x2F;span&gt;&lt;span&gt;))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;  &#x2F;&#x2F; an infinite while loop is okay, because we know&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;  &#x2F;&#x2F; that the only way to get out of the modal&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;  &#x2F;&#x2F; is through the two actions handled in the loop&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  while&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; action&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; yield&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; take&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;manaSlice&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;actions&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;payMana&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;match&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;action&lt;&#x2F;span&gt;&lt;span&gt;)) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;manaModalSlice&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;actions&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;closeManaModal&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;match&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;action&lt;&#x2F;span&gt;&lt;span&gt;)) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;      return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; false&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;function*&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; playCard&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;action&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; action&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;payload&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;id&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; card&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; yield&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; select&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;cardSelectors&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;selectById&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; isPayed&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; yield&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; call&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;getManaPayment&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; card&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;manaCost&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  if&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;isPayed&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    yield&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; put&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;playerSlice&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;actions&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;playCard&lt;&#x2F;span&gt;&lt;span&gt;({&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; id&lt;&#x2F;span&gt;&lt;span&gt; }))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;function*&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; watchRequestPlayCard&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  yield&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; takeEvery&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;requestPlayCard&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;match&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; playCard&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In my opinion, this code is easier to follow than the Observable example.
I can read through the code step by step to get an understanding
of what&#x27;s going on.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;For every &lt;code&gt;requestPlayCard&lt;&#x2F;code&gt; action we run the &lt;code&gt;playCard&lt;&#x2F;code&gt; method.&lt;&#x2F;li&gt;
&lt;li&gt;To play a card we first call &lt;code&gt;getManaPayment&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;getManaPayment&lt;&#x2F;code&gt; dispatches a &lt;code&gt;showModal&lt;&#x2F;code&gt; action and then waits for a &lt;code&gt;closeModal&lt;&#x2F;code&gt; or a &lt;code&gt;payMana&lt;&#x2F;code&gt; action and returns a corresponding boolean&lt;&#x2F;li&gt;
&lt;li&gt;Back in the &lt;code&gt;playCard&lt;&#x2F;code&gt; method, we check whether the mana has been paid and if so we dispatch the &lt;code&gt;playCard&lt;&#x2F;code&gt; action&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This sort of understanding is harder to come by in the Observable version.
I believe this is because in that example we need an understanding of RxJS operators
to know what&#x27;s going on.
With sagas, we only require an understanding of JavaScript language primitives.&lt;&#x2F;p&gt;
&lt;p&gt;I believe, redux-saga is to redux-observable what async&#x2F;await is to Promises.
It&#x27;s a really great syntax for declaring side effects in an imperative way.
All the heavy lifting is handled by redux-saga: waiting, dispatching, and so on.
We just yield descriptions of what should be done making sagas inherently testable.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h1&gt;
&lt;p&gt;The argument for redux-observable is that it&#x27;s just RxJS, which is honestly fantastic.
RxJS has been a godsend for reactive programming and async in general. But when it
comes to complex dependent sequences the syntax sagas provide fits better.&lt;&#x2F;p&gt;
&lt;p&gt;I would much rather onboard a newbie to a codebase with sagas than observables.
That being said I have a few rules of thumb to guide me towards the correct async middleware
for a given scenario.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;For basic asynchronous set&#x2F;forget calls, use thunks&lt;&#x2F;li&gt;
&lt;li&gt;To immediately dispatch multiple actions, use thunks&lt;&#x2F;li&gt;
&lt;li&gt;For everything else there&#x27;re sagas&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;After using both sagas and observables, I&#x27;ll stick with sagas for now.&lt;&#x2F;p&gt;
&lt;p&gt;If you already know RxJS, and need an async middleware,
I think redux-observable is fantastically expressive.
But I think there&#x27;re tradeoffs to everything, and with redux-observable
I felt more constrained than with sagas. Furthermore, it was less difficult for me
to explain to others what was going on in a saga than an observable,
which to me, weighs much higher than terse, meaning dense, code.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Thoughts on Testing Components</title>
          <pubDate>Wed, 22 Jul 2020 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://nordfjord.io/thoughts-on-testing-components/</link>
          <guid>https://nordfjord.io/thoughts-on-testing-components/</guid>
          <description xml:base="https://nordfjord.io/thoughts-on-testing-components/">&lt;p&gt;Testing components is a large undertaking. It can be difficult to decide the scope of testing or if something even needs
tesing.&lt;&#x2F;p&gt;
&lt;p&gt;I recently came upon a problem with an occlusion culled list. Occlusion culling is a technique used to increase
performance of long lists and tables. It does so by not rendering the parts of the component that aren&#x27;t visible on the
user&#x27;s screen. This also means that fewer DOM nodes are needed.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;occlusion-culling&quot;&gt;Occlusion Culling&lt;&#x2F;h1&gt;
&lt;p&gt;A simple occlusion culled list might look like this in code:
{% raw %}&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;tsx&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; itemHeight&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 31&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; tableHeight&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 312&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; items&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Array&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt;({&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; length&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 5000&lt;&#x2F;span&gt;&lt;span&gt; }).&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;((&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;_&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; i&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; ({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  id&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; i&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  utilization&lt;&#x2F;span&gt;&lt;span&gt;: (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 100&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;export default function&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; OcclusionList&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;scrollTop&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; setScrollTop&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; useState&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; begin&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;scrollTop&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; itemHeight&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;  &#x2F;&#x2F; Add 2 so that the top and bottom of the page are filled with&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;  &#x2F;&#x2F; next&#x2F;prev item, not just whitespace if item not in full view&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; end&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; begin&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span&gt; ((&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;tableHeight&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; itemHeight&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; offset&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; scrollTop&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; %&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; itemHeight&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; visibleItems&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; items&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;slice&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;begin&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; end&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  return&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D19A66;font-style: italic;&quot;&gt;      className&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;quot;table&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D19A66;font-style: italic;&quot;&gt;      onScroll&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;e&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; setScrollTop&lt;&#x2F;span&gt;&lt;span&gt;((&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;e&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;target&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; as&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; HTMLDivElement&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;scrollTop&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D19A66;font-style: italic;&quot;&gt;      style&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;        maxHeight&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; `&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;tableHeight&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;px`&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;        overflowY&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;scroll&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D19A66;font-style: italic;&quot;&gt;        className&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;quot;fake-height&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D19A66;font-style: italic;&quot;&gt;        style&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; height&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; `&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;items&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;length&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; *&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; itemHeight&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;px`&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;ul&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;font-style: italic;&quot;&gt; className&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;quot;body&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;font-style: italic;&quot;&gt; style&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; top&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; `&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;${&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;scrollTop&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; offset&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;px`&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;          {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;visibleItems&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;item&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;li&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;font-style: italic;&quot;&gt; className&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;quot;row&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;font-style: italic;&quot;&gt; key&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;item&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;span&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;font-style: italic;&quot;&gt; className&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;quot;id&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;item&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;span&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;span&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;font-style: italic;&quot;&gt; className&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;quot;utilization&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;                {&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;item&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;utilization&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; *&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 100&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;toFixed&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;%&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;              &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;span&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;li&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;          ))&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;ul&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;{% endraw %}&lt;&#x2F;p&gt;
&lt;style&gt;
.listBody {
  list-style: none;
  margin: 0;
  padding: 0;
  position: relative;
  top: 0;
}

.lineItem::before {
display: none;
}

.listBody .lineItem {
  background-color: var(--brand-fg);
  margin: 0;
  margin-bottom: 1px;
  padding: 0 1.25rem;
  height: 1.875rem;
  display: flex;
  flex-flow: row;
  align-items: center;
  justify-content: space-between;
}
  
.fakeTable {
  height: 19.5rem;
}
&lt;&#x2F;style&gt;
&lt;div id=&quot;occlusion-list&quot;&gt;&lt;&#x2F;div&gt;
&lt;script type=&quot;module&quot; src=&quot;&#x2F;occlusion-list.js&quot;&gt;&lt;&#x2F;script&gt;
&lt;p&gt;If you inspect the list above using browser devtools, you will see that as you scroll the component replaces the li&#x27;s in
the DOM with new ones as you scroll. A constant number of list items remains in the DOM.&lt;&#x2F;p&gt;
&lt;p&gt;This increases performance even to the point of hitting the 1000ms mobile benchmark by deferring the rendering of below
the fold content.&lt;&#x2F;p&gt;
&lt;p&gt;However, this presents a problem for your friendly QA staff.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;the-problem&quot;&gt;The Problem&lt;&#x2F;h1&gt;
&lt;p&gt;A common pattern in end-to-end testing is:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Setting up all of data the user expects to see in the actual table&lt;&#x2F;li&gt;
&lt;li&gt;Parsing data from the actual table&lt;&#x2F;li&gt;
&lt;li&gt;Asserting the equivalency of the two&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Or in code:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;tsx&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;function&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; Test&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; expectedData&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Array&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt;({&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; length&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 5000&lt;&#x2F;span&gt;&lt;span&gt; }).&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;((&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;_&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; i&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; ({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;    id&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; i&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;    utilization&lt;&#x2F;span&gt;&lt;span&gt;: (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 100&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; actualData&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; parseDataFromTable&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;    document&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;querySelector&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;#39;.table&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; as&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; HTMLDivElement&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;  assertEqual&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;expectedData&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;length&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; actualData&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;length&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;  assertEqual&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;expectedData&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; actualData&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;  console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;#39;Success!&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;function&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; parseDataFromTable&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;table&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; HTMLDivElement&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Array&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;table&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;querySelectorAll&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;#39;.body &amp;gt; .row&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;rowElement&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; ({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;    id&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; Number&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;rowElement&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;children&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;textContent&lt;&#x2F;span&gt;&lt;span&gt;),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;    utilization&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;      Number&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;rowElement&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;children&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;textContent&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;replace&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;#39;%&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;))&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 100&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;div id=&quot;occlusion-list-sync&quot;&gt;&lt;&#x2F;div&gt;
&lt;p&gt;Why does the above test fail?&lt;&#x2F;p&gt;
&lt;p&gt;It fails because we assume that all the data we want to assert against is in the DOM; however, because we are using
occlusion culling, the data simply isn&#x27;t there.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;the-solution&quot;&gt;The Solution&lt;&#x2F;h1&gt;
&lt;p&gt;In order to properly test this table, we&#x27;re going to have to emulate a user scrolling through it. This means our parsing
function has to run asynchronously to gather all the data.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-do-we-scroll-through-the-table-programatically&quot;&gt;How do we scroll through the table programatically?&lt;&#x2F;h2&gt;
&lt;p&gt;Let&#x27;s start by looking at the table markup. In this example, we&#x27;re using a list of the following form&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;html&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; class&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;#39;table&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; class&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;#39;fake-height&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;ul&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; class&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;#39;body&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;li&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; class&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;#39;row&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;span&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; class&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;#39;id&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;4983&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;span&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;span&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; class&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;#39;utilization&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;4983%&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;span&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;li&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;      &amp;lt;!-- More li&amp;#39;s --&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;ul&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;.table&lt;&#x2F;code&gt; is the scroll container. In code, we set its height to a static value of &lt;code&gt;312&lt;&#x2F;code&gt;, and
set &lt;code&gt;overflow-y: scroll&lt;&#x2F;code&gt; to allow the user to scroll inside the element.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;.fake-height&lt;&#x2F;code&gt; element takes care of telling the browser how big the list is. In the example a row is 31px so for
5000 elements we&#x27;ll see &lt;code&gt;&amp;lt;div class=&quot;fake-height&quot; style=&quot;height: 15500px&quot;&amp;gt;&amp;lt;&#x2F;div&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;.body&lt;&#x2F;code&gt; element will move down as we scroll using the &lt;code&gt;top&lt;&#x2F;code&gt; attribute, so if we&#x27;ve scrolled down 500px, it will
show &lt;code&gt;&amp;lt;ul class=&quot;body&quot; style=&quot;top: 500px&quot;&amp;gt;&amp;lt;&#x2F;ul&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This means in code we&#x27;ll see:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;tsx&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;table&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;scrollHeight&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; ===&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 15500&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;table&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;offsetHeight&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; ===&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 312&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;to scroll an element in code we can use its &lt;code&gt;scrollTop&lt;&#x2F;code&gt; property, so for our table we&#x27;ll use&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;tsx&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;table&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;scrollTop&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; +=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; table&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;offsetHeight&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 31&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;we subtract the &lt;code&gt;31&lt;&#x2F;code&gt; to make sure we don&#x27;t miss any elements.&lt;&#x2F;p&gt;
&lt;p&gt;Calling this method will also trigger the &lt;code&gt;onScroll&lt;&#x2F;code&gt; listener on the element.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;putting-it-to-use&quot;&gt;Putting it to use&lt;&#x2F;h2&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #ABB2BF; background-color: #282C34;&quot;&gt;&lt;code data-lang=&quot;tsx&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; wait&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;ms&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; number&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt; new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Promise&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;res&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; setTimeout&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;res&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; ms&lt;&#x2F;span&gt;&lt;span&gt;))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;async function&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; Test&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; expectedData&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Array&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt;({&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; length&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 5000&lt;&#x2F;span&gt;&lt;span&gt; }).&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;((&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;_&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt; i&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; ({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;    id&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; i&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;    utilization&lt;&#x2F;span&gt;&lt;span&gt;: (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 100&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; actualData&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; parseDataFromTable&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;    document&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;querySelector&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;#39;.table&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; as&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; HTMLDivElement&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;  assertEqual&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;expectedData&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;length&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; actualData&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;length&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;  assertEqual&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;expectedData&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; actualData&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;  console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;#39;Success!&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;async function&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; parseDataFromTable&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;table&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; HTMLDivElement&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; scrollToNextPage&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; ()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;    table&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;scrollTop&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; +=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; table&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;offsetHeight&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 31&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; data&lt;&#x2F;span&gt;&lt;span&gt;: {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; id&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; number&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; utilization&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; number&lt;&#x2F;span&gt;&lt;span&gt; }[]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; []&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  while&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;table&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;scrollTop&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; table&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;scrollHeight&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; table&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;offsetHeight&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;    data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; data&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;concat&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;getCurrentDataFromTable&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;table&lt;&#x2F;span&gt;&lt;span&gt;))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;    scrollToNextPage&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;    &#x2F;&#x2F; give the main thread a chance to render&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;    await&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; wait&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;32&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;  data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; data&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;concat&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;getCurrentDataFromTable&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;table&lt;&#x2F;span&gt;&lt;span&gt;))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7F848E;font-style: italic;&quot;&gt;  &#x2F;&#x2F; Helper from ramda to remove duplicates from the array&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; uniqBy&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;x&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; x&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;id&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt; data&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;function&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; getCurrentDataFromTable&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;table&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; HTMLDivElement&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt;  return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt; Array&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;table&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;querySelectorAll&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;#39;.body &amp;gt; .row&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)).&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;font-style: italic;&quot;&gt;rowElement&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C678DD;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; ({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;    id&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt; Number&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;rowElement&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;children&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;textContent&lt;&#x2F;span&gt;&lt;span&gt;),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;    utilization&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;      Number&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;rowElement&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E06C75;&quot;&gt;children&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;]?.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E5C07B;&quot;&gt;textContent&lt;&#x2F;span&gt;&lt;span&gt;?.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #61AFEF;&quot;&gt;replace&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt;&amp;#39;%&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #98C379;&quot;&gt; &amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;))&lt;&#x2F;span&gt;&lt;span style=&quot;color: #56B6C2;&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D19A66;&quot;&gt; 100&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;div id=&quot;occlusion-list-async&quot;&gt;&lt;&#x2F;div&gt;
&lt;p&gt;This has proven to be an effective solution.&lt;&#x2F;p&gt;
&lt;p&gt;It scrolls through the element just like a user would collecting the data from the list as it scrolls.&lt;&#x2F;p&gt;
</description>
      </item>
    </channel>
</rss>
