Skip to content

Commit

Permalink
update blog and html
Browse files Browse the repository at this point in the history
  • Loading branch information
hjwp committed Aug 2, 2022
1 parent febc000 commit d429cea
Show file tree
Hide file tree
Showing 33 changed files with 2,075 additions and 1,993 deletions.
6 changes: 0 additions & 6 deletions blog/2017-09-07-introducing-command-handler.html
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,6 @@ <h1> Introducing Command Handler</h1>
<span class="n">problem_description</span><span class="p">:</span> <span class="nb">str</span>
</code></pre></div>


<p>A command object is a small object that represents a state-changing action that
can happen in the system. Commands have no behaviour, they&rsquo;re pure data
structures. There&rsquo;s no reason why you have to represent them with classes, since
Expand Down Expand Up @@ -209,7 +208,6 @@ <h1> Introducing Command Handler</h1>
<span class="bp">self</span><span class="o">.</span><span class="n">issue_log</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">issue</span><span class="p">)</span>
</code></pre></div>


<p>Command handlers are stateless objects that orchestrate the behaviour of a
system. They are a kind of glue code, and manage the boring work of fetching and
saving objects, and then notifying other parts of the system. In keeping with
Expand Down Expand Up @@ -240,7 +238,6 @@ <h1> Introducing Command Handler</h1>
<span class="n">issue</span><span class="o">.</span><span class="n">mark_as_resolved</span><span class="p">(</span><span class="n">cmd</span><span class="o">.</span><span class="n">resolution</span><span class="p">)</span>
</code></pre></div>


<p>This handler violates our glue-code principle because it encodes a business
rule: &ldquo;If an issue is already resolved, then it can&rsquo;t be resolved a second
time&rdquo;. This rule belongs in our domain model, probably in the mark_as_resolved
Expand All @@ -258,15 +255,13 @@ <h1> Introducing Command Handler</h1>
<span class="n">issue_log</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">issue</span><span class="p">)</span>
</code></pre></div>


<p>If magic methods make you feel queasy, you can define a handler to be a class
that exposes a handle method like this:</p>
<div class="codehilite"><pre><span></span><code><span class="k">class</span> <span class="nc">ReportIssueHandler</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">handle</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">cmd</span><span class="p">):</span>
<span class="o">...</span>
</code></pre></div>


<p>However you structure them, the important ideas of commands and handlers are:</p>
<ol>
<li>Commands are logic-free data structures with a name and a bunch of values.</li>
Expand Down Expand Up @@ -375,7 +370,6 @@ <h1> Introducing Command Handler</h1>
<span class="n">expect</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">issues</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">description</span><span class="p">)</span><span class="o">.</span><span class="n">to</span><span class="p">(</span><span class="n">equal</span><span class="p">(</span><span class="n">desc</span><span class="p">))</span>
</code></pre></div>


<p>There&rsquo;s not a lot of functionality here, and our issue log has a couple of
problems, firstly there&rsquo;s no way to see the issues in the log yet, and secondly
we&rsquo;ll lose all of our data every time we restart the process. We&rsquo;ll fix the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ <h1> Repository and Unit of Work Pattern</h1>
<span class="n">issue_log</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">issue</span><span class="p">)</span>
</code></pre></div>


<p>The IssueLog is a term from our conversation with the domain expert. It&rsquo;s the
place that they record the list of all issues. This is part of the jargon used
by our customers, and so it clearly belongs in the domain, but it&rsquo;s also the
Expand Down Expand Up @@ -106,7 +105,6 @@ <h1> Repository and Unit of Work Pattern</h1>
<span class="bp">self</span><span class="o">.</span><span class="n">output</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</code></pre></div>


<p>Because we had the great foresight to use standardised ports, we can plug any
number of different devices into our circuit. For example, we could attach a
light-detector to the input and a buzzer to the output, or we could attach a
Expand All @@ -133,7 +131,6 @@ <h1> Repository and Unit of Work Pattern</h1>
<span class="bp">self</span><span class="o">.</span><span class="n">on</span> <span class="o">=</span> <span class="kc">False</span>
</code></pre></div>


<p>Considered in isolation, this is just an example of good OO practice: we are
extending our system through composition. What makes this a ports-and-adapters
architecture is the idea that there is an internal world consisting of the
Expand Down Expand Up @@ -161,7 +158,6 @@ <h1> Repository and Unit of Work Pattern</h1>
<span class="n">json</span><span class="o">.</span><span class="n">dump</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
</code></pre></div>


<p>By analogy to our circuit example, the IssueLog is a WriteablePort - it&rsquo;s a way
for us to get data out of the system. SqlAlchemy and the file system are two
types of adapter that we can plug in, just like the Buzzer or Light classes. In
Expand All @@ -186,7 +182,6 @@ <h1> Repository and Unit of Work Pattern</h1>
<span class="nb">filter</span><span class="p">(</span><span class="n">foo</span><span class="o">.</span><span class="n">latitude</span> <span class="o">==</span> <span class="n">latitude</span><span class="p">)</span>
</code></pre></div>


<p>We expose a few methods, one to add new items, one to get items by their id, and
a third to find items by some criterion. This FooRepository is using a
SqlAlchemy session
Expand All @@ -209,7 +204,6 @@ <h1> Repository and Unit of Work Pattern</h1>
<span class="k">if</span> <span class="n">item</span><span class="o">.</span><span class="n">latitude</span> <span class="o">==</span> <span class="n">latitude</span><span class="p">)</span>
</code></pre></div>


<p>This adapter works just the same as the one backed by a real database, but does
so without any external state. This allows us to test our code without resorting
to Setup/Teardown scripts on our database, or monkey patching our ORM to return
Expand Down Expand Up @@ -267,7 +261,6 @@ <h1> Repository and Unit of Work Pattern</h1>
<span class="k">return</span> <span class="n">IssueRepository</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">session</span><span class="p">)</span>
</code></pre></div>


<p>This code is taken from a current production system - the code to implement
these patterns really isn&rsquo;t complex. The only thing missing here is some logging
and error handling in the commit method. Our unit-of-work manager creates a new
Expand All @@ -287,7 +280,6 @@ <h1> Repository and Unit of Work Pattern</h1>
<span class="n">unit_of_work</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
</code></pre></div>


<p>Our command handler looks more or less the same, except that it&rsquo;s now
responsible for starting a unit-of-work, and committing the unit-of-work when it
has finished. This is in keeping with our rule #1 - we will clearly define the
Expand Down Expand Up @@ -341,7 +333,6 @@ <h1> Repository and Unit of Work Pattern</h1>
<span class="n">expect</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">uow</span><span class="o">.</span><span class="n">was_committed</span><span class="p">)</span><span class="o">.</span><span class="n">to</span><span class="p">(</span><span class="n">be_true</span><span class="p">)</span>
</code></pre></div>


<p>Next time [https://io.made.com/blog/commands-and-queries-handlers-and-views]
we&rsquo;ll look at how to get data back out of the system.</p>
</div>
Expand Down
7 changes: 0 additions & 7 deletions blog/2017-09-13-commands-and-queries-handlers-and-views.html
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ <h3>What is CQS ?</h3>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">light_is_on</span>
</code></pre></div>


<p>In this class, the is_on method is referentially transparent - I can replace it
with the value True or False without any loss of functionality, but the method
toggle_light is side-effectual: replacing its calls with a static value would
Expand Down Expand Up @@ -116,7 +115,6 @@ <h3>What is CQS ?</h3>
<span class="k">return</span> <span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">open_issues</span><span class="p">)</span>
</code></pre></div>


<p>This is totally fine unless you have complex formatting, or multiple entrypoints
to your system. The problem with using your repositories directly in this way is
that it&rsquo;s a slippery slope. Sooner or later you&rsquo;re going to have a tight
Expand All @@ -130,7 +128,6 @@ <h3>What is CQS ?</h3>
<span class="n">uow</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
</code></pre></div>


<p>Super convenient, but then you need to add some error handling and some logging
and an email notification.</p>
<div class="codehilite"><pre><span></span><code><span class="nd">@app</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s1">&#39;/issues/&lt;issue_id&gt;&#39;</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s1">&#39;DELETE&#39;</span><span class="p">])</span>
Expand All @@ -157,7 +154,6 @@ <h3>What is CQS ?</h3>
<span class="k">return</span> <span class="s2">&quot;Deleted!&quot;</span><span class="p">,</span> <span class="mi">202</span>
</code></pre></div>


<p>Aaaaand, we&rsquo;re back to where we started: business logic mixed with glue code,
and the whole mess slowly congealing in our web controllers. Of course, the
slippery slope argument isn&rsquo;t a good reason not to do something, so if your
Expand Down Expand Up @@ -185,7 +181,6 @@ <h3>What is CQS ?</h3>
<span class="k">return</span> <span class="n">jsonify</span><span class="p">(</span><span class="n">view_builder</span><span class="o">.</span><span class="n">fetch</span><span class="p">())</span>
</code></pre></div>


<p>This is my favourite part of teaching ports and adapters to junior programmers,
because the conversation inevitably goes like this:</p>
<blockquote>
Expand Down Expand Up @@ -225,7 +220,6 @@ <h3>Why have a separate read-model?</h3>
<span class="n">assignee</span><span class="o">.</span><span class="n">queues</span><span class="o">.</span><span class="n">inbox</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">task</span><span class="p">)</span>
</code></pre></div>


<p>ORMs make it very easy to &ldquo;dot&rdquo; through the object model this way, and pretend
that we have our data in memory, but this quickly leads to performance issues
when the ORM generates hundreds of select statements in response. Then they get
Expand Down Expand Up @@ -310,7 +304,6 @@ <h3>Application Controlled Identifiers</h3>
<span class="k">return</span> <span class="s2">&quot;&quot;</span><span class="p">,</span> <span class="mi">201</span><span class="p">,</span> <span class="p">{</span> <span class="s1">&#39;Location&#39;</span><span class="p">:</span> <span class="s1">&#39;/issues/&#39;</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">issue_id</span><span class="p">)</span> <span class="p">}</span>
</code></pre></div>


<p>There&rsquo;s a few ways to do this, the most common is just to use a UUID, but you
can also implement something like
<a href="https://pypi.python.org/pypi/sqlalchemy-hilo/0.1.2">hi-lo</a>.
Expand Down
13 changes: 0 additions & 13 deletions blog/2017-09-19-why-use-domain-events.html
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,6 @@ <h2>Mapping our requirements to our domain</h2>
<span class="n">expect</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">issue</span><span class="o">.</span><span class="n">state</span><span class="p">)</span><span class="o">.</span><span class="n">to</span><span class="p">(</span><span class="n">equal</span><span class="p">(</span><span class="n">IssueState</span><span class="o">.</span><span class="n">AwaitingTriage</span><span class="p">))</span>
</code></pre></div>


<p>We&rsquo;re introducing a new concept - Issues now have a state, and a newly reported
issue begins in the AwaitingTriage state. We can quickly add a command and
handler that allows us to triage an issue.</p>
Expand All @@ -161,7 +160,6 @@ <h2>Mapping our requirements to our domain</h2>
<span class="n">uow</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
</code></pre></div>


<p>Triaging an issue, for now, is a matter of selecting a category and priority.
We&rsquo;ll use a free string for category, and an enumeration for Priority. Once an
issue is triaged, it enters the AwaitingAssignment state. At some point we&rsquo;ll
Expand All @@ -180,7 +178,6 @@ <h2>Mapping our requirements to our domain</h2>
<span class="n">uow</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
</code></pre></div>


<p>At this point, the handlers are becoming a little boring. As I said way back in
the first part [https://io.made.com/blog/introducing-command-handler/], commands
handlers are supposed to be boring glue-code, and every command handler has the
Expand Down Expand Up @@ -225,7 +222,6 @@ <h2>Mapping our requirements to our domain</h2>
<span class="bp">self</span><span class="o">.</span><span class="n">email_sender</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="n">email</span><span class="p">)</span>
</code></pre></div>


<p>Something here feels wrong, right? Our command-handler now has two very distinct
responsibilities. Back at the beginning of this series we said we would stick
with three principles:</p>
Expand Down Expand Up @@ -290,7 +286,6 @@ <h2>Mapping our requirements to our domain</h2>
<span class="bp">self</span><span class="o">.</span><span class="n">email_sender</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="n">email</span><span class="p">)</span>
</code></pre></div>


<p>We don&rsquo;t really need a unit of work here, because we&rsquo;re not making any
persistent changes to the Issue state, so what if we use a view builder instead?</p>
<div class="codehilite"><pre><span></span><code><span class="k">class</span> <span class="nc">SendAssignmentEmailHandler</span>
Expand All @@ -312,7 +307,6 @@ <h2>Mapping our requirements to our domain</h2>
<span class="bp">self</span><span class="o">.</span><span class="n">email_sender</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="n">email</span><span class="p">)</span>
</code></pre></div>


<p>That seems better, but how should we invoke our new handler? Building a new
command and handler from inside our AssignIssueHandler also sounds like a
violation of SRP. Worse still, if we start calling handlers from handlers, we&rsquo;ll
Expand Down Expand Up @@ -386,7 +380,6 @@ <h2>Mapping our requirements to our domain</h2>
<span class="n">bus</span><span class="o">.</span><span class="n">handle</span><span class="p">(</span><span class="n">cmd</span><span class="p">)</span>
</code></pre></div>


<p>Here we have a bare-bones implementation of a message bus. It doesn&rsquo;t do
anything fancy, but it will do the job for now. In a production system, the
message bus is an excellent place to put cross-cutting concerns; for example, we
Expand All @@ -404,7 +397,6 @@ <h2>Mapping our requirements to our domain</h2>
<span class="k">return</span> <span class="s2">&quot;&quot;</span><span class="p">,</span> <span class="mi">201</span><span class="p">,</span> <span class="p">{</span><span class="s2">&quot;Location&quot;</span><span class="p">:</span> <span class="s2">&quot;/issues/&quot;</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">issue_id</span><span class="p">)</span> <span class="p">}</span>
</code></pre></div>


<p>Not much has changed here - we&rsquo;re still building our command in the Flask
adapter, but now we&rsquo;re passing it into a bus instead of directly constructing a
handler for ourselves. What about when we need to raise an event? We&rsquo;ve got
Expand All @@ -425,7 +417,6 @@ <h2>Mapping our requirements to our domain</h2>
<span class="n">cmd</span><span class="o">.</span><span class="n">assigned_by</span><span class="p">))</span>
</code></pre></div>


<p>I usually think of this event-raising as a kind of glue - it&rsquo;s orchestration
code. Raising events from your handlers this way makes the flow of messages
explicit - you don&rsquo;t have to look anywhere else in the system to understand
Expand All @@ -445,7 +436,6 @@ <h2>Mapping our requirements to our domain</h2>
<span class="bp">self</span><span class="o">.</span><span class="n">events</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">IssueAssignedToEngineer</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">id</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">assigned_to</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">assigned_by</span><span class="p">))</span>
</code></pre></div>


<p>There&rsquo;s a couple of benefits of doing this: firstly, it keeps our command
handler simpler, but secondly it pushes the logic for deciding when to send an
event into the model. For example, maybe we don&rsquo;t always need to raise the
Expand All @@ -461,7 +451,6 @@ <h2>Mapping our requirements to our domain</h2>
<span class="bp">self</span><span class="o">.</span><span class="n">events</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">IssueAssignedToEngineer</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">id</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">assigned_to</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">assigned_by</span><span class="p">))</span>
</code></pre></div>


<p>Now we&rsquo;ll only raise our event if the issue was assigned by another engineer.
Cases like this are more like business logic than glue code, so today I&rsquo;m
choosing to put them in my domain model. Updating our unit tests is trivial,
Expand All @@ -485,7 +474,6 @@ <h2>Mapping our requirements to our domain</h2>
<span class="bp">self</span><span class="o">.</span><span class="n">assigned_by</span><span class="p">)))</span>
</code></pre></div>


<p>The have_raised function is a custom matcher I wrote that checks the events
attribute of our object to see if we raised the correct event. It&rsquo;s easy to test
for the presence of events, because they&rsquo;re namedtuples, and have value
Expand Down Expand Up @@ -540,7 +528,6 @@ <h2>Mapping our requirements to our domain</h2>
<span class="bp">self</span><span class="o">.</span><span class="n">publish_events</span><span class="p">()</span>
</code></pre></div>


<p>Okay, we&rsquo;ve covered a lot of ground here. We&rsquo;ve discussed why you might want to
use domain events, how a message bus actually works in practice, and how we can
get events out of our domain and into our subscribers. The newest code sample
Expand Down
2 changes: 0 additions & 2 deletions blog/2019-04-15-inversion-of-control.html
Original file line number Diff line number Diff line change
Expand Up @@ -140,12 +140,10 @@ <h1>Conclusion: complex is better than complicated</h1>
<div class="codehilite"><pre><span></span><code>Simple is better than complex.
</code></pre></div>


<p>But also that</p>
<div class="codehilite"><pre><span></span><code>Complex is better than complicated.
</code></pre></div>


<p>I think of inversion of control as an example of choosing the complex over the complicated. If we don&rsquo;t use it when
it&rsquo;s needed, our efforts to create a simple system will tangle into complications. Inverting dependencies allows us,
at the cost of a small amount of complexity, to make our systems less complicated.</p>
Expand Down
Loading

0 comments on commit d429cea

Please sign in to comment.