<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Cqrs on Brian Faust</title><link>https://vacua.dev/tags/cqrs/</link><description>Recent content in Cqrs on Brian Faust</description><generator>Hugo</generator><language>en-US</language><lastBuildDate>Mon, 01 Jan 0001 00:00:00 +0000</lastBuildDate><atom:link href="https://vacua.dev/tags/cqrs/rss.xml" rel="self" type="application/rss+xml"/><item><title>CQRS Starts When Reads Need a Different Model</title><link>https://vacua.dev/command-query-separation-vs-cqrs/</link><pubDate>Mon, 01 May 2023 00:00:00 +0000</pubDate><guid>https://vacua.dev/command-query-separation-vs-cqrs/</guid><description>&lt;p&gt;An order-details page needs customer name, payment status, shipment progress,
line totals, tax labels, and the latest support case. The write model needs to
protect payment, cancellation, and fulfilment invariants.&lt;/p&gt;
&lt;p&gt;Trying to make one Eloquent graph serve both jobs creates a familiar mess:
relationships exist for screen composition, write methods expose persistence
concerns, and every request loads a different subset of a model that is never
quite complete.&lt;/p&gt;
&lt;p&gt;That tension can justify different read and write models. It does not
automatically justify two databases, an event store, a command bus, or eventual
consistency. CQRS is the model split; the infrastructure is a separate set of
decisions.&lt;/p&gt;</description></item><item><title>A Queued Command Changes the Contract</title><link>https://vacua.dev/a-queued-command-changes-the-contract/</link><pubDate>Sat, 01 Apr 2023 00:00:00 +0000</pubDate><guid>https://vacua.dev/a-queued-command-changes-the-contract/</guid><description>&lt;p&gt;Send the same payment-capture request through two Laravel routes.&lt;/p&gt;
&lt;p&gt;The synchronous route returns 204 after the provider has created charge
&lt;code&gt;ch_001&lt;/code&gt; and the order records that reference. When the provider returns 503,
the HTTP request returns 500 and the order remains unpaid.&lt;/p&gt;
&lt;p&gt;The queued route returns 202 while the order is still unpaid and the provider
ledger is empty. Its response contains an operation URL. Cancel the order
before a worker starts and that operation later becomes &lt;code&gt;refused&lt;/code&gt;; the
provider is never called.&lt;/p&gt;</description></item><item><title>A Command Bus Must Pay for Its Indirection</title><link>https://vacua.dev/a-command-bus-must-pay-for-its-indirection/</link><pubDate>Wed, 01 Mar 2023 00:00:00 +0000</pubDate><guid>https://vacua.dev/a-command-bus-must-pay-for-its-indirection/</guid><description>&lt;p&gt;Four Laravel routes handle two commands in the &lt;a href="https://vacua.dev/reproductions/cmd-02/"&gt;reproducible
fixture&lt;/a&gt;
. Two routes inject typed handlers. Two inject Laravel&amp;rsquo;s
dispatcher. All four return 204, and both call graphs leave the same SQLite
rows behind.&lt;/p&gt;
&lt;p&gt;Then one line disappears from the dispatcher&amp;rsquo;s handler map. The bus route
still resolves its controller, constructs &lt;code&gt;CancelOrder&lt;/code&gt;, and enters the
pipeline. It returns 500 without selecting a handler. The direct controller
has no equivalent registry entry to lose because its constructor already
names &lt;code&gt;DirectCancelOrderHandler&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>A Valid Command Can Still Be Refused</title><link>https://vacua.dev/a-valid-command-can-still-be-refused/</link><pubDate>Wed, 01 Feb 2023 00:00:00 +0000</pubDate><guid>https://vacua.dev/a-valid-command-can-still-be-refused/</guid><description>&lt;p&gt;Send this cancellation request to two orders:&lt;/p&gt;
&lt;figure class="code-figure"&gt;
 &lt;div class="code-block" data-copy-code&gt;
 &lt;button class="copy-code" type="button" aria-label="Copy code"&gt;copy&lt;/button&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;reason&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;customer_request&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
 &lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;The placed order returns 204 and becomes cancelled. The shipped order returns
409 and remains shipped. Both requests pass the same Laravel Form Request.&lt;/p&gt;
&lt;p&gt;Change the reason to &lt;code&gt;unknown&lt;/code&gt; and the result is different again: Laravel
returns 422 before the controller constructs a command. Three requests crossed
two different decision boundaries. Calling one result “valid” and the others
“invalid” hides the part of the system which made each decision.&lt;/p&gt;</description></item></channel></rss>