<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Persistence on Brian Faust</title><link>https://wimdy.works/tags/persistence/</link><description>Recent content in Persistence 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://wimdy.works/tags/persistence/rss.xml" rel="self" type="application/rss+xml"/><item><title>A Version Column Is Not Concurrency Control</title><link>https://wimdy.works/thoughts/a-version-column-is-not-concurrency-control/</link><pubDate>Sun, 01 Jan 2023 00:00:00 +0000</pubDate><guid>https://wimdy.works/thoughts/a-version-column-is-not-concurrency-control/</guid><description>&lt;p&gt;A capacity pool has ten units available. Two workers load it before either one
writes:&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;span class="sr-only" data-copy-status role="status" aria-live="polite"&gt;&lt;/span&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;worker A loads: version 0, reserved 0
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;worker B loads: version 0, reserved 0
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;worker A allocates 6 to allocation-a
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;worker B allocates 6 to allocation-b&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
 &lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;Both decisions pass the aggregate&amp;rsquo;s rule. Each worker sees ten remaining
units. They cannot both belong to one ordered history: after either allocation,
only four remain.&lt;/p&gt;</description></item><item><title>Loading State Is Not Creating It Again</title><link>https://wimdy.works/thoughts/loading-state-is-not-creating-it-again/</link><pubDate>Thu, 01 Dec 2022 00:00:00 +0000</pubDate><guid>https://wimdy.works/thoughts/loading-state-is-not-creating-it-again/</guid><description>&lt;p&gt;An order placed in 2024 is still in the database:&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;span class="sr-only" data-copy-status role="status" aria-live="polite"&gt;&lt;/span&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;number=LEGACY-42
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;status=placed
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;fulfil_by=2024-12-20T10:00:00Z
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;version=7&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
 &lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;Today, new web orders need a number beginning with &lt;code&gt;WEB-&lt;/code&gt; and a fulfilment
deadline in the future. Pass that stored row through today&amp;rsquo;s creation factory
and one of two bad things happens. The factory rejects valid history, or the
load pretends that placement happened again and records a new &lt;code&gt;OrderPlaced&lt;/code&gt;
fact.&lt;/p&gt;</description></item><item><title>A Mapper Connects Two Honest Models</title><link>https://wimdy.works/thoughts/a-mapper-connects-two-honest-models/</link><pubDate>Tue, 01 Nov 2022 00:00:00 +0000</pubDate><guid>https://wimdy.works/thoughts/a-mapper-connects-two-honest-models/</guid><description>&lt;p&gt;An order in the domain has an &lt;code&gt;OrderNumber&lt;/code&gt;, a &lt;code&gt;Money&lt;/code&gt; total, typed lines, and
an operation named &lt;code&gt;place()&lt;/code&gt;. The same order in a relational database has an
&lt;code&gt;orders&lt;/code&gt; row, several &lt;code&gt;order_lines&lt;/code&gt; rows, integer minor units, a nullable
deadline, check constraints, a foreign key, and an index used by fulfilment.&lt;/p&gt;
&lt;p&gt;Forcing those shapes to match exactly damages one side or the other.&lt;/p&gt;
&lt;p&gt;Make the domain object mirror the tables and it acquires column names, nullable
intermediate state, relationship-loading concerns, and methods shaped around
persistence. Store the object as one opaque document and the database loses
straightforward constraints and access paths required by the selected query.&lt;/p&gt;</description></item><item><title>A Repository Is a Domain Collection, Not an ORM Wrapper</title><link>https://wimdy.works/thoughts/what-repositories-really-do/</link><pubDate>Sat, 01 Oct 2022 00:00:00 +0000</pubDate><guid>https://wimdy.works/thoughts/what-repositories-really-do/</guid><description>&lt;p&gt;This interface appears in many Laravel codebases:&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;span class="sr-only" data-copy-status role="status" aria-live="polite"&gt;&lt;/span&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-php" data-lang="php"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Repository&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;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Collection&lt;/span&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="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="nx"&gt;Model&lt;/span&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="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$attributes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Model&lt;/span&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="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$attributes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Model&lt;/span&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="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;bool&lt;/span&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="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;It hides the word Eloquent and preserves everything else about Eloquent&amp;rsquo;s
world: models, attribute arrays, record IDs, and CRUD operations. Callers still
need to know which fields are writable, which relationships are loaded, and
which operations preserve domain rules.&lt;/p&gt;
&lt;p&gt;That is not a useful persistence boundary. It is a smaller, less capable ORM,
which is an odd thing to spend an interface on.&lt;/p&gt;</description></item><item><title>A Composition Root Makes Choices Visible</title><link>https://wimdy.works/thoughts/a-composition-root-makes-choices-visible/</link><pubDate>Thu, 01 Sep 2022 00:00:00 +0000</pubDate><guid>https://wimdy.works/thoughts/a-composition-root-makes-choices-visible/</guid><description>&lt;p&gt;Remove the &lt;code&gt;ProductPrices&lt;/code&gt; binding from a Laravel service provider, then ask
the container for &lt;code&gt;QuoteProduct&lt;/code&gt;. Laravel refuses to construct it:&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;span class="sr-only" data-copy-status role="status" aria-live="polite"&gt;&lt;/span&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Target [App\Application\ProductPrices] is not instantiable while building
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[App\Application\QuoteProduct].&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
 &lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;Move the same lookup into &lt;code&gt;QuoteProduct::execute()&lt;/code&gt; and construction succeeds.
That can look like simplification because the constructor now says nothing.
The object graph did not become simpler. Part of it moved into method code and
configuration, where Laravel cannot reject it until a quote reaches that line.&lt;/p&gt;</description></item><item><title>A Hexagon Is Not a Directory Layout</title><link>https://wimdy.works/thoughts/a-hexagon-is-not-a-directory-layout/</link><pubDate>Mon, 01 Aug 2022 00:00:00 +0000</pubDate><guid>https://wimdy.works/thoughts/a-hexagon-is-not-a-directory-layout/</guid><description>&lt;p&gt;A Laravel controller calls a catalogue API, reads &lt;code&gt;price_cents&lt;/code&gt;, and returns a
quote. Moving that HTTP code into &lt;code&gt;Application/QuoteProduct.php&lt;/code&gt; makes the
controller thinner. It also leaves the application service coupled to
Laravel&amp;rsquo;s HTTP client, the provider URL, and the provider&amp;rsquo;s JSON.&lt;/p&gt;
&lt;p&gt;The tree looks more architectural. The source dependencies have not moved.
That is architecture by folder name, and it buys almost nothing.&lt;/p&gt;
&lt;p&gt;That difference becomes visible when the catalogue replaces &lt;code&gt;price_cents&lt;/code&gt;
with &lt;code&gt;price.amount_minor&lt;/code&gt;. A folder name cannot decide where the resulting
change lands. A useful hexagonal boundary can: application policy defines the
conversation it needs, while an outside adapter owns the provider mechanism.&lt;/p&gt;</description></item></channel></rss>