<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Object-Design on Brian Faust</title><link>https://vacua.dev/tags/object-design/</link><description>Recent content in Object-Design 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/object-design/rss.xml" rel="self" type="application/rss+xml"/><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 Constructor Should Leave a Usable Object</title><link>https://vacua.dev/a-constructor-should-leave-a-usable-object/</link><pubDate>Wed, 01 Dec 2021 00:00:00 +0000</pubDate><guid>https://vacua.dev/a-constructor-should-leave-a-usable-object/</guid><description>&lt;p&gt;This &lt;code&gt;Subscription&lt;/code&gt; exists before it can safely answer a question:&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="nv"&gt;$subscription&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Subscription&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="nv"&gt;$subscription&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$ids&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;next&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="nv"&gt;$subscription&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;customerId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$customerId&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="nv"&gt;$subscription&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;plan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$plans&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$planCode&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="nv"&gt;$subscription&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;trial&amp;#39;&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="nv"&gt;$subscription&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;startsAt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$clock&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;now&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="nv"&gt;$subscription&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;trialEndsAt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$subscription&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;startsAt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;modify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;+14 days&amp;#39;&lt;/span&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;Between the first and last line, another method can observe an object without a
plan, status, or start time. Forget the last assignment and an active
subscription can carry a trial end. Reorder the calls and identity may be
consumed for a plan which does not exist.&lt;/p&gt;
&lt;p&gt;Adding a factory does not automatically repair this. Neither does replacing
&lt;code&gt;new Subscription()&lt;/code&gt; with &lt;code&gt;Subscription::create()&lt;/code&gt;. The useful question is
which boundary owns each decision.&lt;/p&gt;</description></item><item><title>Strategy Chooses How; Command Records What to Do</title><link>https://vacua.dev/strategy-vs-command-pattern/</link><pubDate>Sun, 01 Aug 2021 00:00:00 +0000</pubDate><guid>https://vacua.dev/strategy-vs-command-pattern/</guid><description>&lt;p&gt;Two PHP types can have almost identical shapes and still solve different
problems:&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;ShippingRate&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;quote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Shipment&lt;/span&gt; &lt;span class="nv"&gt;$shipment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Money&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;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;&lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ApplicationCommand&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;execute&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;void&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;Both hide behavior behind a method. Both can be injected and tested. Their
surface similarity makes “algorithm versus action” an easy first distinction,
but it is not enough for application architecture.&lt;/p&gt;
&lt;p&gt;The more useful distinction appears after execution fails. A strategy is a
collaborator used while another operation is already in progress. A command
represents work that can acquire its own lifecycle. Once work must be queued,
retried, deduplicated, authorized, or audited independently, it is no longer
merely a choice of implementation.&lt;/p&gt;</description></item><item><title>Composition Over Inheritance Is a Decision, Not a Rule</title><link>https://vacua.dev/composition-vs-inheritance/</link><pubDate>Thu, 01 Jul 2021 00:00:00 +0000</pubDate><guid>https://vacua.dev/composition-vs-inheritance/</guid><description>&lt;p&gt;“Favor composition over inheritance” is useful advice right up to the point
where it becomes a reflex. Replacing every parent class with an interface and a
service-container binding does not make a design better. It merely exchanges
one form of coupling for another.&lt;/p&gt;
&lt;p&gt;The decision is about how behavior is allowed to vary.&lt;/p&gt;
&lt;p&gt;Inheritance fixes variation into a type hierarchy. Composition puts variation
behind collaborators that can change independently. When tax, discounts,
shipping, and payment rules evolve along different axes, forcing them into one
family tree is expensive. When a subtype genuinely preserves a small, stable
parent contract, inheritance may be the clearest tool available.&lt;/p&gt;</description></item></channel></rss>