<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><generator uri="https://jekyllrb.com/" version="4.2.0">Jekyll</generator><link href="https://jamejone.github.io/feed.xml" rel="self" type="application/atom+xml"/><link href="https://jamejone.github.io/" rel="alternate" type="text/html"/><updated>2021-06-13T18:09:43+00:00</updated><id>https://jamejone.github.io/feed.xml</id><title type="html">So long, and thanks for all the bits</title><author><name>James Jones</name><email>james.russel.jones@gmail.com</email></author><entry><title type="html">The Ultimate MacBook+PC Monitor Showdown</title><link href="https://jamejone.github.io/2021/01/01/the-ultimate-macbook-pc-monitor-showdown/" rel="alternate" type="text/html" title="The Ultimate MacBook+PC Monitor Showdown"/><published>2021-01-01T00:00:00+00:00</published><updated>2021-06-13T18:07:01+00:00</updated><id>https://jamejone.github.io/2021/01/01/the-ultimate-macbook-pc-monitor-showdown</id><content type="html" xml:base="https://jamejone.github.io/2021/01/01/the-ultimate-macbook-pc-monitor-showdown/">&lt;p&gt;Like many folks finding their way through the COVID-19 pandemic, I’ve &lt;a href=&quot;https://twitter.com/james_output/status/1320110838792003584&quot;&gt;recently accepted a job&lt;/a&gt; working permanently remote. So for the foreseeable future my workstation will be pulling double-duty for macOS-based software development with a MacBook Pro and Windows-based gaming on a PC.&lt;/p&gt; &lt;p&gt;Modern monitors come with a lot of interesting features. Did you know you can connect a MacBook to a monitor using a single USB-C cable and transmit USB, power, audio &lt;strong&gt;&lt;em&gt;and&lt;/em&gt;&lt;/strong&gt; video?&lt;/p&gt; &lt;p&gt;Unfortunately, it’s not as simple as knowing whether a monitor supports USB-C. Many monitors support USB-C but don’t have any sort of way to pipe the audio out from the monitor (for example, to a sound bar). And some monitors have USB-C and audio out, but only provide 15W of power.&lt;/p&gt; &lt;p&gt;With enough features, the monitor effectively acts as a KVM+audio, like so:&lt;/p&gt; &lt;picture&gt;&lt;source srcset=&quot;/generated/public/usb-c-monitor-diagram-400-1075c6361.webp 400w, /generated/public/usb-c-monitor-diagram-600-1075c6361.webp 600w, /generated/public/usb-c-monitor-diagram-678-1075c6361.webp 678w&quot; type=&quot;image/webp&quot; /&gt;&lt;source srcset=&quot;/generated/public/usb-c-monitor-diagram-400-1075c6361.png 400w, /generated/public/usb-c-monitor-diagram-600-1075c6361.png 600w, /generated/public/usb-c-monitor-diagram-678-1075c6361.png 678w&quot; type=&quot;image/png&quot; /&gt;&lt;img src=&quot;/generated/public/usb-c-monitor-diagram-678-1075c6361.png&quot; alt=&quot;USB-C Monitor Diagram&quot; /&gt;&lt;/picture&gt; &lt;p&gt;So I set out to identify a monitor that meets all of my criteria. Specifically:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Has a built-in KVM switch that lets me easily switch between my PC and MacBook.&lt;/li&gt; &lt;li&gt;Provides at least 60W power over USB-C (the bare minimum to power a 16-inch MacBook Pro).&lt;/li&gt; &lt;li&gt;Some sort of audio output (almost always analog).&lt;/li&gt; &lt;li&gt;Upstream USB-B Port so that I can plug my keyboard and mouse into the monitor and pipe PC audio through the monitor to the sound bar.&lt;/li&gt; &lt;li&gt;VESA mountable.&lt;/li&gt; &lt;li&gt;IPS panel. The viewing angles on my TN panel are so bad it interferes with my work so IPS is a must.&lt;/li&gt; &lt;li&gt;27” or 34” ultrawide @ 1440p. Good for gaming and happens to be &lt;a href=&quot;https://bjango.com/articles/macexternaldisplays/&quot;&gt;the ideal non-retina DPI for macOS&lt;/a&gt;. Full retina @ 5k instead of 1440p would be nice, but unfortunately I couldn’t find such a monitor that meets all the minimum criteria. Awkward DPIs are a deal-breaker due to scaling and “shimmering” effects mentioned in the linked post.&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Nice-to-haves:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Adaptive sync (AS).&lt;/li&gt; &lt;li&gt;144hz refresh rate. At 60hz I lose track of my mouse cursor in League of Legends and we can’t have that!&lt;/li&gt; &lt;li&gt;90W power delivery over USB-C.&lt;/li&gt; &lt;li&gt;Reasonable price.&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Here’s what I found after about 15 hours of research:&lt;/p&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt;Model&lt;/th&gt; &lt;th&gt;Size&lt;/th&gt; &lt;th&gt;Pros&lt;/th&gt; &lt;th&gt;Cons&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;ViewSonic VP2771&lt;/td&gt; &lt;td&gt;27” 1440p&lt;/td&gt; &lt;td&gt;$200-$450&lt;/td&gt; &lt;td&gt;60hz, 60W USB-C&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;LG 34WK95C-W&lt;/td&gt; &lt;td&gt;34” 1440p UW&lt;/td&gt; &lt;td&gt;Adaptive Sync&lt;/td&gt; &lt;td&gt;75hz, 60W USB-C, ~$1,000&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Dell U3421WE&lt;/td&gt; &lt;td&gt;34” 1440p UW&lt;/td&gt; &lt;td&gt;90W USB-C&lt;/td&gt; &lt;td&gt;60hz, ~$1,000&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;LG 34WK95U&lt;/td&gt; &lt;td&gt;34” 4k UW&lt;/td&gt; &lt;td&gt;85W USB-C&lt;/td&gt; &lt;td&gt;60hz, ~$1,300, awkward DPI&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Dell U3219Q&lt;/td&gt; &lt;td&gt;32” 4k&lt;/td&gt; &lt;td&gt;90W USB-C&lt;/td&gt; &lt;td&gt;60hz, ~$800, awkward DPI&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;BenQ PD3220U&lt;/td&gt; &lt;td&gt;32” 4k&lt;/td&gt; &lt;td&gt;85W USB-C&lt;/td&gt; &lt;td&gt;60hz, ~$1,200, awkward DPI&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Razer Raptor 27&lt;/td&gt; &lt;td&gt;27” 1440p&lt;/td&gt; &lt;td&gt;AS, 144hz, HDR&lt;/td&gt; &lt;td&gt;15W USB-C, No VESA mount, $700&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Philips 346B1C&lt;/td&gt; &lt;td&gt;34” 1440p UW&lt;/td&gt; &lt;td&gt;90W, AS, $450&lt;/td&gt; &lt;td&gt;100hz, VA panel&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Acer Predator X34 S&lt;/td&gt; &lt;td&gt;34” 1440p UW&lt;/td&gt; &lt;td&gt;&lt;strong&gt;200hz&lt;/strong&gt;, AS, 85W&lt;/td&gt; &lt;td&gt;Not available (yet)&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;p&gt;I can’t guarantee this is an exhaustive list, but it might be. I left off monitors that are no longer for sale or lack crucial features like an audio output or upstream USB-B port.&lt;/p&gt; &lt;p&gt;As you can see, there’s no ideal monitor except for the Acer Predator X34 S, which &lt;a href=&quot;https://www.tomshardware.com/news/acer-predator-x34-s-a-34-inch-200hz-nano-ips-curved-monitor-w-05ms-response-time&quot;&gt;isn’t available in the US yet and currently costs ¥9999&lt;/a&gt; (roughly $1,500 USD). There’s a similar model with a lower refresh rate, the X34 GS, however I can only find it &lt;a href=&quot;https://online.acer.com.au/acer/store/acer-predator-x34-gs-curved-gsync-gaming-monitor&quot;&gt;for sale in Australia&lt;/a&gt; for approximately $1,342 USD. In terms of what’s available today, you ultimately need to choose whether you want to sacrifice refresh rate, VESA mounting, USB-C charging, or panel type.&lt;/p&gt; &lt;p&gt;As luck would have it, I ended up snagging a ViewSonic VP2771 on eBay for $200 shipped. After all, I still have a 144hz TN panel that I can keep plugged into the gaming PC. It’s not ideal but it’ll work until I get my hands on that Acer.&lt;/p&gt; &lt;p&gt;Let me know in the comments if you find a monitor that should be in this list. If you want to play League of Legends together you can find me under the summoner name JamesJonesJrJr.&lt;/p&gt; &lt;p&gt;Stay safe out there, friends.&lt;/p&gt;</content><author><name>James Jones</name><email>james.russel.jones@gmail.com</email></author><summary type="html">Like many folks finding their way through the COVID-19 pandemic, I’ve recently accepted a job working permanently remote. So for the foreseeable future my workstation will be pulling double-duty for macOS-based software development with a MacBook Pro and Windows-based gaming on a PC.</summary></entry><entry><title type="html">HTTP PUT is dead, long live HTTP PUT</title><link href="https://jamejone.github.io/2020/02/27/put-is-dead/" rel="alternate" type="text/html" title="HTTP PUT is dead, long live HTTP PUT"/><published>2020-02-27T00:00:00+00:00</published><updated>2021-06-13T18:07:01+00:00</updated><id>https://jamejone.github.io/2020/02/27/put-is-dead</id><content type="html" xml:base="https://jamejone.github.io/2020/02/27/put-is-dead/">&lt;p&gt;Absent some sort of alien quantum technology, engineering client-server applications will always mean entertaining the possibility that clients will lag the server by at least one version.&lt;/p&gt; &lt;p&gt;Most people I know prefer to use the oldest version of software they can get away with. Updating software without a specific feature in mind means spending precious time learning a new interface and dealing with new bugs.&lt;/p&gt; &lt;p&gt;As a system engineer, you can respond a couple different ways:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;If you’re ok with your users hating you, you can &lt;a href=&quot;http://www.pushsquare.com/news/2019/12/ps4_firmware_update_7_02_is_ready_for_download_right_now&quot;&gt;force client updates&lt;/a&gt;.&lt;/li&gt; &lt;li&gt;Plan for the possibility that some clients will lag the server by at least one version.&lt;/li&gt; &lt;/ol&gt; &lt;h3 id=&quot;a-problematic-verb&quot;&gt;A problematic verb&lt;/h3&gt; &lt;p&gt;When it comes to RESTful API’s, the first HTTP verb that comes to mind when you’re editing a entity is almost always PUT. The problem with using PUT is that it lacks forward compatibility. Here’s why:&lt;/p&gt; &lt;p&gt;Step 1: Client v1.1 POSTs a new entity to the server:&lt;/p&gt; &lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Dave&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;age&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;47&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;Step 2: An older client (v1.0) GETs the entity client v1.1 created, transforms it to a model compatible with its UI, lets the user modify it, and then attempts to send it back to the server via HTTP PUT. The thing is, the ‘age’ property was just added in v1.1 and client v1.0 has no notion of it, so the payload it sends looks like this:&lt;/p&gt; &lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Whitmer&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;Because we’re using PUT, the server must replace the entire document in the database with this new document, so “age” is dropped and this data is lost. If an older client is unknowingly deleting data created by newer clients, you’re gonna have a bad time.&lt;/p&gt; &lt;p&gt;We can avoid having a bad time.&lt;/p&gt; &lt;h3 id=&quot;enter-json-merge-patch&quot;&gt;Enter JSON Merge Patch&lt;/h3&gt; &lt;p&gt;No, not &lt;a href=&quot;https://tools.ietf.org/html/rfc6902&quot;&gt;that other patch&lt;/a&gt;. &lt;a href=&quot;https://tools.ietf.org/html/rfc7386&quot;&gt;JSON Merge Patch&lt;/a&gt; is perfect because it’s almost exactly like PUT except it doesn’t delete fields it doesn’t know about.&lt;/p&gt; &lt;p&gt;In other words, when you omit a property in a Merge PATCH, the database will simply let that property remain whatever value it presently is. This provides forward compatibility and thus fixes the main issue we have with PUT.&lt;/p&gt; &lt;h3 id=&quot;in-closing-stop-using-http-put&quot;&gt;In closing, stop using HTTP PUT&lt;/h3&gt; &lt;p&gt;It’s just bad, mkay? Okay fine it’s actually not that bad. PUT can actually be pretty great &lt;a href=&quot;https://stackoverflow.com/questions/630453/put-vs-post-in-rest&quot;&gt;for other things&lt;/a&gt;, just consider PATCH instead when you’re trying to edit entities.&lt;/p&gt;</content><author><name>James Jones</name><email>james.russel.jones@gmail.com</email></author><summary type="html">Absent some sort of alien quantum technology, engineering client-server applications will always mean entertaining the possibility that clients will lag the server by at least one version.</summary></entry><entry><title type="html">Temporal repository implementation using MongoDB and ASP.NET Core</title><link href="https://jamejone.github.io/2019/11/14/temporal-repository-implementation-using-mongodb-and-aspnet-core/" rel="alternate" type="text/html" title="Temporal repository implementation using MongoDB and ASP.NET Core"/><published>2019-11-14T00:00:00+00:00</published><updated>2021-06-13T18:07:01+00:00</updated><id>https://jamejone.github.io/2019/11/14/temporal-repository-implementation-using-mongodb-and-aspnet-core</id><content type="html" xml:base="https://jamejone.github.io/2019/11/14/temporal-repository-implementation-using-mongodb-and-aspnet-core/">&lt;p&gt;&lt;a href=&quot;/2019/10/30/its-called-crud-for-a-reason/&quot;&gt;In my last post&lt;/a&gt; we covered why CRUD patterns can be inherently difficult to scale. If you’ve reached that point, congratulations, it’s time to upgrade your design. In this post we’ll cover a different kind of data access pattern that’s a little more complicated than CRUD, but offers some benefits that just might make it worthwhile.&lt;/p&gt; &lt;p&gt;The key to scalable systems is asyncronous processing. And the key to asyncronous processing is adopting data structures that facilitate asyncronous processing. One such data structure is the ‘temporal’ repository.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;temporal (ˈtemp(ə)rəl)&lt;/p&gt; &lt;p&gt;&lt;em&gt;adjective&lt;/em&gt;&lt;/p&gt; &lt;ol&gt; &lt;li&gt;relating to time.&lt;/li&gt; &lt;/ol&gt; &lt;/blockquote&gt; &lt;p&gt;The temporal repository is capable of knowing not just the current state of the entities within it &lt;em&gt;but also their prior states&lt;/em&gt;. This repo has the memory of an elephant and knows not just facts about the world, but also &lt;strong&gt;when&lt;/strong&gt; it came to know about them. Here’s what such an interface looks like:&lt;/p&gt; &lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ITemporalRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;/// Saves the entity to the database.&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;SaveAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;/// Gets the latest version of the entity from the database.&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;identifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;/// Gets a version of the entity as of a given point in time.&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;identifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;/// Gets the complete history of an entity.&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IAsyncEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetHistoryAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;identifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;/// Gets the latest versions of all the entities in the&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;/// database.&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IAsyncEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetAllAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;/// Gets all the entities from the database as of a given&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;/// point in time.&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IAsyncEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetAllAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;asOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;/// Purges historical versions of an entity beyond a given&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;/// point in time. Keeps however many versions specified.&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PurgeHistoricalVersionsAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DateTime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;howFarBackToPurge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;minVersionsToKeep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;As you can see, by enabling our data model to travel backwards in time, we end up with more than just the standard four CRUD routines. This interface enables us to do some pretty cool stuff, namely:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Coordinate multiple batch processes to asynchronously process the system as of an exact point in time. In other words, we have an immutable data set. We don’t have to worry about anyone pulling the rug out from under us and retries can be idempotent.&lt;/li&gt; &lt;li&gt;Perform diffs on the data, for example to understand what has changed since the last time a batch process was ran.&lt;/li&gt; &lt;li&gt;Provide an audit log, which is immensely useful for debugging and is oftentimes required to meet regulatory requirements.&lt;/li&gt; &lt;li&gt;Retroactively process data as of a specific point in time in the past (i.e. quarterly reporting).&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;You get all of this and more without ever needing to create a transaction.&lt;/p&gt; &lt;h3 id=&quot;see-it-in-action&quot;&gt;See it in action&lt;/h3&gt; &lt;p&gt;I’ve created a demo of this pattern using ASP.NET Core 3.0, MongoDB and Docker. One thing I love about Docker is that you don’t need to install MongoDB just to see this demo work. Simply run the Docker Compose file and you’re off to the races. I should also mention this can run on both macOS and Windows. I know because I developed this demo on both a MacBook and a Windows PC. 🤠&lt;/p&gt; &lt;p&gt;To run the demo I’d recommend installing the following software:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;a href=&quot;https://visualstudio.microsoft.com/downloads/&quot;&gt;Visual Studio 2019&lt;/a&gt;&lt;/li&gt; &lt;li&gt;Docker (along with Kitematic for managing containers)&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Next, &lt;a href=&quot;https://github.com/jamejone/temporal-repository-pattern&quot;&gt;clone or download the source from my GitHub&lt;/a&gt;. You should be able to simply open the solution in Visual Studio and run the docker-compose project. In Kitematic, use Ctrl + R (or ⌘ + R) to refresh the container list. You should see the list of Docker containers that were created and you should observe that the MongoDB cluster has formed:&lt;/p&gt; &lt;picture&gt;&lt;source srcset=&quot;/generated/public/temporal-repository-pattern-kitematic-400-78d9fb0f2.webp 400w, /generated/public/temporal-repository-pattern-kitematic-600-78d9fb0f2.webp 600w, /generated/public/temporal-repository-pattern-kitematic-800-78d9fb0f2.webp 800w, /generated/public/temporal-repository-pattern-kitematic-1000-78d9fb0f2.webp 1000w&quot; type=&quot;image/webp&quot; /&gt;&lt;source srcset=&quot;/generated/public/temporal-repository-pattern-kitematic-400-78d9fb0f2.png 400w, /generated/public/temporal-repository-pattern-kitematic-600-78d9fb0f2.png 600w, /generated/public/temporal-repository-pattern-kitematic-800-78d9fb0f2.png 800w, /generated/public/temporal-repository-pattern-kitematic-1000-78d9fb0f2.png 1000w&quot; type=&quot;image/png&quot; /&gt;&lt;img src=&quot;/generated/public/temporal-repository-pattern-kitematic-800-78d9fb0f2.png&quot; alt=&quot;'Kitematic shows that all the containers are running and the MongoDB cluster is formed.'&quot; /&gt;&lt;/picture&gt; &lt;p&gt;Next, use the Test Explorer in Visual Studio to run the integration tests and observe that they all pass:&lt;/p&gt; &lt;picture&gt;&lt;source srcset=&quot;/generated/public/temporal-repository-pattern-integration-tests-400-68d715f0f.webp 400w, /generated/public/temporal-repository-pattern-integration-tests-600-68d715f0f.webp 600w, /generated/public/temporal-repository-pattern-integration-tests-710-68d715f0f.webp 710w&quot; type=&quot;image/webp&quot; /&gt;&lt;source srcset=&quot;/generated/public/temporal-repository-pattern-integration-tests-400-68d715f0f.png 400w, /generated/public/temporal-repository-pattern-integration-tests-600-68d715f0f.png 600w, /generated/public/temporal-repository-pattern-integration-tests-710-68d715f0f.png 710w&quot; type=&quot;image/png&quot; /&gt;&lt;img src=&quot;/generated/public/temporal-repository-pattern-integration-tests-710-68d715f0f.png&quot; alt=&quot;'Visual Studio shows that all the integration tests are passing.'&quot; /&gt;&lt;/picture&gt; &lt;p&gt;I think I’ve said enough for one blog post. Go ahead, set some breakpoints and tinker around. If you’d like to connect to the local MongoDB cluster you can connect to it on the default port 27017. If you have MongoDB Compass you should be able to simply open it up and hit ‘Connect’.&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/public/make-like-a-tree-and-get-outta-here.gif&quot; alt=&quot;The way I see it, if you're going to build a time machine into a car, why not do it with some style?&quot; /&gt;&lt;/p&gt;</content><author><name>James Jones</name><email>james.russel.jones@gmail.com</email></author><summary type="html">In my last post we covered why CRUD patterns can be inherently difficult to scale. If you’ve reached that point, congratulations, it’s time to upgrade your design. In this post we’ll cover a different kind of data access pattern that’s a little more complicated than CRUD, but offers some benefits that just might make it worthwhile.</summary></entry><entry><title type="html">It’s called ‘CRUD’ for a reason</title><link href="https://jamejone.github.io/2019/10/30/its-called-crud-for-a-reason/" rel="alternate" type="text/html" title="It’s called ‘CRUD’ for a reason"/><published>2019-10-30T00:00:00+00:00</published><updated>2021-06-13T18:07:01+00:00</updated><id>https://jamejone.github.io/2019/10/30/its-called-crud-for-a-reason</id><content type="html" xml:base="https://jamejone.github.io/2019/10/30/its-called-crud-for-a-reason/">&lt;blockquote&gt; &lt;p&gt;&lt;em&gt;“To err is human, but to really foul things up you need a computer.”&lt;/em&gt;&lt;/p&gt; &lt;p&gt;-Paul R. Ehrlich&lt;/p&gt; &lt;/blockquote&gt; &lt;p&gt;Many business processes that exist today were born out of old fashioned paper/form-driven processes. This is because paper was invented before computers were invented. Had computers been invented first, you might imagine a completely different world in which you place an order online to buy something and your browser spins until the package arrives on your doorstep.&lt;/p&gt; &lt;p&gt;I’m kidding, sort of.&lt;/p&gt; &lt;p&gt;Many enterprise software systems are less exaggerated versions of this. In some cases I’ve seen ‘submit’ buttons routinely keep the user waiting as long as several minutes for the underlying transaction to complete.&lt;/p&gt; &lt;p&gt;Why has the bar fallen this low? And how can we fix it? In this post we’ll dive into exactly that.&lt;/p&gt; &lt;h3 id=&quot;history-doesnt-repeat-itself-but-it-often-rhymes&quot;&gt;History doesn’t repeat itself, but it often rhymes&lt;/h3&gt; &lt;p&gt;Most modern ways of doing things can be traced back to a historical problem that was solved using the path of least resistance. From the icons on your phone to the arrangement of the keys on your keyboard, these enduring vestiges of the past surround us.&lt;/p&gt; &lt;p&gt;Back when the world was run on paper, modifying a piece of information might have meant moving a giant, heavy filing cabinet. It made much more sense to grab a fresh form and fill it out, with the intent of replacing the original form later on. This is a very intuitive practice because the cabinet is heavy and slow, and there aren’t significant consequences for updating original form at your convenience. And if finance wants to do a quarter-end process that takes a few days and depends on these forms, you can let them do their thing unbothered by everyone wanting to update their information. Many processes like this exist because they feel natural.&lt;/p&gt; &lt;p&gt;Fast forward to the digital age, our first (and correct) assumption is that computers are fast. To assume otherwise would be a missed opportunity to design a simpler system that performs adequately. In the industry this dilemma is known as “premature optimization.” In the typical lifecycle of a software system, the first step is to build a rudimentary proof-of-concept that’s gradually modified to accommodate the increased demands placed upon it.&lt;/p&gt; &lt;p&gt;The simplest kind of computer information system is a CRUD-like design. CRUD stands for Create/Read/Update/Delete, which refers to the way data is stored on the system. In this kind of system, every entity in the system reflects its most recent write. If the need arises to keep multiple entities in a consistent state or capture the state of the system at a single point in time, a dark magic known as ACID transactions are employed. A CRUD design is effectively the same as the paper-driven process except your data entry person is The Flash and is capable of immediately updating information upon arrival and can also generate any kind of report you need, when you need it.&lt;/p&gt; &lt;p&gt;The unfortunate reality is that even The Flash has his limits. CPU speeds haven’t budged in decades and data requirements continue to grow exponentially. And as it turns out, &lt;a href=&quot;https://pdfs.semanticscholar.org/136c/3bb91cb7984fa7add24d197f1056465e0975.pdf&quot;&gt;ACID transactions aren’t magic&lt;/a&gt;. They typically rely on a mix of locks (which reduce system availability) or optimistic concurrency (which reduces reliability and consistency). These effects are ever more pronounced as the system scales. &lt;a href=&quot;https://en.wikipedia.org/wiki/CAP_theorem&quot;&gt;Mutable state, scalability and consistency are sworn enemies&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;Like a thin layer of filth (crud?) that slowly accumulates on your lenses, it can be difficult to notice that you’ve gradually moved the goalposts on the responsiveness of the system. It’s tempting to blame the CAP theorem for your woes and settle for investing weeks of developer time into regaining marginal amounts of lost ground. Any progress can seem impressive when you’re desperate enough. But fear not, there’s solution to all of this, you just haven’t seen it in a very, very long time.&lt;/p&gt; &lt;h3 id=&quot;meet-the-new-process-same-as-the-old-process&quot;&gt;Meet the new process, same as the old process&lt;/h3&gt; &lt;p&gt;It would be easy for me to throw software developers under the bus for not championing a solution to this problem. The reality is that business process owners are equally culpable. After all, early business process owners were able to conquer these issues using the slowest and most error-prone computers of all – human beings.&lt;/p&gt; &lt;p&gt;The major difference between the slow, paper-driven processes of yore and the “fast,” computer-driven processes of today is something called “temporality.” It’s this idea that you can have multiple versions of a piece of information, each valid as of their respective points in time. By incorporating the dimension of time, the system can recall what its exact state was at any given point in time.&lt;/p&gt; &lt;p&gt;Early information systems possessed elements of temporal systems by virtue of the fact that the easiest thing to do was to version the data stored within them. By intentionally utilizing this concept in digital information systems, we can achieve a whole new level of scalability, responsiveness, reliability, testability and simplicity. I can’t wait to show you how easy it can be to design this sort of system. Stay tuned.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;See the follow-up blog post:&lt;/strong&gt; &lt;a href=&quot;/2019/11/14/temporal-repository-implementation-using-mongodb-and-aspnet-core/&quot;&gt;Temporal repository implementation using MongoDB and ASP.NET&lt;/a&gt;&lt;/p&gt;</content><author><name>James Jones</name><email>james.russel.jones@gmail.com</email></author><summary type="html">“To err is human, but to really foul things up you need a computer.” -Paul R. Ehrlich</summary></entry><entry><title type="html">Put your ASP.NET Core controller on a diet with middleware</title><link href="https://jamejone.github.io/2019/03/02/put-your-aspnet-core-controller-on-a-diet-with-middleware/" rel="alternate" type="text/html" title="Put your ASP.NET Core controller on a diet with middleware"/><published>2019-03-02T00:00:00+00:00</published><updated>2021-06-13T18:07:01+00:00</updated><id>https://jamejone.github.io/2019/03/02/put-your-aspnet-core-controller-on-a-diet-with-middleware</id><content type="html" xml:base="https://jamejone.github.io/2019/03/02/put-your-aspnet-core-controller-on-a-diet-with-middleware/">&lt;p&gt;One of the most important responsibilities of RESTful web services is that they translate business logic outcomes (and unexpected errors) to the correct HTTP response code. When it comes to accomplishing this in ASP.NET Core, this logic usually ends up in the controller and looks something like this:&lt;/p&gt; &lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ActionResult&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Put&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FromForm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// don't do this!&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;responseObject&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;value is a required parameter&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;responseBody&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JsonConvert&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SerializeObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;responseObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BadRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;responseBody&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_repo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;Or even worse:&lt;/p&gt; &lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ActionResult&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Put&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FromForm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_repo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ArgumentNullException&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// ew&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;responseObject&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;responseBody&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JsonConvert&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SerializeObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;responseObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BadRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;responseBody&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// gross&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StatusCodeResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;The problem is that these controller methods are exceedingly thick. Every controller method requires input validation and emitting an HTTP response in the event of a validation failure. There are ways to push this validation to other places in the service layer, but the crux of the issue is that validation is in fact business logic which should really be in a domain layer.&lt;/p&gt; &lt;p&gt;Ideally, we could reduce the controller methods down to something much simpler, like this:&lt;/p&gt; &lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ActionResult&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Put&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FromForm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;BadRequestException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;value is required&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_repo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;Or even better:&lt;/p&gt; &lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ActionResult&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Put&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FromForm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_repo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//internally throw BadRequestException&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;Where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BadRequestException&lt;/code&gt; is an exception that lives in our domain layer:&lt;/p&gt; &lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;BadRequestException&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;Getting &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;throw BadRequestException(&quot;...&quot;)&lt;/code&gt; out of the controller and pushing it down into the domain layer is desirable, because as mentioned before, much of the business logic is captured in validation. And almost everyone can agree business logic shouldn’t be in the HTTP layer. The HTTP layer is complicated enough without having to worry about business logic!&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;In case you’re balking at the idea of using exceptions this way, allow me the opportunity to allay your concern:&lt;/p&gt; &lt;p&gt;You may want this exception to be called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ValidationException&lt;/code&gt; or something less HTTP-oriented, and by all means feel free to do that. I personally think it’s fine for the domain layer to have some notion of an HTTP response since requirements are so usually so coupled with HTTP responses. To each their own. However you name it, the key is that we’re using exceptions for the validation failure workflow.&lt;/p&gt; &lt;p&gt;You also might be wondering if it’s okay to use exceptions for business logic. I personally think this is fine for one big reason: Exceptions are an &lt;em&gt;immensely&lt;/em&gt; powerful construct for implementing validation logic because they let you write functions as if a validation failure will never happen. They enable you write clean, happy-path function signatures that are blissfully unaware of the possibility that a validation failure may occur. And this is infinitely better than passing validation lists via reference into every function or stuffing your controller with validation logic.&lt;/p&gt; &lt;/blockquote&gt; &lt;p&gt;Anyway, back to the point of this post. Since the controller and domain layer won’t catch exceptions, then who will? You guessed it – the answer is &lt;a href=&quot;https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/&quot;&gt;ASP.NET Core middleware&lt;/a&gt;. I’m telling you, it’s SO comforting knowing middleware will always be there to catch you. Seriously, once you start using this pattern you’ll never go back. So let’s get to it.&lt;/p&gt; &lt;p&gt;First, we need to create a marker-like interface which will let our middleware discern between exceptions thrown by our business logic and the other, more nefarious exceptions like NullReferenceException, etc:&lt;/p&gt; &lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;IHttpException&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HttpStatusCode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;And then we’ll fix up our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BadRequestException&lt;/code&gt; to use that interface:&lt;/p&gt; &lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;BadRequestException&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IHttpException&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HttpStatusCode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StatusCodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Status400BadRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;And, without further adieu, here’s the middleware that will handle all uncaught exceptions:&lt;/p&gt; &lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IApplicationBuilder&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;UseExceptionHandlingMiddleware&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IApplicationBuilder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseExceptionHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Features&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IExceptionHandlerFeature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ContentType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;application/json&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;responseMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Error&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IHttpException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StatusCode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Error&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IHttpException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpStatusCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;responseMessage&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StatusCode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StatusCodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Status500InternalServerError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;responseMessage&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;internal server error :(&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;responseObject&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;responseMessage&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;responseBody&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JsonConvert&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SerializeObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;responseObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;responseBody&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;blockquote&gt; &lt;p&gt;&lt;strong&gt;&lt;em&gt;Note&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;Notice how exceptions which don’t match the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IHttpException&lt;/code&gt; marker will emit an internal server error. Internal server errors are certainly inconvenient, but we owe it to our service consumers to be transparent when something very bad has happened! This way, we can quickly respond to critical errors in a consistent way (for example, by logging the stack trace, sending a notification to support, reaching out to the user, etc). And it’s pretty nice not having try/catches all over the place.&lt;/p&gt; &lt;/blockquote&gt; &lt;p&gt;If anything, I hope this post demonstrates the power of ASP.NET Core middleware to handle &lt;a href=&quot;https://en.wikipedia.org/wiki/Cross-cutting_concern&quot;&gt;cross-cutting concerns&lt;/a&gt; such as exception handling. As a rule of thumb, pretty much any time you have code repeating in many controller methods there’s usually a way to move that code into middleware.&lt;/p&gt; &lt;p&gt;Feel free to &lt;a href=&quot;https://github.com/jamejone/http-response-exception-middleware&quot;&gt;clone the source from GitHub&lt;/a&gt; and see the pattern in action. The unit tests use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Microsoft.AspNetCore.Mvc.Testing&lt;/code&gt; to test the entire HTTP pipeline, including the middleware. Enjoy.&lt;/p&gt;</content><author><name>James Jones</name><email>james.russel.jones@gmail.com</email></author><summary type="html">One of the most important responsibilities of RESTful web services is that they translate business logic outcomes (and unexpected errors) to the correct HTTP response code. When it comes to accomplishing this in ASP.NET Core, this logic usually ends up in the controller and looks something like this:</summary></entry><entry><title type="html">Bug fix - HTTP Error 502.5 - Process Failure</title><link href="https://jamejone.github.io/2019/02/22/process-failure-error-starting-application/" rel="alternate" type="text/html" title="Bug fix - HTTP Error 502.5 - Process Failure"/><published>2019-02-22T00:00:00+00:00</published><updated>2021-06-13T18:07:01+00:00</updated><id>https://jamejone.github.io/2019/02/22/process-failure-error-starting-application</id><content type="html" xml:base="https://jamejone.github.io/2019/02/22/process-failure-error-starting-application/">&lt;p&gt;I encountered a weird error today attempting to run a .NET Core application hosted in IIS. The error was:&lt;/p&gt; &lt;picture&gt;&lt;source srcset=&quot;/generated/public/2019-02-22-process-failure-error-starting-application/error-starting-application-400-b660ce5d8.webp 400w, /generated/public/2019-02-22-process-failure-error-starting-application/error-starting-application-600-b660ce5d8.webp 600w, /generated/public/2019-02-22-process-failure-error-starting-application/error-starting-application-763-b660ce5d8.webp 763w&quot; type=&quot;image/webp&quot; /&gt;&lt;source srcset=&quot;/generated/public/2019-02-22-process-failure-error-starting-application/error-starting-application-400-b660ce5d8.jpg 400w, /generated/public/2019-02-22-process-failure-error-starting-application/error-starting-application-600-b660ce5d8.jpg 600w, /generated/public/2019-02-22-process-failure-error-starting-application/error-starting-application-763-b660ce5d8.jpg 763w&quot; type=&quot;image/jpeg&quot; /&gt;&lt;img src=&quot;/generated/public/2019-02-22-process-failure-error-starting-application/error-starting-application-763-b660ce5d8.jpg&quot; alt=&quot;'An error occurred while starting the application.'&quot; /&gt;&lt;/picture&gt; &lt;blockquote&gt; &lt;p&gt;&lt;em&gt;An error occurred while starting the application.&lt;/em&gt;&lt;/p&gt; &lt;/blockquote&gt; &lt;p&gt;Well that’s a little vague. It worked fine in IIS Express. So I decided to restart my computer because that’s what I do when I need some time to think, and every once in a while it helps. In this case it actually did help by giving a different error message the first time I visit the page after a fresh restart:&lt;/p&gt; &lt;picture&gt;&lt;source srcset=&quot;/generated/public/2019-02-22-process-failure-error-starting-application/process-failure-400-e732e3556.webp 400w, /generated/public/2019-02-22-process-failure-error-starting-application/process-failure-600-e732e3556.webp 600w, /generated/public/2019-02-22-process-failure-error-starting-application/process-failure-763-e732e3556.webp 763w&quot; type=&quot;image/webp&quot; /&gt;&lt;source srcset=&quot;/generated/public/2019-02-22-process-failure-error-starting-application/process-failure-400-e732e3556.jpg 400w, /generated/public/2019-02-22-process-failure-error-starting-application/process-failure-600-e732e3556.jpg 600w, /generated/public/2019-02-22-process-failure-error-starting-application/process-failure-763-e732e3556.jpg 763w&quot; type=&quot;image/jpeg&quot; /&gt;&lt;img src=&quot;/generated/public/2019-02-22-process-failure-error-starting-application/process-failure-763-e732e3556.jpg&quot; alt=&quot;'HTTP Error 502.5 - Process Failure'&quot; /&gt;&lt;/picture&gt; &lt;blockquote&gt; &lt;p&gt;HTTP Error 502.5 - Process Failure&lt;/p&gt; &lt;p&gt;Common causes of this issue:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;The application process failed to start&lt;/li&gt; &lt;li&gt;The application process started but then stopped&lt;/li&gt; &lt;li&gt;The application process started but failed to listen on the configured port&lt;/li&gt; &lt;/ul&gt; &lt;/blockquote&gt; &lt;p&gt;It occurred to me that since this is an ASP.NET Core application that the ASP.NET Core module for IIS is attempting to launch an executable, which I can also execute at will. Running the executable directly gives me a clear indication of the problem on the console output – there’s an uncaught exception being thrown in Startup.cs:&lt;/p&gt; &lt;picture&gt;&lt;source srcset=&quot;/generated/public/2019-02-22-process-failure-error-starting-application/stack-trace-400-76dac7a6b.webp 400w, /generated/public/2019-02-22-process-failure-error-starting-application/stack-trace-600-76dac7a6b.webp 600w, /generated/public/2019-02-22-process-failure-error-starting-application/stack-trace-800-76dac7a6b.webp 800w, /generated/public/2019-02-22-process-failure-error-starting-application/stack-trace-977-76dac7a6b.webp 977w&quot; type=&quot;image/webp&quot; /&gt;&lt;source srcset=&quot;/generated/public/2019-02-22-process-failure-error-starting-application/stack-trace-400-76dac7a6b.jpg 400w, /generated/public/2019-02-22-process-failure-error-starting-application/stack-trace-600-76dac7a6b.jpg 600w, /generated/public/2019-02-22-process-failure-error-starting-application/stack-trace-800-76dac7a6b.jpg 800w, /generated/public/2019-02-22-process-failure-error-starting-application/stack-trace-977-76dac7a6b.jpg 977w&quot; type=&quot;image/jpeg&quot; /&gt;&lt;img src=&quot;/generated/public/2019-02-22-process-failure-error-starting-application/stack-trace-800-76dac7a6b.jpg&quot; alt=&quot;'Incriminating stack trace'&quot; /&gt;&lt;/picture&gt; &lt;p&gt;There’s the problem! A missing config variable. I love when I don’t need to scour the internet with cryptic error codes. ASP.NET Core told me exactly what the issue was.&lt;/p&gt;</content><author><name>James Jones</name><email>james.russel.jones@gmail.com</email></author><summary type="html">I encountered a weird error today attempting to run a .NET Core application hosted in IIS. The error was:</summary></entry><entry><title type="html">Real-time by accident</title><link href="https://jamejone.github.io/2019/01/22/real-time-by-accident/" rel="alternate" type="text/html" title="Real-time by accident"/><published>2019-01-22T00:00:00+00:00</published><updated>2021-06-13T18:07:01+00:00</updated><id>https://jamejone.github.io/2019/01/22/real-time-by-accident</id><content type="html" xml:base="https://jamejone.github.io/2019/01/22/real-time-by-accident/">&lt;p&gt;Wednesday, 8:55 AM.&lt;/p&gt; &lt;p&gt;You get settled into your chair and log in. Time to get some work done.&lt;/p&gt; &lt;p&gt;…&lt;/p&gt; &lt;p&gt;9:35 AM.&lt;/p&gt; &lt;p&gt;You hear someone tapping on your doorway.&lt;/p&gt; &lt;p&gt;“The 8:00 AM daily batch job failed run. Can you look into it?”&lt;/p&gt; &lt;p&gt;9:51 AM.&lt;/p&gt; &lt;p&gt;You discover what happened. The 4:00 AM nightly batch job took longer than usual to run. Your excellent logging indicates it was processing an usually large set of data. In fact, it’s still running right now and it’s almost complete. You fire off an email to let the concerned know what’s going on and that you’ll restart the 8:00 AM job once the 4:00 AM job is done.&lt;/p&gt; &lt;p&gt;Crap! You’re late for the 10:00 AM standup.&lt;/p&gt; &lt;p&gt;…&lt;/p&gt; &lt;p&gt;11:05 AM.&lt;/p&gt; &lt;p&gt;You finally arrive back at your desk, quickly verify the 4:00 AM job finally finished, manually kick off the job that was supposed to run at 8:00 AM, and send another email letting the user know when the job finishes. It usually only takes 30 minutes or so. Try to get some work done in the meantime.&lt;/p&gt; &lt;p&gt;11:35 AM.&lt;/p&gt; &lt;p&gt;Crap, the job’s still running.&lt;/p&gt; &lt;p&gt;11:40 AM.&lt;/p&gt; &lt;p&gt;Still running…&lt;/p&gt; &lt;p&gt;11:45 AM.&lt;/p&gt; &lt;p&gt;The job’s done! Fire off a third email letting them know the issue’s resolved.&lt;/p&gt; &lt;p&gt;What do you know, it’s lunch time! Where’d the morning go?&lt;/p&gt; &lt;h3 id=&quot;heres-where-it-went&quot;&gt;Here’s where it went&lt;/h3&gt; &lt;p&gt;You spent it babysitting a faux-real-time system.&lt;/p&gt; &lt;p&gt;It’s real-time in the sense that processing deadlines are imposed on the system. In our example, the 4:00 AM job has a &lt;a href=&quot;https://en.wikipedia.org/wiki/Worst-case_execution_time&quot;&gt;worst-case execution time&lt;/a&gt; of 4 hours. It’s a &lt;a href=&quot;https://en.wikipedia.org/wiki/Real-time_computing#Criteria_for_real-time_computing&quot;&gt;hard deadline&lt;/a&gt; in the sense that the system fails if the jobs don’t complete on-time. It’s ‘faux’ in the sense that these constraints are unnecessary and engender a lot of serious drawbacks.&lt;/p&gt; &lt;p&gt;Scheduling batch jobs this way lends itself to tedious management of issues arrising from the inherent race conditions involved. Additionally, it’s difficult to determine the dependencies between the jobs since they appear more coincidental than explicit. This sort of knowledge is usually known by everyone but never defined anywhere. “Everyone knows the 4:00 AM job needs to be done before the 8:00 AM job starts!” And worst of all, these patterns tend to fail you just when you need them to work the most.&lt;/p&gt; &lt;p&gt;This pattern is usually borne out of a few reasons:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;An attempt to sequence the jobs in a decoupled way,&lt;/li&gt; &lt;li&gt;The availability of ‘cron’-like job scheduling tools, and&lt;/li&gt; &lt;li&gt;The apparent lack of equivalently-available and simple alternatives.&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;So how can we get ourselves out of this mess? Are there any alternatives?&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/public/only-winning-move.gif&quot; alt=&quot;The only winning move is not to play.&quot; /&gt;&lt;/p&gt; &lt;h3 id=&quot;potential-solutions&quot;&gt;Potential solutions&lt;/h3&gt; &lt;h4 id=&quot;mutex-locks-dont-do-this&quot;&gt;Mutex locks (don’t do this)&lt;/h4&gt; &lt;p&gt;One solution I’ve seen at a major international company was to keep the existing scheduling system and augment it with mutex locks. Specifically:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Create a dependency graph for every job.&lt;/li&gt; &lt;li&gt;When each job starts, attempt to obtain a lock on all dependencies that job depends on (including the job itself). &lt;ul&gt; &lt;li&gt;In case of failure to obtain all the locks, release the locks and try again in several minutes.&lt;/li&gt; &lt;li&gt;If all locks were successfuly obtained, start the job.&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;li&gt;When the job finishes, release the locks that were obtained in order to start the job.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;Although this approach worked in the short-term, it was fraught with issues. If a job goes longer than usual and causes other jobs to enter a retry state, you never know which of the retrying jobs will go next. Oftentimes you want them to go in chronological order, but retry logic doesn’t naturally enable this sort of behavior. You’d need to tack on some additional logic which looks at the other jobs and this quickly gets very convoluted.&lt;/p&gt; &lt;p&gt;There are other issues such as deadlocks and lost locks. Just stay away from this pattern.&lt;/p&gt; &lt;h4 id=&quot;event-sourcingcqrsjob-queueing&quot;&gt;Event sourcing/CQRS/Job queueing&lt;/h4&gt; &lt;p&gt;Much smarter people than me have written about these patterns in vast detail, so I won’t waste time rehashing their work. In essence, we want to remove the timing-based scheduling and replace it with chaining or queueing. The end of one job should indirectly trigger the next job, ideally without the jobs knowing about each other.&lt;/p&gt; &lt;p&gt;This is roughly how it would look:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;At 4:00 AM, a command is submitted to the command queue to start Job 1.&lt;/li&gt; &lt;li&gt;An agent picks up Job 1 and starts working on it. Once it ends the agent creates a “Job 1 completed” event.&lt;/li&gt; &lt;li&gt;The “Job 1 completed” event is translated into a command to start Job 2 and the command is subsequently submitted to the command queue.&lt;/li&gt; &lt;li&gt;An agent picks up the Job 2 command and starts working on it.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;At its heart we’re literally linking the jobs together. In other words, when one job ends another quickly starts after it. The dependency is explicitly defined in code (a “Job 1 completed” event &lt;em&gt;causes&lt;/em&gt; a Job 2 command to be submitted).&lt;/p&gt; &lt;p&gt;Sure, it’s possible that one job severely delays the other jobs, but in enterprise system that’s generally okay.&lt;/p&gt; &lt;p&gt;As the adage goes, better late than never.&lt;/p&gt;</content><author><name>James Jones</name><email>james.russel.jones@gmail.com</email></author><summary type="html">Wednesday, 8:55 AM.</summary></entry><entry><title type="html">New blog, who dis?</title><link href="https://jamejone.github.io/2018/11/24/new-blog-who-dis/" rel="alternate" type="text/html" title="New blog, who dis?"/><published>2018-11-24T00:00:00+00:00</published><updated>2021-06-13T18:07:01+00:00</updated><id>https://jamejone.github.io/2018/11/24/new-blog-who-dis</id><content type="html" xml:base="https://jamejone.github.io/2018/11/24/new-blog-who-dis/">&lt;p&gt;So I got my new blog up and running in a matter of 10 minutes. Of course that doesn’t count all the hours spent deliberating between all the potential alternatives (Medium, WordPress, MkDocs, Blogger, etc). Nor does it count all the prerequisite knowledge it takes to understand how to do, well, anything. Nonetheless, I’m sticking with 10 minutes.&lt;/p&gt; &lt;p&gt;I ended up going with Jekyll on GitHub Pages for a few reasons:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;I can edit my posts directly on GitHub and GitHub automatically recompiles the whole Jekyll site for me. This is immensely convenient.&lt;/li&gt; &lt;li&gt;Creating it was simply a matter of &lt;a href=&quot;https://github.com/poole/poole#4-serving-it-up&quot;&gt;forking a repo and adjusting a couple variables&lt;/a&gt;.&lt;/li&gt; &lt;li&gt;The website is static once compiled, so I never need to worry about it going down.&lt;/li&gt; &lt;li&gt;My blog is portable, so if GitHub Pages goes away I can host it pretty much anywhere else.&lt;/li&gt; &lt;li&gt;I trust GitHub more than anyone to keep my code &lt;a href=&quot;https://www.wired.com/2016/04/github-now-three-places-keep-code-connected/&quot;&gt;replicated and safe&lt;/a&gt; in case of a disaster scenario. Plus, I’ll always have a copy on my local machine by virtue of Git.&lt;/li&gt; &lt;li&gt;It’s 100% free.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;OK sure that was more than just a few reasons. Gotta keep you on your toes. Hopefully the convenience factor will reduce the friction to a point that I’ll be willing to write more often. We shall see…&lt;/p&gt;</content><author><name>James Jones</name><email>james.russel.jones@gmail.com</email></author><summary type="html">So I got my new blog up and running in a matter of 10 minutes. Of course that doesn’t count all the hours spent deliberating between all the potential alternatives (Medium, WordPress, MkDocs, Blogger, etc). Nor does it count all the prerequisite knowledge it takes to understand how to do, well, anything. Nonetheless, I’m sticking with 10 minutes. I ended up going with Jekyll on GitHub Pages for a few reasons: I can edit my posts directly on GitHub and GitHub automatically recompiles the whole Jekyll site for me. This is immensely convenient. Creating it was simply a matter of forking a repo and adjusting a couple variables. The website is static once compiled, so I never need to worry about it going down. My blog is portable, so if GitHub Pages goes away I can host it pretty much anywhere else. I trust GitHub more than anyone to keep my code replicated and safe in case of a disaster scenario. Plus, I’ll always have a copy on my local machine by virtue of Git. It’s 100% free. OK sure that was more than just a few reasons. Gotta keep you on your toes. Hopefully the convenience factor will reduce the friction to a point that I’ll be willing to write more often. We shall see…</summary></entry></feed>