<?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://vacua.dev/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://vacua.dev/tags/persistence/rss.xml" rel="self" type="application/rss+xml"/><item><title>A Version Column Is Not Concurrency Control</title><link>https://vacua.dev/a-version-column-is-not-concurrency-control/</link><pubDate>Sun, 01 Jan 2023 00:00:00 +0000</pubDate><guid>https://vacua.dev/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;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://vacua.dev/loading-state-is-not-creating-it-again/</link><pubDate>Thu, 01 Dec 2022 00:00:00 +0000</pubDate><guid>https://vacua.dev/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;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;
&lt;p&gt;Loading is not a second attempt at creation. Creation decides whether a new
transition is allowed now. Reconstitution restores state which was accepted
before, without claiming that the transition just occurred.&lt;/p&gt;</description></item><item><title>A Mapper Connects Two Honest Models</title><link>https://vacua.dev/a-mapper-connects-two-honest-models/</link><pubDate>Tue, 01 Nov 2022 00:00:00 +0000</pubDate><guid>https://vacua.dev/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://vacua.dev/what-repositories-really-do/</link><pubDate>Sat, 01 Oct 2022 00:00:00 +0000</pubDate><guid>https://vacua.dev/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;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.&lt;/p&gt;</description></item><item><title>A Composition Root Makes Choices Visible</title><link>https://vacua.dev/a-composition-root-makes-choices-visible/</link><pubDate>Thu, 01 Sep 2022 00:00:00 +0000</pubDate><guid>https://vacua.dev/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;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.
The missing dependency does not become visible until a quote request reaches
that line. The application object looks simpler because its constructor says
nothing. Its object graph is not simpler; part of the graph has moved into
method code and configuration.&lt;/p&gt;</description></item><item><title>A Hexagon Is Not a Directory Layout</title><link>https://vacua.dev/a-hexagon-is-not-a-directory-layout/</link><pubDate>Mon, 01 Aug 2022 00:00:00 +0000</pubDate><guid>https://vacua.dev/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.&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>