<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Joe Petrakovich</title>
    <description>The latest articles on Forem by Joe Petrakovich (@makingloops).</description>
    <link>https://forem.com/makingloops</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F96876%2F73acd16d-71f3-4950-b04a-474e7a61ab57.jpg</url>
      <title>Forem: Joe Petrakovich</title>
      <link>https://forem.com/makingloops</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/makingloops"/>
    <language>en</language>
    <item>
      <title>Adding a second line of business: Refactoring Strategies</title>
      <dc:creator>Joe Petrakovich</dc:creator>
      <pubDate>Tue, 05 Nov 2019 00:00:00 +0000</pubDate>
      <link>https://forem.com/makingloops/adding-a-second-line-of-business-refactoring-strategies-2io7</link>
      <guid>https://forem.com/makingloops/adding-a-second-line-of-business-refactoring-strategies-2io7</guid>
      <description>&lt;p&gt;In the &lt;a href="https://makingloops.com/adding-a-second-line-of-business/"&gt;Lessons Learned&lt;/a&gt; case study, we discussed the high level logistical issues driving my team’s decision to add a second line of business to an existing application.&lt;/p&gt;

&lt;p&gt;In this article, we’ll go over the code-level refactoring steps involved in this very large and high risk project. If you find yourself in a similar position to the one in the above article, you can use the techniques in this guide to make the process a lot easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Safely extracting subclasses from a 10,000 line God Object
&lt;/h2&gt;

&lt;p&gt;For consistency, we’ll continue to use the car insurance &amp;amp; home insurance examples from the case study.&lt;/p&gt;

&lt;p&gt;In our example, our application’s main line of business was car insurance, and although there were a lot of car insurance related business concerns scattered throughout, there were also a lot of generic entities that could be shared across the insurance landscape. These are things like insurance agents, insurance companies, insured businesses and people, notices, payments, and most other accounting-related entities.&lt;/p&gt;

&lt;p&gt;In this application, we found that there were two &lt;em&gt;root objects&lt;/em&gt; that tie everything else together. We found that every customer was managed first through a quoting process via a &lt;code&gt;Quote&lt;/code&gt; object, and if the quote was accepted, it became an &lt;code&gt;Account&lt;/code&gt;. All business was conducted through these objects. These &lt;code&gt;Quote&lt;/code&gt; and &lt;code&gt;Account&lt;/code&gt; objects also had database primary key IDs that spread throughout most other objects.&lt;/p&gt;

&lt;p&gt;In your case, you likely have at least one object like this.&lt;/p&gt;

&lt;p&gt;We found that these root objects were heavily laced with car insurance concerns, so to add a second line of business in a way that would allow us to continue using &lt;code&gt;Quote&lt;/code&gt; and &lt;code&gt;Account&lt;/code&gt; as the main business vehicle (no pun intended), we decided to convert both &lt;code&gt;Quote&lt;/code&gt; and &lt;code&gt;Account&lt;/code&gt; into abstract base classes, and have the application work with &lt;code&gt;CarInsuranceQuote&lt;/code&gt;, &lt;code&gt;CarInsuranceAccount&lt;/code&gt;, &lt;code&gt;HomeInsuranceQuote&lt;/code&gt;, and &lt;code&gt;HomeInsuranceAccount&lt;/code&gt; subclasses instead.&lt;/p&gt;

&lt;p&gt;By extracting subclasses and leaving any shared properties and methods in the base class, we were also priming the system to accept future lines of business as well. Adding boat insurance would simply involve implementing &lt;code&gt;BoatInsuranceQuote&lt;/code&gt; and &lt;code&gt;BoatInsuranceAccount&lt;/code&gt; subclasses.&lt;/p&gt;

&lt;p&gt;The remainder of this article will look at what it took to extract subclasses from just one of the above classes (the &lt;code&gt;Quote&lt;/code&gt; class), as you’ll only have to repeat the process for any others.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Creating The Line Of Business Distinction
&lt;/h2&gt;

&lt;p&gt;The very first step involved when adding a second line of business to an existing application is to &lt;em&gt;make your application aware of the concept of lines of business&lt;/em&gt;. Your app will need a way to distinguish between the two, and since your app has only ever been the original line (car insurance, in our case), it makes sense to initialize all existing &lt;code&gt;Quote&lt;/code&gt; objects as belonging to that line.&lt;/p&gt;

&lt;p&gt;The easiest way to do this is to add an &lt;code&gt;enum&lt;/code&gt; property that represents each line of business, and initialize all existing objects to be of the original line.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public enum LineOfBusiness { CarInsurance = 1, HomeInsurance = 2 }

public class Quote
{
    public LineOfBusiness LineOfBusiness { get; set; }

    //...
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And the supporting schema changes…&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ALTER TABLE Quotes
ADD LineOfBusiness int NOT NULL
DEFAULT (1) --init to existing line of business

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You’ll also likely need to modify your ORM or any persistence code for saving/loading this property.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Constructor Madness
&lt;/h2&gt;

&lt;p&gt;Now that your app is &lt;em&gt;LOB-aware&lt;/em&gt;, it’s time to convert your original class into an abstract base class and bring about the existence of the line of business child classes.&lt;/p&gt;

&lt;p&gt;First, make your original class &lt;code&gt;abstract&lt;/code&gt;, along with the LOB property we created in the last step.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public abstract class Quote
{
    public abstract LineOfBusiness LineOfBusiness { get; }
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then, create a child class for each of your planned lines of business. You can permanently set the line of business since it will never change when using this object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class CarInsuranceQuote : Quote
{
    public override LineOfBusiness LineOfBusiness =&amp;gt; LineOfBusiness.CarInsurance;
}

public class HomeInsuranceQuote : Quote
{
    public override LineOfBusiness LineOfBusiness =&amp;gt; LineOfBusiness.HomeInsurance;
}

//any others?

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dq8PXV8v--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://makingloops.com/public/mushroom.gif/" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dq8PXV8v--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://makingloops.com/public/mushroom.gif/" alt="Spawning the children"&gt;&lt;/a&gt;Spawn the children!&lt;/p&gt;

&lt;p&gt;This step is called &lt;em&gt;Constructor Madness&lt;/em&gt; because if your app has been using plain old constructors to create new and/or load existing objects for use, the abstract class conversion will instantly cause a &lt;em&gt;lot&lt;/em&gt; of compiler errors.&lt;/p&gt;

&lt;p&gt;Being 20-years old, our app was created using the &lt;em&gt;Active Record&lt;/em&gt; design pattern, which means that objects are responsible for handling their own persistence (they save and load themselves by interacting directly with the database).&lt;/p&gt;

&lt;p&gt;If your app is using an ORM, you may have it slightly easier.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Quote existingQuote = new Quote(existingQuoteID);

var q = new Quote(); //these no longer work...

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;There are a couple ways to handle this:&lt;/p&gt;

&lt;p&gt;1) Create a static factory method on the base class that works out which child class to instantiate based on the line of business. Then replace all usages of &lt;code&gt;new Quote()&lt;/code&gt; with the factory method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public static Quote GetExisting(int quoteId)
{
    var lineOfBusiness = GetLineOfBusiness(quoteId);

    switch (lineOfBusiness)
    {
        case LineOfBusiness.CarInsurance:
            return new CarInsuranceQuote(quoteId);

        case LineOfBusiness.HomeInsurance:
            return new HomeInsuranceQuote(quoteId);
    }
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Or…&lt;/p&gt;

&lt;p&gt;2) Assume most existing constructor usages in your code were designed for the original line of business, so just replace &lt;code&gt;new Quote()&lt;/code&gt; with &lt;code&gt;new CarInsuranceQuote()&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var existingQuote = new CarInsuranceQuote(existingQuoteID);

var q = new CarInsuranceQuote(); //ahh, all better...

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We ended up doing a bit of both.&lt;/p&gt;

&lt;p&gt;In our case, it turned out that most of the constructor usages were on user interface pages, and because we decided we would make brand new pages for the new line of business, the reality was that &lt;em&gt;in most cases&lt;/em&gt; we could simply replace the old &lt;code&gt;Quote&lt;/code&gt; constructor usages with &lt;code&gt;CarInsuranceQuote&lt;/code&gt; constructors.&lt;/p&gt;

&lt;p&gt;Since the system was built for car insurance for 20+ years, it is safe to assume that using the car insurance subclass throughout the existing code will be appropriate.&lt;/p&gt;

&lt;p&gt;If you’re not sure, just use the factory method and you can always change it later.&lt;/p&gt;

&lt;p&gt;Plan to have to work through quite a bit of compiler errors. Take your time and &lt;strong&gt;commit often&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This refactoring is mostly a hefty exercise in &lt;em&gt;leaning on the compiler&lt;/em&gt;. It’s in your best interest to make small changes and re-compile often in order to see what new errors turn up.&lt;/p&gt;

&lt;p&gt;If you make too many changes at once, you’ll easily get overwhelmed at the multi-headed hydra of errors that recursively spawn out of each change and subsequent fix.&lt;/p&gt;

&lt;p&gt;Change, recompile, fix, commit, repeat.&lt;/p&gt;

&lt;h3&gt;
  
  
  Active Record pattern woes
&lt;/h3&gt;

&lt;p&gt;In our case, with the Active Record pattern, we also needed to make sure our child classes still made use of the base class’s existing save and load methods.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public abstract class Quote
{
    public void Save()
    {
        SaveInner();
    }

    public virtual void SaveInner()
    {
        //save properties
    }
}

public class CarInsuranceQuote : Quote
{
    public override SaveInner()
    {
        base.SaveInner();

        //save car-insurance-specific properties
    }
}

public class HomeInsuranceQuote : Quote
{
    public override SaveInner()
    {
        base.SaveInner();

        //save home-insurance-specific properties
    }
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This meant that all our existing usages of &lt;code&gt;.Save()&lt;/code&gt; and &lt;code&gt;.Load()&lt;/code&gt; would continue to work and would handle all base class properties, as well as any properties specific to a child class.&lt;/p&gt;

&lt;p&gt;Again, if you are fortunate enough to not have to use the Active Record pattern, you may not need to go through this much trouble.&lt;/p&gt;

&lt;h3&gt;
  
  
  Single vs. Class Table Inheritance
&lt;/h3&gt;

&lt;p&gt;Another decision that will need to be made is how you will store the properties that are unique to your new second line of business.&lt;/p&gt;

&lt;p&gt;If you’ve only been using one table for &lt;code&gt;Quote&lt;/code&gt;, and you’re making use of a save and load strategy like in the last section, your persistence will need to work out which columns (or tables) to include or exclude depending on which object is being stored or retrieved.&lt;/p&gt;

&lt;p&gt;Our application was already using &lt;a href="https://www.martinfowler.com/eaaCatalog/singleTableInheritance.html"&gt;single table inheritance&lt;/a&gt; in other areas, so we went with that. We also did not have experience with &lt;a href="https://martinfowler.com/eaaCatalog/classTableInheritance.html"&gt;class table inheritance&lt;/a&gt; and felt that single table would be the easiest way to continue reusing existing relationships off the primary key.&lt;/p&gt;

&lt;p&gt;At this point you will have two or more very &lt;em&gt;thin&lt;/em&gt; child classes and one thick base class. It is now safe to begin using your child classes, but obviously they would not be of much use to you, as all the properties and methods are still in the base class and the distinction between the lines of business is no more than a name.&lt;/p&gt;

&lt;p&gt;The final step in this process involves three kinds of modifications to the base class. They are:&lt;/p&gt;

&lt;p&gt;1) Pushing down all of the properties and methods from the base class that belong exclusively in the original line of business subclass. 2) Converting some properties and methods to be abstract, with custom line-of-business specific implementations.3) Leaving any others behind that can be shared among all lines.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Push down methods, properties, and fields (in that order)
&lt;/h2&gt;

&lt;p&gt;After you’ve got your object constructors back in order, and your persistence set up to correctly handle the new hierarchy, the remaining work involves finding the proper home for the properties and methods that are all hanging out on the base class.&lt;/p&gt;

&lt;p&gt;In our example scenario, because the application was originally built specifically for car insurance, that meant that a &lt;em&gt;large&lt;/em&gt; majority of the properties and methods could be pushed down to live exclusively on the &lt;code&gt;CarInsuranceQuote&lt;/code&gt; subclass.&lt;/p&gt;

&lt;p&gt;After all is said and done, the base &lt;code&gt;Quote&lt;/code&gt; class would be left with the few properties and methods that are shared across all lines of business. For us, that meant metadata properties like when the quote was created (and by whom), when it was last modified, user notes, helper/utility methods and several abstract member definitions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Methods first!
&lt;/h3&gt;

&lt;p&gt;Depending on how you chose to do the constructor step, this step could be the most time-consuming, since all your existing code is expecting these class members to exist on the &lt;code&gt;Quote&lt;/code&gt; class.&lt;/p&gt;

&lt;p&gt;Pushing down a single method could result in &lt;em&gt;hundreds or more&lt;/em&gt; compiler errors!&lt;/p&gt;

&lt;p&gt;To do this step without wanting to pull your hair out, you’ll find it works best to &lt;strong&gt;push methods down first, before trying to push properties or fields down&lt;/strong&gt;. This is because a property or field likely has many more usages than a method, &lt;em&gt;and&lt;/em&gt; a method might not depend on &lt;em&gt;any&lt;/em&gt; fields, so moving it cause little to no errors. On the other hand, if you move a field or property, many things could depend on it and they will all error.&lt;/p&gt;

&lt;p&gt;So methods first, properties second, fields last.&lt;/p&gt;

&lt;p&gt;I believe I learned that tip (and the next one) either from Martin Fowler’s Refactoring book or Joshua Kierevsky’s Refactoring To Patterns book. Both are worth a space on your shelf.&lt;/p&gt;

&lt;h3&gt;
  
  
  Access modifier flipping for error reduction
&lt;/h3&gt;

&lt;p&gt;When you’re pushing things down, you may see many errors related to usages of &lt;code&gt;private&lt;/code&gt; members on the base class. An easy, short term fix is to simply make them &lt;code&gt;protected&lt;/code&gt;. They will most likely be pushed down eventually, but you can use this &lt;em&gt;access modifier&lt;/em&gt; flipping technique to quickly clear up compiler errors so you can keep moving forward with the refactor.&lt;/p&gt;

&lt;p&gt;Depending on how large your base class is, this task will surely test your patience. You’ll be pushing down one field, generating a ton of errors, fixing them (which cause several more, and fixing those (which cause several more…(ad-infinitum)).&lt;/p&gt;

&lt;p&gt;I recommend you go very slow, do one thing at a time, recompile often, and fix &lt;em&gt;only&lt;/em&gt; those errors that come up from your single change. Once it compiles without error, consider doing a commit to lock in that change and begin another. If you get over-eager and try to do more than one at once, you’ll be looking at potentially hundreds or thousands of errors and you’ll get so overwhelmed trying to fix them that you could make a grave mistake.&lt;/p&gt;

&lt;p&gt;I had to start over &lt;em&gt;completely&lt;/em&gt; so many times because of this.&lt;/p&gt;

&lt;p&gt;I’d get ambitious and try to do too many refactors at once and I’d get lost in a whack-a-mole recursive error hell where one fix would generate 50 more errors. Then I’d feel so uneasy about all the changes that I’d just &lt;code&gt;git reset HEAD --hard&lt;/code&gt; and start over.&lt;/p&gt;

&lt;p&gt;Small changes and commits!&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Whew! We made it.&lt;/p&gt;

&lt;p&gt;Let’s recap over some of the highlights of this refactor.&lt;/p&gt;

&lt;p&gt;Check out the &lt;a href="https://makingloops.com/adding-a-second-line-of-business/"&gt;case study&lt;/a&gt; article to help you decide if doing this kind of refactor is the best decision for your team.&lt;/p&gt;

&lt;p&gt;Your application will likely have one or more &lt;em&gt;root&lt;/em&gt; objects that orchestrate the majority of the business processes and logic. These will be your splitting points. The goal is to extract a hierarchy of two or more subclasses that will each hold a separate line of business and that can make use of most of the accessory entities in the application (we benefited most from the reuse of the accounting and payment submodules).&lt;/p&gt;

&lt;p&gt;Start by creating the knowledge of the line of business via a type enum. In our example, we went with a single-table inheritance model in our database using an &lt;code&gt;LineOfBusiness&lt;/code&gt; &lt;em&gt;descriminator&lt;/em&gt; column.&lt;/p&gt;

&lt;p&gt;Next handle all the constructor issues that turn up once your existing class becomes an abstract base class.&lt;/p&gt;

&lt;p&gt;Finally, use the methods-properties-fields order when pushing down members from the base class into the appropriate child class. I found that order to result in the least amount of headaches.&lt;/p&gt;

&lt;p&gt;Keep the class refactoring books close by to reference the &lt;em&gt;Extract Hierarchy&lt;/em&gt;, &lt;em&gt;Extract Subclass&lt;/em&gt;, and &lt;em&gt;Extract Superclass&lt;/em&gt; refactorings.&lt;/p&gt;

&lt;p&gt;Once you’ve finished, it may be wise to only make use of your new line-of-business subclass within brand new UI pages so that you don’t break existing code.&lt;/p&gt;

&lt;p&gt;Good luck!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>dotnet</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Lessons learned adding a second line of business to a 20-year-old application</title>
      <dc:creator>Joe Petrakovich</dc:creator>
      <pubDate>Mon, 30 Sep 2019 00:00:00 +0000</pubDate>
      <link>https://forem.com/makingloops/lessons-learned-adding-a-second-line-of-business-to-a-20-year-old-application-k98</link>
      <guid>https://forem.com/makingloops/lessons-learned-adding-a-second-line-of-business-to-a-20-year-old-application-k98</guid>
      <description>&lt;p&gt;At some point in your career you’ll be tasked with a project that will strike fear into the very core of your soul.&lt;/p&gt;

&lt;p&gt;It will require you to do things you’ve never done before, to make choices for which you have no former wisdom to rely on.&lt;/p&gt;

&lt;p&gt;You’ll pour endlessly over ancient programming lore, online tutorials, articles, and blog posts, hoping you’ll find &lt;em&gt;someone&lt;/em&gt;, &lt;em&gt;anyone&lt;/em&gt; who has gone through the same scenario and can tell you &lt;em&gt;exactly&lt;/em&gt; what to do.&lt;/p&gt;

&lt;p&gt;And you &lt;em&gt;won’t&lt;/em&gt; find it.&lt;/p&gt;

&lt;p&gt;You’ll be &lt;strong&gt;all alone&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And if you don’t get it right, if you fuck it up – you’re toast.&lt;/p&gt;

&lt;p&gt;I just came out the other end of this very scenario, alive and unscathed, and I’d like to shine some light on the topic in which it was born.&lt;/p&gt;

&lt;p&gt;With the proper strategies and mindsets, you can be confident that you can handle this particular kind of problem. We’ll take a look at the problem itself, some of the strategies available to you, the path my team chose (and it’s present day result), and finally review the main lessons learned.&lt;/p&gt;

&lt;p&gt;This article won’t cover the technical, code-level details of &lt;em&gt;how&lt;/em&gt; it was done. If you’re interested in that part, it’ll be in an upcoming article. You can &lt;a href="https://makingloops.com/adding-a-second-line-of-business/#mc_embed_signup"&gt;jump to the bottom and add your email&lt;/a&gt; and I’ll send you it when it’s done.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: Adding a second line of business to a 20-year old codebase
&lt;/h2&gt;

&lt;p&gt;If you work for a company that is doing well, it’s common that it will expand into new markets.&lt;/p&gt;

&lt;p&gt;For example, if you work for an insurance company that sells car insurance, it is not inconceivable that your company could eventually sell RV, boat, life, rental, or home insurance.&lt;/p&gt;

&lt;p&gt;The timeline of when this &lt;em&gt;branching out&lt;/em&gt; might occur could be within the first year of business, all the way until 100+ years down the line.&lt;/p&gt;

&lt;p&gt;To understand the problem my team encountered, you can imagine that the software we work on has served only one &lt;em&gt;line of business&lt;/em&gt; for 20 years. For the sake of example, let’s continue with the insurance example and say that this is strictly a car insurance business.&lt;/p&gt;

&lt;p&gt;Architecturally-speaking, you have 20-years worth of components, classes, methods, and variables, all modeled around car insurance.&lt;/p&gt;

&lt;p&gt;This sytem models things like automotive insurance policies covering car accidents, based on things like vehicle age, make, and model, insured driver history, where the car is stored, and how it’s used. Things like a policy’s status and lifetime. Recurring or one-time payments. All the accounting data for these transactions. This is your typical line-of-business application that has grown to support all the different departments in the company.&lt;/p&gt;

&lt;p&gt;And since this software has been supported for 20 years, the business language and concepts can get &lt;em&gt;heavily&lt;/em&gt; embedded, and on top of that it’s usually got a lot of legacy baggage being dragged along with each new release.&lt;/p&gt;

&lt;p&gt;Now, after 20 years time, your car insurance company decides it’s going to start selling home insurance.&lt;/p&gt;

&lt;p&gt;A new line of business.&lt;/p&gt;

&lt;p&gt;Home insurance is of course still within the overarching business of &lt;em&gt;insurance&lt;/em&gt;, but it is also quite different. Despite both businesses dealing with &lt;em&gt;the act of of insuring&lt;/em&gt;, they have grown and developed separately, so the terminology is not always the same, the properties, statuses, and aging of policies is vastly different, and the way the insurance can be paid for follows substantially different rules.&lt;/p&gt;

&lt;p&gt;About the only thing that is similar is the fact that they involve something called an insurance &lt;em&gt;policy&lt;/em&gt;, and that there are entities like the people being insured (insureds), insurance companies, and agents.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BD0oW52o--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://makingloops.com/public/shared-concepts.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BD0oW52o--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://makingloops.com/public/shared-concepts.png" alt="Shared business concepts"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For now, the company can get by doing things the old fashioned way with spreadsheets and file shares, but they’re looking to you to come up with something better in a relatively short amount of time.&lt;/p&gt;

&lt;p&gt;How you accomplish this is entirely up to you, but there are a few requirements, and some obvious conditions that you’ll need to weigh carefully.&lt;/p&gt;

&lt;h3&gt;
  
  
  Constraints and other mentionables
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The company needs something better than spreadsheets, relatively quickly (less than a year, preferably sooner).&lt;/li&gt;
&lt;li&gt;This system will most likely need many of the same features as the existing system, when it comes to things like reports, mailing notices, and accounting data. It won’t need these things on day 1, but eventually.&lt;/li&gt;
&lt;li&gt;The company has found a way to &lt;em&gt;partially&lt;/em&gt; perform some of the home insurance business work using the existing system by &lt;em&gt;pretending&lt;/em&gt; car insurance policies are home insurance policies, and various other mental hacks.&lt;/li&gt;
&lt;li&gt;Your team is small, 1-2 developers, no QA, and you need to keep supporting the existing line of business.&lt;/li&gt;
&lt;li&gt;Obviously don’t break the existing system. It runs the entire business.
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Weighing Our Options
&lt;/h2&gt;

&lt;p&gt;After thinking about the problem and it’s constraints for awhile, we determined that we had three practical options.&lt;/p&gt;

&lt;p&gt;Option 1 (a.k.a New Toys) - Create a new application from scratch.&lt;/p&gt;

&lt;p&gt;Option 2 (a.k.a Safe Duplication) - Branch the existing app source code into a new app and heavily modify it to work with the new line of business.&lt;/p&gt;

&lt;p&gt;Option 3 (a.k.a Obvious But Hard) - Modify the existing app to support the new line of business side-by-side.&lt;/p&gt;

&lt;p&gt;Each option had a plethora of positives and negatives, and even to this day I wonder if one of these options would have been better than the one we chose.&lt;/p&gt;

&lt;p&gt;Next we’ll take a look at the pros and cons of each option and play plinko with the constraints until we end up with the decision.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 1 (New Toys) - Create a new application from scratch.
&lt;/h3&gt;

&lt;p&gt;After years of working on ASP.NET Web Forms applications, and only getting to play with ASP.NET Core on the side, this option was obviously very appealing.&lt;/p&gt;

&lt;p&gt;A new app would not disturb the existing app, it would allow us to take advantage of faster development (and deployment) practices, and it could become the training ground for all future development at the company, freeing us from the dying branch that is .NET Framework.&lt;/p&gt;

&lt;p&gt;I even recall taking a few nights of my own time to whip together a starter app to work from, transferring many of the existing tables and POCOs into equivalent Entity Framework code-first versions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BaA5DPsh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://makingloops.com/public/old-vs-new.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BaA5DPsh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://makingloops.com/public/old-vs-new.png" alt="A new app from scratch"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It wasn’t all puppies and candy though. Building this as a new app would be a lot like &lt;em&gt;rewriting&lt;/em&gt; an app from scratch (something that is rarely a good idea.)&lt;/p&gt;

&lt;p&gt;This new system was going to share many of the existing features from the other system, so we’d need to duplicate a &lt;em&gt;ton&lt;/em&gt; of it or find some way to share it via APIs. With that comes lots of testing. Testing that in theory was already done for 20 years to battle-harden the existing system. All those iterations of testing and bug fixing have to be redone for any features you duplicate into the new app.&lt;/p&gt;

&lt;p&gt;Then comes maintaining this app with separate releases, and a host of other problems that come with having a small team working on multiple systems.&lt;/p&gt;

&lt;p&gt;I think to do this option we would have either needed a dedicated team, or much more time. I would have liked this route, but in the end, there was way too much meat and potatoes in the existing app that we would have missed out on, and so we passed on it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 2 (Safe Duplication) - Branch the existing app source code into a new app and heavily modify it to work with the new line of business.
&lt;/h3&gt;

&lt;p&gt;This option was more like converting a school bus into an apartment rather than a refactor.&lt;/p&gt;

&lt;p&gt;We would branch the code into a new repository, and then play hack ‘n slash to convert all the car insurance behavior and appearance into it’s home insurance counterpart.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NgnVuTT4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://makingloops.com/public/branched.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NgnVuTT4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://makingloops.com/public/branched.png" alt="Branching the existing app to create a new one"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There were many benefits to this strategy. It would be safer because we’d leave the proverbial nest of our original application. Any bugs would be isolated from the main application, and that was quite enticing. All throughout my research I clung to this idea as a safety net because I was afraid my lack of experience with large refactorings would inevitably lead to a ball-of-mud situation.&lt;/p&gt;

&lt;p&gt;Further, this option may have been the fastest to reach a version 1, as we’d only need to modify the quirks that the business already knew about by trying to force home insurance into the existing application’s domain. A lot of the code changes would be &lt;em&gt;adaptations&lt;/em&gt; of existing behaviour and UI, rather than entirely new code.&lt;/p&gt;

&lt;p&gt;The downside with a second branch is that it would heavily complicate deployment and the sharing of features and bug fixes.&lt;/p&gt;

&lt;p&gt;Any bug you fix must be fixed and tested in both branches. If there are large differences between two areas where the bug exists, it may not be as easy as a git merge. It could require some &lt;em&gt;finesse&lt;/em&gt;. And often you simply might not have enough time or resources (see above: small team).&lt;/p&gt;

&lt;p&gt;The same is true for new features. Both systems may not be able to get them at the same time (or ever), so you end up playing a game of cat and mouse to keep them in sync.&lt;/p&gt;

&lt;p&gt;Every fix and feature has to have the added conversation “Should we do put it on the other system too?”.&lt;/p&gt;

&lt;p&gt;If possible, you could pull certain features out into separate shared libraries, but again, you need time and resources for that, and the &lt;em&gt;stickiness&lt;/em&gt; of a 20-year old system isn’t as welcoming to that path.&lt;/p&gt;

&lt;p&gt;We did not choose to go this route, although to this day I wonder if we would have had a version 1.0 out the door months earlier. And as we implemented the option we &lt;em&gt;did&lt;/em&gt; choose, this option was still appealing as a way to &lt;em&gt;jump ship&lt;/em&gt; in case things started to get bad.&lt;/p&gt;

&lt;p&gt;There were times where I felt very unconfident that it would turn out well, and the fear of creating a ball-of-mud kept this option in my periphery.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 3 (Obvious But Hard) - Modifying the existing app to support the new line of business side-by-side.
&lt;/h3&gt;

&lt;p&gt;This option was the first to arise because the business folks had already explained that they were able to &lt;em&gt;sort of&lt;/em&gt; use the existing application on top of a new database by playing pretend with the existing features. For example, because the system allows for custom car insurance policy coverages, they could simulate home insurance coverages and &lt;em&gt;make due&lt;/em&gt; with the random vehicle and driver references in the user interface here and there.&lt;/p&gt;

&lt;p&gt;In the end though, it was merely a familiar place to store some data, arguably better than an Excel spreadsheet, and it didn’t help automate any of the real home insurance business processes.&lt;/p&gt;

&lt;p&gt;But because we could see it in action, it was clear that the existing application had a lot of similarities and usable &lt;em&gt;meat and potatoes&lt;/em&gt;, so figuring out how to refactor it to support two lines of business seemed plausible, although daunting.&lt;/p&gt;

&lt;p&gt;There was &lt;em&gt;sooooo much&lt;/em&gt; car insurance jargon and concerns laced throughout this 20 year old juggernaut.&lt;/p&gt;

&lt;p&gt;Using some DDD terminology, from a higher level we could see that the existing line of business and the new line of business shared the same primary &lt;em&gt;aggregate roots&lt;/em&gt; in concept. I.E. both lines of business revolve around the lifetime of an insurance policy.&lt;/p&gt;

&lt;p&gt;The fundamental policy quote and policy account aggregate root classes on the existing application acted as the glue, tying together all other entities and transactions, they simply differed in &lt;em&gt;properties&lt;/em&gt; and &lt;em&gt;behavior&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;It turned out that there were a few different ways we could do this modification.&lt;/p&gt;

&lt;p&gt;We could &lt;strong&gt;A)&lt;/strong&gt; simply add all new code and create new interfaces for things we wanted to reuse from the existing system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;B)&lt;/strong&gt; Refactor it so that the differences between car insurance and home insurance were abstracted away via techniques like the strategy pattern or plain old inheritance.&lt;/p&gt;

&lt;p&gt;Or &lt;strong&gt;C)&lt;/strong&gt; The rapid ball-of-mud-inducing option of modifying existing code via basic if-else statements scattered everywhere to accommodate differences in behavior and appearance.&lt;/p&gt;

&lt;p&gt;Because I was absolutely terrified of turning this 20-year-old beast into a ball-of-mud, I was staunchly against option C, and rightfully so. But it is unfortunately an option many will take. In my desperate attempt to find guidance with this project, several developers even recommended it via something like “Hey man, the code is probably not that great anyway, just add a bunch of if-elses. NBD.”&lt;/p&gt;

&lt;p&gt;Option A sounded nice in theory. Adding new code is always safer than modifying existing code. All living in one app would allow us to have simple deployments, and we could still share code and features, albeit needing to write new interfaces for them.&lt;/p&gt;

&lt;p&gt;The downside of option A had to do with the fact that the fundamental car insurance policy quote and account classes were deeply engrained throughout the app via their primary keys.&lt;/p&gt;

&lt;p&gt;QuoteID and AccountID lived in almost every table, every class, as a reference back to the primary policy it originated from.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cikgr_CQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://makingloops.com/public/quote-id.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cikgr_CQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://makingloops.com/public/quote-id.png" alt="Primary key problem"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we were to create brand new classes for home insurance in order to keep isolated from car insurance, we would need new interfaces for almost every existing feature, meaning we wouldn’t really save the time we were intending by sharing.&lt;/p&gt;

&lt;p&gt;We’d also have to sprinkle new IDs all throughout the existing tables and classes.&lt;/p&gt;

&lt;p&gt;Yuck.&lt;/p&gt;

&lt;p&gt;And what happens if our business expands again to a third line of business?&lt;/p&gt;

&lt;p&gt;Option A was not scalable with the business in that sense.&lt;/p&gt;

&lt;p&gt;Of the three options for modifying the existing application, option B had the most chance for success. Abstracting away the differences between car and home insurance, whilst maintaining the base concepts of policy quotes, accounts, payments, insurance companies, agent, and insureds, simply felt &lt;em&gt;right&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;If done correctly, it would make sharing code and features almost automatic, and would also support an infinite number of additional lines of insurance business. Those precious QuoteID and AccountID primary keys could be maintained and shared between any new line of business.&lt;/p&gt;

&lt;p&gt;It seemed like a no brainer. It even fit all the classic examples of inheritance structures we learn about in computer science classes or software design books. Base class of insurance policy, child classes for specializations (car insurance, home insurance.)&lt;/p&gt;

&lt;p&gt;Duh, right?&lt;/p&gt;

&lt;p&gt;Although this option seemed fitting, I had zero experience taking on a refactor of this size. And when I encounter the unknown, it puts me into a kind of robotic dissociative state where my body becomes an afterthought to my mind’s rabid information thirst.&lt;/p&gt;

&lt;p&gt;I consume everything I can on the topic to ease my discomfort of not knowing, hoping I’ll find some wisdom that points the “one true way”.&lt;/p&gt;

&lt;p&gt;Massive fear and lack of experience was a solid barrier to this option.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Chosen One
&lt;/h2&gt;

&lt;p&gt;After matrix brain downloading endless books and blogs to overcome &lt;em&gt;the dread of unknowning&lt;/em&gt;, it was time to set forth on a plan.&lt;/p&gt;

&lt;p&gt;Ultimately, based on our small team and the 20-years of features and bug fixes we wanted to make use of, we decided to try option 3. We would set forth refactoring the existing application so that it could handle multiple lines of business, share code between those lines, and keep deployment simple.&lt;/p&gt;

&lt;p&gt;We also laid down some guidelines for this project to ensure we wouldn’t break the existing system in the process.&lt;/p&gt;

&lt;p&gt;These were things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Other than splitting the fundamental aggregate root classes into two (to share QuoteID and AccountID, remember?), resist modifying existing code and pages. Just about all business logic is specific to a line of business, so trying to reuse it would be high risk and an if-else nightmare. If it has the smell of car insurance, avoid it if at all possible.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Similarly, always lean towards creating brand new code, pages, stored procedures, dependencies, etc. New code is low risk because it doesn’t interact with existing code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;There will be times where existing code can be shared and may need an adjustment, if this is the case, do so with great caution, write tests beforehand, and be confident it is truly a shared business concept. Never try to shoehorn an existing feature to work in the other line.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Further, while I tried my darnedest to use delegation over inheritance, I ultimately couldn’t make it work the way I wanted to, so I went for a more traditional use of inheritance to break our aggregate roots apart.&lt;/p&gt;

&lt;p&gt;This meant converting two massive 10,000+ line god objects into abstract base classes, extracting two line of business subclasses, and &lt;em&gt;very&lt;/em&gt; slowly pushing down all variables, properties, and methods that were exclusive to each subclass, while leaving any metadata-like shared property and behavior in the base class.&lt;br&gt;&lt;br&gt;
This was daunting and resulting in a lot of git resets where I’d start over from scratch to get a cleaner run. Keep an eye of for an upcoming article that details the strategy for this large and risky undertaking.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yj8RFLDx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://makingloops.com/public/intermingle.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yj8RFLDx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://makingloops.com/public/intermingle.png" alt="Adding a second line"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At first we did this refactoring gradually, meaning that we made use of the new line of business subclass even before all the existing line’s concerns were pushed down from the base class. This was an unsettling experience because it meant any developer could make use of (or be confused by) the large assortment of inappropriate members being offered by our code editor’s autocomplete.&lt;/p&gt;

&lt;p&gt;Fortunately though, with a small team, we could communicate this risk and tip-toe around it while we iterated quickly on the new system.&lt;/p&gt;

&lt;p&gt;After enough bumps and bruises, we finished up the last of the refactoring, resulting in two lines of business sharing the same codebase for things like payments, accounting, email notices, and entities common to both worlds of insurance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;p&gt;You may find yourself in a situation like mine, but fear not, it is surmountable! Below are some high level key takeaways from this project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1) Get your whole team on board so everyone knows what’s up and can share the decision.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Don’t put the burden entirely on your shoulders, even if you are most qualified to handle it. Keep everyone on the same page and unified by analyzing and sharing the various options. This project seemed a whole lot more intimidating when I was trying to tackle it all on my own.&lt;/p&gt;

&lt;p&gt;When I brought my other team members into the picture, they were able to see things more realistically, rather than exclusively through my constricting developer perfectionism.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2) Benefit from the collective wisdom (books, articles, people).&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In times like these, when you simply don’t know what to do and don’t feel confident, you can assume someone has encountered a similar problem and created a book or article about it.&lt;/p&gt;

&lt;p&gt;In my case, I couldn’t find &lt;em&gt;anything&lt;/em&gt; regarding whether or not it’s even a good idea to split up a large system in this way, so I had to go about asking real people their opinions. Combining that with the excellent refactoring books from folks like Fowler and Kierevsky, I was able to make gradual progress forward.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3) Understand and accept that you can’t always know if it will be the best decision until much later.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With large systems, it’s very hard to know if the path forward will end up fruitful without actually using it for some length of time.&lt;/p&gt;

&lt;p&gt;Do your research, come up with a plan B and C in case it goes to hell quickly, and then march forward. Software is malleable and even with large decisions like these, you can work your way out if you have to.&lt;/p&gt;

&lt;p&gt;Put your perfectionism aside and accept some amount of messy, unfinished business.&lt;/p&gt;

&lt;p&gt;Is software ever really &lt;em&gt;done&lt;/em&gt; anyway?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4) Remember that software is, by it’s nature, malleable. You always have the option of taking things in another direction.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes, it may be more difficult after some changes, but it’s not impossible. I found it was good to remind myself of this to help quell my fears and to keep moving forward. At any time if things started to look fishy, I could jump ship and branch the code into a separate app (I still could!).&lt;/p&gt;

&lt;p&gt;Take your time and ideally do &lt;strong&gt;small releases&lt;/strong&gt; so issues are caught fast.&lt;/p&gt;

&lt;p&gt;Break the work up into small tasks and don’t worry about following patterns to the T, software can be a bit messy, especially very old systems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5) Jump in and begin despite your fear.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I know it sounds a bit cheesy in the context of writing code, but you will ultimately have to &lt;em&gt;just start&lt;/em&gt;. I had to accept that I wouldn’t be able to find a resource that laid out exactly what to do or told me if I was making the right move. I simply surrounded myself with the collective wisdom and then made tiny adjustments forward in a direction while communicating heavily with my team.&lt;/p&gt;

&lt;p&gt;Refactoring can be done in small bits, it doesn’t have to be all at once. With our project, we went several months with half-divided quote classes that interfered with each other but were able to handle it because our team is small.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6) If you have a small team and a short_er_ deadline, consider sharing the existing system’s foundation.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Without a lot of team bandwidth or time on your side, you’ll want to lean on all your strengths and eliminate excess. By adding a line of business to your existing app, you’ll benefit from years of foundational code, bug fixes, and testing, and you’ll only have one application to deploy and manage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7) If you have a large team and/or plenty of time, consider a new application or branched application.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With plenty of hands to spare or no impending deadline, you can benefit from the conveniences of modern technology. New platforms are great for shrinking dev and deployment time by reducing boilerplate code and being designed for easy cloud integration. If the new business line is unique enough, it may be worth building on a new foundation.&lt;/p&gt;

&lt;p&gt;If you truly want to share existing features, you have the option of making them available via APIs or shared libraries. Just remember that the network is still the slowest part of a system.&lt;/p&gt;

&lt;p&gt;God speed and good luck!&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://makingloops.com/adding-a-second-line-of-business/#mc_embed_signup"&gt;part two&lt;/a&gt; I’ll share the tactical, code-level steps and order of execution that I used to make this line of business &lt;em&gt;halving&lt;/em&gt; a manageable task.&lt;/p&gt;

&lt;p&gt;My hope is that if you ever encounter this kind of problem you’ll be able to reference it to make it much easier rather than binging on architecture books like I did.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>dotnet</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Escaping abstraction hell</title>
      <dc:creator>Joe Petrakovich</dc:creator>
      <pubDate>Fri, 05 Jul 2019 00:00:00 +0000</pubDate>
      <link>https://forem.com/makingloops/escaping-abstraction-hell-56f2</link>
      <guid>https://forem.com/makingloops/escaping-abstraction-hell-56f2</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WKYnVNKr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://makingloops.com/public/abstraction-hell.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WKYnVNKr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://makingloops.com/public/abstraction-hell.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You’re probably a lot like me when it comes to design decisions around code.&lt;/p&gt;

&lt;p&gt;You &lt;em&gt;like&lt;/em&gt; clean code.&lt;/p&gt;

&lt;p&gt;It is satisfying to both read and write, your teammates appreciate it, and it makes your application easier to work with down line, so &lt;strong&gt;you put a lot of effort into it&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But upon the this purity of intent, the psychological gremlin of &lt;a href="https://makingloops.com/perfectionism/"&gt;perfectionism&lt;/a&gt; often rears its head.&lt;/p&gt;

&lt;p&gt;Driven by a subconscious shame and fear of failure, it’s becomes the obsession to find &lt;em&gt;the best way&lt;/em&gt; to go about your task at hand.&lt;/p&gt;

&lt;p&gt;It’s tough &lt;em&gt;not knowing what you don’t know&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Of course, a quick web search to find the best way of doing something is a great thing, and it’s how our software improves, but what has happened to me, and likely you too, is that it becomes a &lt;em&gt;worm hole&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Scanning one browser tab on how to use Mediatr then spawns three more on CQRS, &lt;a href="https://makingloops.com/why-should-controllers-be-thin/"&gt;keeping controllers thin&lt;/a&gt;, and &lt;a href="https://makingloops.com/why-should-you-use-the-repository-pattern/"&gt;the repository pattern&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You realize you weren’t following the single responsibility principle and that you have a &lt;em&gt;command&lt;/em&gt; inside your &lt;em&gt;query&lt;/em&gt;, so you’ll first need to refactor that.&lt;/p&gt;

&lt;p&gt;As you refactor and are at the same time drinking beyond your fill from Google’s tit, you come across domain driven design and decide it would be better to host an event storming session with your team before you can &lt;em&gt;feel comfortable&lt;/em&gt; implementing the original small task you were assigned.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is a hell spiral.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It doesn’t end, and rather than finishing your task, you instead just add a bundle of abstractions that &lt;em&gt;in theory&lt;/em&gt; reduce coupling, but are likely untimely and low in value to your team and your company.&lt;/p&gt;

&lt;p&gt;This is usually because advice articles are written with a very specific problem context in mind which is often not &lt;em&gt;your&lt;/em&gt; problem context, so you’re building your application &lt;em&gt;based on the needs of some other application&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;It’s also a lot like waterfall vs. agile software development.&lt;/p&gt;

&lt;p&gt;The perfectionistic obsession has you putting the cart before the horse trying to &lt;em&gt;predict eternity&lt;/em&gt; rather than &lt;em&gt;just finishing your task&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The best architecture promotes agility (ease of change), but if you haven’t had a need to change anything about it, &lt;em&gt;you’re prematurely optimizing based on a hunch.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do instead
&lt;/h2&gt;

&lt;p&gt;First, don’t beat yourself up. This sort of &lt;em&gt;architecture thrashing&lt;/em&gt; is something just about every developer I’ve ever met has done.&lt;/p&gt;

&lt;p&gt;With time and experience, you will be able to make faster and better predictions for what kind of design a certain feature should have, but until then you can use a much simpler approach.&lt;/p&gt;

&lt;p&gt;Start with just getting your feature &lt;em&gt;working&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Put aside all of the &lt;em&gt;shoulds&lt;/em&gt; the software world is tossing onto your shoulders and just get it working in the simplest way possible.&lt;/p&gt;

&lt;p&gt;Perhaps begin with one single method and a set of comments that break the feature up into a high level algorithm.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt; private void purchaseItem()
 {
     //set up payment gateway api client
     //process payment
     //generate receipt
 }

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;After you have it working, commit to &lt;em&gt;maybe&lt;/em&gt; 30 minutes to an hour on basic &lt;em&gt;tidying&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Focus this time on making it easy to understand (small methods, clear names), and on aiming for the single responsibility principal with your those methods and their classes.&lt;/p&gt;

&lt;p&gt;That’s it.&lt;/p&gt;

&lt;p&gt;Move the hell on.&lt;/p&gt;

&lt;p&gt;And here is the important bit – you can &lt;em&gt;trust&lt;/em&gt; that when your feature begins being used in the wild, &lt;strong&gt;the opportunity to iteratively improve the design will reveal itself organically&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Rather than toiling over upfront psychic predictions, your optimal design &lt;em&gt;emerges&lt;/em&gt; via actual pain hot spots.&lt;/p&gt;

&lt;p&gt;So you end up with a design that is more congruent with need, and you finish your features faster, with more ease and enjoyment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Going further…
&lt;/h2&gt;

&lt;p&gt;If this resonated with you, I encourage you to take a look at my &lt;a href="https://makingloops.com/boundaries/"&gt;article on boundaries&lt;/a&gt; to deepen your understanding of other personality traits that could be affecting your growth as a developer.&lt;/p&gt;

</description>
      <category>productivity</category>
    </item>
    <item>
      <title>How To Refactor Business Logic Into Services</title>
      <dc:creator>Joe Petrakovich</dc:creator>
      <pubDate>Sun, 23 Jun 2019 00:00:00 +0000</pubDate>
      <link>https://forem.com/makingloops/how-to-refactor-business-logic-into-services-5e0h</link>
      <guid>https://forem.com/makingloops/how-to-refactor-business-logic-into-services-5e0h</guid>
      <description>&lt;p&gt;Yo dawg, are you controllers getting plump?&lt;/p&gt;

&lt;p&gt;Well I’ve got one technique you can use to drop pounds QUICK!&lt;/p&gt;

&lt;p&gt;What is one of the most &lt;a href="https://makingloops.com/fat-controller-causes/"&gt;common ways&lt;/a&gt; things start to &lt;em&gt;chub up&lt;/em&gt;?&lt;/p&gt;

&lt;p&gt;…&lt;/p&gt;

&lt;p&gt;Business Logic.&lt;/p&gt;

&lt;p&gt;Damn is it easy to slather on that biz logic fat right within our controller actions.&lt;/p&gt;

&lt;p&gt;So what can we do about it?&lt;/p&gt;

&lt;h2&gt;
  
  
  Business Logic Thinning Strategies
&lt;/h2&gt;

&lt;p&gt;There are multiple strategies you could choose to tackle business logic in a controller. Naming just two of them, you could:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Move the logic into reusable &lt;code&gt;ActionFilter&lt;/code&gt;s&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Good if the logic is common amongst other controller actions like authorizing access to an entity. However, it does still couple the business logic with controller-related libraries. In other words, you wouldn’t use an &lt;code&gt;ActionFilter&lt;/code&gt; outside of an MVC or API route, which would mean you couldn’t use that business logic outside of that domain either. Perhaps you never would anyway, so in that case, an &lt;code&gt;ActionFilter&lt;/code&gt; might be a fine, pragmatic choice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Move the logic into a service that is injected into the controller&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This way works well because your business logic can live happily in its own reusable class, without coupling itself to any particular client technology.&lt;/p&gt;

&lt;p&gt;This is the strategy we’ll be looking at for the remainder of this article.&lt;/p&gt;

&lt;h2&gt;
  
  
  Refactoring business logic into services
&lt;/h2&gt;

&lt;p&gt;First, let’s clarify terms.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Business Logic&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For our purposes, &lt;em&gt;business logic&lt;/em&gt; is any code that implements the goals of the business. For an e-commerce application, this would be processes or tasks like creating orders, processing payments, creating and sending receipts, and adding or updating customer data.&lt;/p&gt;

&lt;p&gt;In comparison, &lt;em&gt;controllers&lt;/em&gt; are only responsible for managing the incoming HTTP request, delegating the work of satisfying the request, and passing on a response.&lt;/p&gt;

&lt;p&gt;If you have trouble identifying the boundary between business logic and controller logic (clarifying responsibilities), &lt;a href="https://twitter.com/joepetrakovich"&gt;send me a message on Twitter&lt;/a&gt; and I’ll try to help.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Services&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A service is really anything that… serves.&lt;/p&gt;

&lt;p&gt;Whenever you call a method on one class from another, the calling class is the client and &lt;em&gt;the class with the method is the service&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;So there you have it, a service is a class with a method that can be called.&lt;/p&gt;

&lt;p&gt;Sticking our business logic in its own class has a number of advantages. The ability to use the feature in areas beyond just the controller, the ability to test the business logic (and the controller) in isolation, and a big improvement in readability due to the fact that a blob or blobs of logic filling the controller can be reduced to clearly named method calls.&lt;/p&gt;

&lt;h3&gt;
  
  
  The code in question
&lt;/h3&gt;

&lt;p&gt;Ok, I’m going to throw you for a bit of a loop.&lt;/p&gt;

&lt;p&gt;The code we will be refactoring is actually an Azure Function, and not a controller.&lt;/p&gt;

&lt;p&gt;Don’t freak out. Let me explain.&lt;/p&gt;

&lt;p&gt;I’m choosing to use this code because it’s actual &lt;em&gt;real world&lt;/em&gt; production code rather than an invented example.&lt;/p&gt;

&lt;p&gt;On top of that, the code used to build an Azure Function is so gosh darn similar to a controller that you would barely know the difference if I didn’t call it out.&lt;/p&gt;

&lt;p&gt;There are minor differences that I’ll point out, but the refactoring concepts in this article can still be applied 1-to-1.&lt;/p&gt;

&lt;p&gt;For that reason I’ll be using the word &lt;em&gt;controller&lt;/em&gt; and &lt;em&gt;function&lt;/em&gt; interchangeably for the remainder of this article.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Let’s see the code…&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public static class HandleNotification
{  
    public static async Task&amp;lt;IActionResult&amp;gt; Run(
        HttpRequest req, 
        ILogger log, 
        ExecutionContext context)
    {
        log.LogInformation("C# HTTP trigger function processed a request.");

        var config = new ConfigurationBuilder()
            .SetBasePath(context.FunctionAppDirectory)
            .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
            .AddEnvironmentVariables()
            .Build();

        var googleSheetsCredentialsPath = Path.Combine(context.FunctionAppDirectory, "credentials.json");

        var spreadsheetDefinition = (ID: config["SpreadsheetId"], Name: config["SpreadsheetName"], Range: config["SpreadsheetRange"]);

        var requestBody = await new StreamReader(req.Body).ReadToEndAsync();

        var googleSheetsService = GetGoogleSheetsService(googleSheetsCredentialsPath);

        log.LogInformation(requestBody);

        try
        {
            var jsonSerializerSettings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, ContractResolver = new DefaultContractResolver { NamingStrategy = new SnakeCaseNamingStrategy { } } };
            var samCartEvent = JsonConvert.DeserializeObject&amp;lt;SamCartEvent&amp;gt;(requestBody, jsonSerializerSettings);

            if (samCartEvent.Type == EventType.Order)
            {
                await AddOrderToSalesLogSpreadsheet(samCartEvent, spreadsheetDefinition, googleSheetsService);
            }
            else if (samCartEvent.Type == EventType.Refund)
            {
                await SetExistingOrderStatusToRefunded(samCartEvent, spreadsheetDefinition, googleSheetsService);
            }

            return new OkResult();
        }
        catch (Exception e)
        {
            log.LogError(e, e.Message);

            return new BadRequestObjectResult(e);
        }

    }

    private static async Task AddOrderToSalesLogSpreadsheet(SamCartEvent orderEvent, (string ID, string Name, string Range) spreadsheetDefinition, SheetsService sheetsService)
    {
        var customerName = $"{orderEvent.Customer.FirstName} {orderEvent.Customer?.LastName}";
        var programCity = orderEvent.Order.CustomFields?.SingleOrDefault(cf =&amp;gt; cf.Name == "Program City")?.Value;

        var row = new List&amp;lt;object&amp;gt;() { orderEvent.Order.ID, customerName, orderEvent.Customer.Email, programCity, DateTime.Now.ToString("MM/dd/yyyy h:mm tt"), "Ordered" };

        var valueRange = new ValueRange();
        valueRange.Range = spreadsheetDefinition.Range;
        valueRange.Values = new List&amp;lt;IList&amp;lt;object&amp;gt;&amp;gt;();
        valueRange.Values.Add(row);

        var appendRequest = sheetsService.Spreadsheets.Values.Append(valueRange, spreadsheetDefinition.ID, spreadsheetDefinition.Range);
        appendRequest.ValueInputOption = SpreadsheetsResource.ValuesResource.AppendRequest.ValueInputOptionEnum.USERENTERED;

        var appendResponse = await appendRequest.ExecuteAsync();
    }

    private static async Task SetExistingOrderStatusToRefunded(SamCartEvent refundEvent, (string ID, string Name, string Range) spreadsheetDefinition, SheetsService sheetsService)
    {
        var refundedOrderID = refundEvent.Order.ID.ToString();

        var salesLogSpreadsheet = await sheetsService.Spreadsheets.Values.Get(spreadsheetDefinition.ID, spreadsheetDefinition.Range).ExecuteAsync();

        string statusCell = FindExistingOrderStatusCell(refundedOrderID, salesLogSpreadsheet, spreadsheetDefinition.Name);

        if (statusCell != null)
        {
            var row = new List&amp;lt;object&amp;gt;() { "Refunded" };

            var valueRange = new ValueRange();
            valueRange.Range = statusCell;
            valueRange.Values = new List&amp;lt;IList&amp;lt;object&amp;gt;&amp;gt;();
            valueRange.Values.Add(row);

            var updateRequest = sheetsService.Spreadsheets.Values.Update(valueRange, spreadsheetDefinition.ID, statusCell);
            updateRequest.ValueInputOption = SpreadsheetsResource.ValuesResource.UpdateRequest.ValueInputOptionEnum.USERENTERED;

            await updateRequest.ExecuteAsync();
        }

    }

    private static string FindExistingOrderStatusCell(string orderID, ValueRange salesLogSpreadsheet, string spreadsheetName)
    {
        string statusCell = null;

        for (int i = 0; i &amp;lt; salesLogSpreadsheet.Values?.Count; i++)
        {
            var row = salesLogSpreadsheet.Values[i];

            if ((string)row[0] == orderID)
            {
                statusCell = $"{spreadsheetName}!F{i+1}";
                break;
            }
        }

        return statusCell;
    }

    private static SheetsService GetGoogleSheetsService(string credentialsPath)
    {
        GoogleCredential credential;
        string[] _scopes = { SheetsService.Scope.Spreadsheets };

        using (var stream = new FileStream(credentialsPath, FileMode.Open, FileAccess.Read))
        {
            credential = GoogleCredential.FromStream(stream).CreateScoped(_scopes);
        }

        // Create Google Sheets API service.

        var sheetsService = new SheetsService(new BaseClientService.Initializer()
        {
            HttpClientInitializer = credential,
            ApplicationName = "SamCart Notification Listener"
        });

        return sheetsService;
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Comparing Azure Functions and ASP.NET Controllers
&lt;/h3&gt;

&lt;p&gt;You’ll notice right away that this code comprises a single class with a single method that returns an &lt;code&gt;IActionResult&lt;/code&gt;. Nearly identical to an action method endpoint of a &lt;code&gt;Controller&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The main difference is that it is declared as static and passes a few arguments that you may not be used to (we’ll be changing that with our refactoring).&lt;/p&gt;

&lt;h3&gt;
  
  
  A brief explanation of behavior
&lt;/h3&gt;

&lt;p&gt;This code is used as a listener for processing sales notifications that come from a shopping cart web app called SamCart.&lt;/p&gt;

&lt;p&gt;You can configure an endpoint URL within SamCart’s admin panel that it will POST notifications to upon successful sales (or refunds). This code listens for those notifications and logs them to a Google Sheet spreadsheet using the Google Sheets API and it’s client library.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tackling the task of refactoring
&lt;/h3&gt;

&lt;p&gt;In &lt;a href="https://makingloops.com/art-of-refactoring-part-1/"&gt;an earlier article&lt;/a&gt; we looked at some strategies for code cleanup.&lt;/p&gt;

&lt;p&gt;In this particular class, we have a fairly large &lt;code&gt;Run&lt;/code&gt; method that is doing multiple logical blocks of work.&lt;/p&gt;

&lt;p&gt;Can you name them?&lt;/p&gt;

&lt;p&gt;…&lt;/p&gt;

&lt;p&gt;Setting up configuration, reading and parsing the HTTP request body into a &lt;code&gt;SamCartEvent&lt;/code&gt; POCO, and making use of the Google Sheets API to either add a new sales order or update the refund status of an existing order on a spreadsheet.&lt;/p&gt;

&lt;p&gt;In order to simplify this code so that it’s easier to read and easier to change, let’s try to break it up into smaller bits and follow the Single Responsibility Principle more closely.&lt;/p&gt;

&lt;h3&gt;
  
  
  Parsing…
&lt;/h3&gt;

&lt;p&gt;First, I found out the day of writing this article that Azure Functions allow you to benefit from the same automatic model binding that &lt;code&gt;Controllers&lt;/code&gt; are provided. I was lead astray by the original template-generated code that you can see is manually parsing the HTTP request body.&lt;/p&gt;

&lt;p&gt;So our first change is to completely eliminate that parsing logic and simply declare a &lt;code&gt;SamCartEvent&lt;/code&gt; as the first argument to the &lt;code&gt;Run&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;I’m also going to remove the logging calls and the logger argument for the clarity of this demonstration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public static async Task&amp;lt;IActionResult&amp;gt; Run(
    SamCartEvent samCartEvent, 
    ExecutionContext context)
{
    var config = new ConfigurationBuilder()
        .SetBasePath(context.FunctionAppDirectory)
        .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
        .AddEnvironmentVariables()
        .Build();

    var googleSheetsCredentialsPath = Path.Combine(context.FunctionAppDirectory, "credentials.json");

    var spreadsheetDefinition = (ID: config["SpreadsheetId"], Name: config["SpreadsheetName"], Range: config["SpreadsheetRange"]);

    var googleSheetsService = GetGoogleSheetsService(googleSheetsCredentialsPath);

    try
    {
        if (samCartEvent.Type == EventType.Order)
        {
            await AddOrderToSalesLogSpreadsheet(samCartEvent, spreadsheetDefinition, googleSheetsService);
        }
        else if (samCartEvent.Type == EventType.Refund)
        {
            await SetExistingOrderStatusToRefunded(samCartEvent, spreadsheetDefinition, googleSheetsService);
        }

        return new OkResult();
    }
    catch (Exception e)
    {
        return new BadRequestObjectResult(e);
    }       
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;With that cleaned up, we are left with only two primary tasks. Setting up configuration, and making use of the Google Sheets API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a service class
&lt;/h2&gt;

&lt;p&gt;Looking a bit deeper, we see that the configuration is only used by the Google Sheets API, so we could say that the work involved in setting it up is really along the same logical block of work of calling the API’s various services.&lt;/p&gt;

&lt;p&gt;For that reason, it makes sense to move all of that code into it’s own class to be used as a service.&lt;/p&gt;

&lt;p&gt;To do that, I’ll create a new class and method, named after what this code is actually doing from a high level. Ultimately we are using Google Sheets to log our SamCart events to a spreadsheet, so we move all of that code behind a method called &lt;code&gt;LogEvent&lt;/code&gt; for the class &lt;code&gt;GoogleSheetsSamCartEventLogger&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;One key difference here is that at the time of writing this article, there was no way to inject the ExecutionContext needed to derive the location of the configuration file, so the first line of code is an alternative way of doing so.&lt;/p&gt;

&lt;p&gt;The rest is the same as before: getting the spreadsheet configuration and calling the appropriate API endpoint based on the type of &lt;code&gt;SamCartEvent&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class GoogleSheetsSamCartEventLogger
{
    public async Task LogEvent(SamCartEvent samCartEvent)
    {
        var functionAppDirectory = Environment.GetEnvironmentVariable("AzureWebJobsScriptRoot")
                ?? (Environment.GetEnvironmentVariable("HOME") == null
                    ? Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)
                    : $"{Environment.GetEnvironmentVariable("HOME")}/site/wwwroot");

        var config = new ConfigurationBuilder()
            .SetBasePath(functionAppDirectory)
            .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
            .AddEnvironmentVariables()
            .Build();

        var googleSheetsCredentialsPath = Path.Combine(functionAppDirectory, "credentials.json");
        var spreadsheetDefinition = (ID: config["SpreadsheetId"], Name: config["SpreadsheetName"], Range: config["SpreadsheetRange"]);
        var googleSheetsService = GetGoogleSheetsService(googleSheetsCredentialsPath);

        if (samCartEvent.Type == EventType.Order)
        {
            await AddOrderToSalesLogSpreadsheet(samCartEvent, spreadsheetDefinition, googleSheetsService);
        }
        else if (samCartEvent.Type == EventType.Refund)
        {
            await SetExistingOrderStatusToRefunded(samCartEvent, spreadsheetDefinition, googleSheetsService);
        }

    }

    private async Task AddOrderToSalesLogSpreadsheet(SamCartEvent orderEvent, (string ID, string Name, string Range) spreadsheetDefinition, SheetsService sheetsService)
    {
        //...

    }

    private async Task SetExistingOrderStatusToRefunded(SamCartEvent refundEvent, (string ID, string Name, string Range) spreadsheetDefinition, SheetsService sheetsService)
    {
        //...

    }

    private string FindExistingOrderStatusCell(string orderID, ValueRange salesLogSpreadsheet, string spreadsheetName)
    {
        //...

    }

    private SheetsService GetGoogleSheetsService(string credentialsPath)
    {
        //...

    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;With this service class created, we now just need to make use of it in our original &lt;code&gt;Run&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;The fastest way to do that would be “new it up” directly in the method. Let’s go that route first, and then we will improve upon it further on in the article.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public async Task&amp;lt;IActionResult&amp;gt; Run(SamCartEvent samCartEvent)
{
    try
    {
        var eventLogger = new GoogleSheetsSamCartEventLogger();

        await eventLogger.LogEvent(samCartEvent);

        return new OkResult();
    }
    catch (Exception e)
    {
        return new BadRequestObjectResult(e);
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now we are looking good.&lt;/p&gt;

&lt;p&gt;We have a much thinner method with a lot less reasons to change (code stability). Our method simply needs to hand off the work to a service and respond with a relevant result.&lt;/p&gt;

&lt;p&gt;It is now more likely that the event logger would change, rather than the function or controller itself.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Can we improve this code further?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Earlier we mentioned “newing up” the event logger. If we instead make use of dependency injection, we can move the &lt;em&gt;responsibility of choice&lt;/em&gt; onto the application itself.&lt;/p&gt;

&lt;p&gt;Responsibility of choice is a term I’m coining just now which entails the decision of how a certain behavior is implemented.&lt;/p&gt;

&lt;p&gt;As it stands right now, we are &lt;em&gt;hardcoding&lt;/em&gt; the use of Google Sheets by our listener. Meaning it would require an actual code change and redeployment to ever log our events differently. This could be perfectly fine if you had no plans or requirements to change your logging mechanism, but for the sake of demonstration, let’s say that our customer mentioned possibly wanting to move the logging to Microsoft’s Excel, or to a plain old text file.&lt;/p&gt;

&lt;p&gt;With that being the case, it would be in our benefit to allow &lt;em&gt;changing the implementation&lt;/em&gt; of event logging, without having to change the controller.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Less change = less risk.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is where a combination of interfaces and &lt;a href="https://makingloops.com/why-should-you-use-dependency-injection/"&gt;dependency injection&lt;/a&gt; come in handy.&lt;/p&gt;

&lt;p&gt;First, we can &lt;em&gt;extract an interface&lt;/em&gt; from our Google Sheets event logger.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public interface ISamCartEventLogger
{
    Task LogEvent(SamCartEvent samCartEvent);
}

public class GoogleSheetsSamCartEventLogger : ISamCartEventLogger
{
    //...

}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;With this in place, we are now free to create as many different logger implementations as we want.&lt;/p&gt;

&lt;p&gt;As an example, we could have a very primitive console logger like this one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public interface ISamCartEventLogger
{
    Task LogEvent(SamCartEvent samCartEvent);
}

public class ConsoleSamCartEventLogger : ISamCartEventLogger
{
    public async Task LogEvent(SamCartEvent samCartEvent) 
    {
        if (samCartEvent.Type == EventType.Order)
        {
            Console.WriteLine("An order was placed!");
        }
        else if (samCartEvent.Type == EventType.Refund)
        {
            Console.WriteLine("Aww... a refund.");
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Interfaces are fun, but without dependency injection we would still have to change the code of the controller’s &lt;code&gt;Run&lt;/code&gt; method to “new up” the alternative implementation.&lt;/p&gt;

&lt;p&gt;Remember this?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public async Task&amp;lt;IActionResult&amp;gt; Run(SamCartEvent samCartEvent)
{
    try
    {
        var eventLogger = new ConsoleSamCartEventLogger();

        await eventLogger.LogEvent(samCartEvent);

        //...

    }
    //...

}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Let’s instead make use of the built in dependency injection &lt;em&gt;container&lt;/em&gt; that is provided by ASP.NET to move the implementation decision to the application startup configuration.&lt;/p&gt;

&lt;p&gt;To do that in an Azure Function is very similar to how you do that in an ASP.NET MVC or Web API project, you simply register your types in the Startup.cs class and then use them via constructor parameters.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class Startup : FunctionsStartup
{
    public override void Configure(IFunctionsHostBuilder builder)
    {
        builder.Services.AddScoped&amp;lt;ISamCartEventLogger, GoogleSheetsSamCartEventLogger&amp;gt;();
    }
}

public class HandleNotification
{
    private readonly ISamCartEventLogger eventLogger;

    public HandleNotification(ISamCartEventLogger eventLogger)
    {
        this.eventLogger = eventLogger;
    }

    public async Task&amp;lt;IActionResult&amp;gt; Run(SamCartEvent samCartEvent)
    {
        try
        {
            await eventLogger.LogEvent(samCartEvent);
        }
        //...

    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;With it moved, we have improved the stability of the code by no longer needing to change the controller’s &lt;code&gt;Run&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;We have also dramatically enhanced the readability of each individual component. Compare the above code snippet to the original code at the beginning of the article.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
    </item>
    <item>
      <title>Azure Functions vs. AWS Lambdas: A .NET Developer's Perspective</title>
      <dc:creator>Joe Petrakovich</dc:creator>
      <pubDate>Tue, 11 Jun 2019 00:00:00 +0000</pubDate>
      <link>https://forem.com/makingloops/azure-functions-vs-aws-lambdas-a-net-developer-s-perspective-4ilc</link>
      <guid>https://forem.com/makingloops/azure-functions-vs-aws-lambdas-a-net-developer-s-perspective-4ilc</guid>
      <description>&lt;p&gt;Have you gotten around to trying this “serverless” stuff out?&lt;/p&gt;

&lt;p&gt;I recently had to scratch my own itch and put together a couple small utilities, one with an &lt;em&gt;AWS Lambda&lt;/em&gt;, and the other with an &lt;em&gt;Azure Function&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Have you wondered if one was better than the other?&lt;/p&gt;

&lt;p&gt;The experiences were… similar, but also &lt;em&gt;quite&lt;/em&gt; different, and thus I share a .NET developer’s perspective of each.&lt;/p&gt;

&lt;p&gt;Before I start, keep in mind that whilst building both of these utilities, my mindset was “let’s get this thing live as fast as possible”, and &lt;strong&gt;not&lt;/strong&gt; “let’s do precision research on the most effective way to implement this using each platforms recommended tooling and workflow”.&lt;/p&gt;

&lt;p&gt;Nowadays I tend to try and get something working &lt;em&gt;first&lt;/em&gt;, and then refactor it into something more elegant &lt;em&gt;after&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;With that in mind, this article reflects my opinion as a first time user just getting started, with a bias of being heavily engulfed in the .NET world &lt;em&gt;and&lt;/em&gt; with quite a bit of prior Azure experience (I’ve built out an entire Azure cloud infrastructure from scratch.)&lt;/p&gt;

&lt;p&gt;In contrast this was the first time touching AWS other than migrating personal files from Dropbox to an S3 bucket (easy way to save 10 bucks a month!)&lt;/p&gt;

&lt;h3&gt;
  
  
  Building an AWS Lambda
&lt;/h3&gt;

&lt;p&gt;The idea of using an AWS Lambda came to me when I was stubbornly resistant to putting any javascript on this very blog. Not because I don’t like javascript, but simply because the blog started out as pure static html and css and the minimalist in me wanted to keep it that way.&lt;/p&gt;

&lt;p&gt;I needed an elegant way to allow folks on my email list to be able to sign up for other gizmos on my site &lt;em&gt;even if they had already signed up for something else&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;My email provider, Mailchimp, would require you to check off boxes for what they wanted, which was a shit experience.&lt;/p&gt;

&lt;p&gt;I could have called their API via some javascript, but rather than shattering my holy static site purism, I decided to have my email sign-up form simply POST itself and handle the work on a server. Since I had recently deallocated my personal server droplet, I thought hey, a serverless function would be perfect!&lt;/p&gt;

&lt;p&gt;Being the first time I dipped my toes into serverless, I found the experience &lt;em&gt;very&lt;/em&gt; liberating.&lt;/p&gt;

&lt;p&gt;Once you create an AWS developer account and navigate around the grid of seemingly &lt;em&gt;hundreds&lt;/em&gt; of oddly named AWS resource offerings displayed in Amazon’s classic ugly font against a dull gray background, it’s fairly seamless setting up a basic Lambda.&lt;/p&gt;

&lt;p&gt;The Lambda creation workflow guides you along choosing a language and platform, sets you up with an in-browser text editor, and presents an interface similar to one of those middle school programming languages where you link different components together for input and output.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1irbnr2rnzpvi8k07jo2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1irbnr2rnzpvi8k07jo2.png" alt="The AWS Lambda editor" width="800" height="455"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It’s ugly, and it gets more confusing as you add additional complexities, but it works.&lt;/p&gt;

&lt;p&gt;For this project, I chose NodeJS as the framework, since I had recently enjoyed how its expansive package ecosystem paired with javascript’s loose typing allowed me to fumble together guilt-free utilities at lightning speed.&lt;/p&gt;

&lt;p&gt;Now, I’m willing to bet there are nicer deployment experiences than what I used, but combining NodeJS with the in-browser editor was not ideal.&lt;/p&gt;

&lt;p&gt;If you haven’t used NodeJS before, when you want to pull in an outside library like you would with NuGet in the .NET world, you have to declare its use first and then &lt;em&gt;npm&lt;/em&gt; (the package manager) pulls in all of the library’s raw code right within your project into a folder called &lt;code&gt;node_modules&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This process is mostly automatic if you have your local dev environment set up for it, but it doesn’t work in the AWS Lambda in-browser editor. The only solution at the time of building it was to instead build the majority of the utility locally using VS Code, then uploading it &lt;em&gt;and&lt;/em&gt; the &lt;code&gt;node_modules&lt;/code&gt; folder back into AWS.&lt;/p&gt;

&lt;p&gt;Like I said, there are probably better ways, but this was my experience trying to build at relatively fast pace.&lt;/p&gt;

&lt;p&gt;Once I had the utility doing what I needed, which was essentially just calling a few Mailchimp API endpoints, I just had to configure the function so it could actually be executed via an HTTP POST.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;You can see the code &lt;a href="https://github.com/joepetrakovich/lambda-chimp" rel="noopener noreferrer"&gt;here&lt;/a&gt; if you’re curious. It ain’t perfect but it gets the job done!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This was another knock against AWS. Although I will say, it was still a good experience, but you’ll see why I say this when we get to the Azure side of things.&lt;/p&gt;

&lt;p&gt;In order to set up the endpoint, you had to provision another kind of AWS resource called an API Gateway.&lt;/p&gt;

&lt;p&gt;An API Gateway allows you to set up the URL format, authentication scheme, and available HTTP methods that you can slap in front of any other AWS resource as the first line of interaction from the public internet into your cloud resources.&lt;/p&gt;

&lt;p&gt;Still easy in the grande scheme of software development and deployment, but again, Amazon could definitely work on the user experience design.&lt;/p&gt;

&lt;p&gt;I did have some difficulty creating an authorization scheme so that only my website could call it. Compared to Azure Functions, it was rather confusing to set up a token that could be used. Eventually I figured it out, but it’s worth noting that it wasn’t seamless.&lt;/p&gt;

&lt;p&gt;Once the API Gateway was up and running, I was good to go.&lt;/p&gt;

&lt;p&gt;At the time of this article being written, this Lambda has been serving this site without any errors for several months at no cost to me.&lt;/p&gt;

&lt;p&gt;Despite the quirks with uploading javascript files and vanilla Amazon design, I found this to be very gratifying.&lt;/p&gt;

&lt;p&gt;Both AWS and Azure offer these services with 1 million free executions per month, so if you’re creating something with less traffic than that, it’s pretty amazing to be able to plug into that computational power for almost no cost.&lt;/p&gt;

&lt;h4&gt;
  
  
  Pros
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Integrates well with other AWS offerings.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you’re already an AWS user, you might as well stay within the confines. Lambdas are built with the expectation that you will connect them to your other AWS resources, and from my experience, both AWS and Azure are similar enough that there wouldn’t be a compelling reason to exit your existing cloud.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you prefer command-line tooling, or are a linux/java dev, you might enjoy AWS Lamdas more.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I believe Amazon skimps on their UI because they offer robust command-line tooling. I’ve only used a bit of it in my experience, but have heard from others that it is great. I encourage you to explore that on your own and report back.&lt;/p&gt;

&lt;h4&gt;
  
  
  Cons
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;The user experience is lacking.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I find Amazon to be lacking in design in just about ALL of their products. Maybe it’s just me but I find myself wondering how anyone can navigate around their sites if they aren’t already a keen technologist. But hey, money bags Bezos obviously isn’t hurting from it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Setting up the API Gateway was clunky.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This was similar to above, a user experience problem. A bit confusing setting up an authorization scheme.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Manually uploading the source files wasn’t pleasant.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It’s likely AWS has a better deployment method, but this was the straightforward solution using the in-browser editor.&lt;/p&gt;

&lt;p&gt;For all I know, there might be Visual Studio tooling that would have made it easier, but again, it wasn’t obvious, and you’ll see below how drastically different this experience was whilst already being in the Microsoft landscape as a .NET developer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Azure Functions
&lt;/h3&gt;

&lt;p&gt;My introduction to Azure Functions was very recent, about one week before writing this article, so it’s fresh on my mind.&lt;/p&gt;

&lt;p&gt;A friend of mine uses a shopping cart tool that doesn’t provide a public API to its users, but instead offers a push notification event option. So you can’t them ask “how many sales have I made for this product?”, but you can listen for sales events, collect that data into your own database, and then you’ll be able to answer that question for yourself.&lt;/p&gt;

&lt;p&gt;Handling notifications from an external service is a perfect use case for a serverless utility, so this was my hell yes moment. Time to try out new toys!&lt;/p&gt;

&lt;p&gt;I had two big advantages coming into this, one that I had already used Lambdas, and two, that I have essentially built an Azure cloud infrastructure from the ground up, so it was all familiar territory. First I followed the &lt;em&gt;Getting Started&lt;/em&gt; documentation to familiarize myself with the &lt;em&gt;Portal based deployment&lt;/em&gt; which is analogous to the AWS in-browser text editor.&lt;/p&gt;

&lt;p&gt;What I noticed right away, to contrast from AWS, is that two of the main trouble spots I had with AWS were taken care of by default in Azure. Simply selecting the Webhook + API function template option from the initial wizard automatically created the API endpoint for calling the function, along with a client authentication token.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzn9w4dsoda6hpcjrs3bz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzn9w4dsoda6hpcjrs3bz.png" alt="The Azure Function initial creation wizard" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the prior step you could also choose from a number of deployment methods including Visual Studio, VS Code, Portal editor, and command-line.&lt;/p&gt;

&lt;p&gt;Because the functionality of this utility was going to be rather complex (parsing an incoming notification and calling the Google Spreadsheets API to fill a table), I decided to try out the Visual Studio deployment option rather than the browser editor.&lt;/p&gt;

&lt;p&gt;For this, I upgraded Visual Studio 2017 to 2019 (yay more new toys!) and used the included template for an Azure Function.&lt;/p&gt;

&lt;p&gt;This was dope.&lt;/p&gt;

&lt;p&gt;It sets you up with a local dev environment to test on, and get access to the usual one-click publishing feature that lets you push everything right into Azure. One thing that stood out to me as pretty nifty was the local to remote app settings UI.&lt;/p&gt;

&lt;p&gt;Your app settings are encrypted locally, and when deployed, you could see a side-by-side comparison of local vs. remote settings values where you could edit right within Visual Studio.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F15znglpyow0awvddlvs6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F15znglpyow0awvddlvs6.png" alt="The Azure Function app settings publish dialog" width="800" height="592"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It should be expected, really, but this is where it helps to already be a .NET and Visual Studio user. The experience of building this (in .NET Core by the way), testing it, and deploying it was &lt;em&gt;seamless&lt;/em&gt;. All the way from finding up-to-date documentation and guides, to having a live function that processes those shopping cart events.&lt;/p&gt;

&lt;p&gt;It was also nice that the function itself was very similar to an ASP.NET Core controller action.&lt;/p&gt;

&lt;h4&gt;
  
  
  Pros
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;As a .NET Developer, Azure’s integration with Visual Studio made the entire experience far more delightful.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can choose to build and deploy from the Azure Portal, Visual Studio, VS Code, or command line, and each option came with clear documentation and easy publishing steps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HTTP trigger endpoints and authentication configured by default, stupidly easy.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The most common ways you will want to set up your function are configured by default. There aren’t additional resources you need to learn about or set up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strong documentation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Microsoft has been putting a ton of effort in to create clear and up-to-date documentation and the effort is obvious in this area.&lt;/p&gt;

&lt;h4&gt;
  
  
  Cons
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;I’m reaching here, but if I were to knock something it would be that sometimes it’s not clear what things cost.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For example, I understood that 1-million executions of the function would be free, but in order to have the function live, you also need a storage account. It might be nice to display dollar values in-and-around the deployment screens.&lt;/p&gt;

&lt;p&gt;Both Visual Studio and Azure Portal do a nice job at auto-populating everything you need, so it would be cool if it also made it clear what the total monthly cost estimate is. I noticed that my current Azure storage account and logging resource will bill me about $0.12 per month as it currently stands.&lt;/p&gt;

&lt;h3&gt;
  
  
  Final Thoughts
&lt;/h3&gt;

&lt;p&gt;Coming from my current job where we are still deploying manually via installer UIs on virtual machines, this whole experience with serverless functions was an incredible delight. That’s with both AWS and Azure.&lt;/p&gt;

&lt;p&gt;It brought me back to that classic experience of my childhood when I got boosts of self-esteem from writing a bit of HTML and CSS and seeing it magically create something in the browser instantly. This was that experience all over again except with actual computation on a live HTTP endpoint over the web.&lt;/p&gt;

&lt;p&gt;The fact that we can build these micro apps at little to no cost is also a real game-changer, especially as an entrepreneur. It opens up an entirely new canvas to create upon.&lt;/p&gt;

&lt;p&gt;I encourage you to try both AWS Lamdbas and Azure Functions, since I found them to be similar enough that your own personal preferences will be more important than my opinion. With that said, I believe you will find a much more seamless and fluid experience by using Visual Studio with Azure Functions. All the futzing around I had to do with AWS was taken care of by default in Azure.&lt;/p&gt;

&lt;p&gt;As you might expect, it’s a lot like using a Mac if you are an iPhone user. You benefit greatly by staying within the walls of your ecosystem.&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>azure</category>
    </item>
    <item>
      <title>Practical Visual Studio Tips Part 1</title>
      <dc:creator>Joe Petrakovich</dc:creator>
      <pubDate>Thu, 25 Apr 2019 00:00:00 +0000</pubDate>
      <link>https://forem.com/makingloops/practical-visual-studio-tips-part-1-3c0a</link>
      <guid>https://forem.com/makingloops/practical-visual-studio-tips-part-1-3c0a</guid>
      <description>&lt;p&gt;One of the best ways to become a solid, more productive and effective developer is to really &lt;strong&gt;master&lt;/strong&gt; the things you do &lt;em&gt;most often&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://makingloops.com/extract-method/" rel="noopener noreferrer"&gt;kickoff article&lt;/a&gt; that initiated this &lt;em&gt;practical Visual Studio tips&lt;/em&gt; series highlighted the &lt;em&gt;extract method&lt;/em&gt; refactoring (my absolute favorite) and how we can harness Visual Studio’s keyboard shortcuts like a Dothraki warrior so that we can write code fast (if we want to), while improving readability.&lt;/p&gt;

&lt;p&gt;For this next part in the series, we’ll take a look at &lt;strong&gt;two more shortcuts&lt;/strong&gt; that you can use on a daily basis to speed up your workflow. Now, these particular shortcuts are rather basic, obvious even, but by giving them focus here and practicing them, you’ll use them more consistently and integrate them into a daily habit.&lt;/p&gt;

&lt;h3&gt;
  
  
  We humans are funny, or at least I am, anyway.
&lt;/h3&gt;

&lt;p&gt;I’ll do the same thing over and over, and even after learning a faster way to do it, I’ll continue doing it my old way anyway simply because that’s the groove currently patterned in my brain.&lt;/p&gt;

&lt;p&gt;The purpose of this article and often many of my articles are to give old patterns a second look so we can question our behaviors and change if necessary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tab + arrow key suggestion invocations
&lt;/h2&gt;

&lt;p&gt;For the first one, we’ll go &lt;em&gt;reaaaal&lt;/em&gt; primitive, but I’ll add a twist, so stick with me.&lt;/p&gt;

&lt;p&gt;When you’re writing code in Visual Studio, a program called &lt;em&gt;Intellisense&lt;/em&gt; is actively analyzing each character you type and trying to determine what you might be trying to write.&lt;/p&gt;

&lt;p&gt;Typing is &lt;em&gt;slow&lt;/em&gt;, so if the editor can predict what you are about to write &lt;em&gt;with accuracy&lt;/em&gt;, you can &lt;em&gt;lean on it&lt;/em&gt; to reduce the number of keystrokes you have to make.&lt;/p&gt;

&lt;p&gt;It’s here to make our lives easier!&lt;/p&gt;

&lt;p&gt;On top of that I’ve found that Intellisense is pretty damn smart, and either suggests exactly what I want right away, or the option I want is one or two list items up or down.&lt;/p&gt;

&lt;p&gt;Pay particular attention below at how I can type these extra long statements in about half the number of keystrokes compared to typing each letter manually.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmakingloops.com%2Fpublic%2Ftab-invoke.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmakingloops.com%2Fpublic%2Ftab-invoke.gif" alt="Tab invoking Intellisense suggestions"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What I want for you is to get used to either hitting the tab key to &lt;em&gt;invoke the suggestion&lt;/em&gt;, or using the arrow keys on your keyboard to select the desired option in the Intellisense dropdown menu, then hitting tab or enter to invoke it.&lt;/p&gt;

&lt;p&gt;Type only the first few characters and let your eyes pinpoint the correct suggestion as it appears.&lt;/p&gt;

&lt;p&gt;Start to relax and trust that what you want will be available and &lt;em&gt;lean on&lt;/em&gt; Intellisense to improve your productivity and make you seem like a developer wizard to your friends and teammates.&lt;/p&gt;

&lt;p&gt;Funny side note, I’ve actually had two past lovers comment “Damn, that’s really hot” when I’m dishing out commands to the console.&lt;/p&gt;

&lt;p&gt;Who would have thought, skill and proficiency is sexy!&lt;/p&gt;

&lt;p&gt;Master your editor and keyboard.&lt;/p&gt;

&lt;h2&gt;
  
  
  Renaming like a savage
&lt;/h2&gt;

&lt;p&gt;Alright, now you’re going about your day, shrinking big methods via &lt;em&gt;extract method&lt;/em&gt;, leaning on Intellisense predictions so you can type less, what else can we do to pro it up?&lt;/p&gt;

&lt;p&gt;Another &lt;em&gt;very&lt;/em&gt; common thing you probably do as a compassionate and considerate dev, is &lt;strong&gt;constantly refine your names&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The longer you work on a codebase, the better your understanding of it, which leads to clearer, more accurate class, method, property, and variable names.&lt;/p&gt;

&lt;p&gt;I rename all the time. Often I’ll come up with one name at the start of a method, then after actually implementing the method, I’ll realize it could be named in a way that conveys it’s intent even better.&lt;/p&gt;

&lt;p&gt;For that reason, making the process of renaming as fast as possible is another worthy endeavor.&lt;/p&gt;

&lt;p&gt;Fortunately for us, it has a built-in keyboard shortcut, so we only have to understand how to use it, &lt;em&gt;and&lt;/em&gt; to remember it.&lt;/p&gt;

&lt;p&gt;The first act in a renaming is to select the entire existing name you’d like to change.&lt;/p&gt;

&lt;p&gt;To do that, the best way I’ve found is to use your mouse or trackpad and double-click the word itself.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmakingloops.com%2Fpublic%2Fdouble-click.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmakingloops.com%2Fpublic%2Fdouble-click.gif" alt="Double-click to select whole name"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Visual Studio understands logical delimiters of a name in code and will automatically highlight the word.&lt;/p&gt;

&lt;p&gt;Once highlighted, hold down the CTRL key, then quickly hit the letter R &lt;em&gt;twice&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmakingloops.com%2Fpublic%2Frename.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmakingloops.com%2Fpublic%2Frename.gif" alt="Rename hot-key"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the rename refactoring is invoked, you can begin typing your new name at once and notice it changing across all usages.&lt;/p&gt;

&lt;p&gt;And there you have it.&lt;/p&gt;

&lt;p&gt;Get used to trusting intellisense, and speed up your renames.&lt;/p&gt;

&lt;p&gt;In the next article in the series, we’ll either continue mastering common tasks, or I may go for something new that peaks my interest… I haven’t quiiiite decided.&lt;/p&gt;

&lt;p&gt;Stay tuned!&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>Perfectionism</title>
      <dc:creator>Joe Petrakovich</dc:creator>
      <pubDate>Tue, 09 Apr 2019 00:00:00 +0000</pubDate>
      <link>https://forem.com/makingloops/perfectionism-3fj7</link>
      <guid>https://forem.com/makingloops/perfectionism-3fj7</guid>
      <description>&lt;p&gt;When I was younger, I used to think perfectionism was a good thing.&lt;/p&gt;

&lt;p&gt;The word “perfect” is in it, after all.&lt;/p&gt;

&lt;p&gt;But I never thought &lt;em&gt;I&lt;/em&gt; was a perfectionist. I only had heard other people say it about themselves, wearing the term like a badge of honor.&lt;/p&gt;

&lt;p&gt;“Oh, it’s just because I’m a perfectionist” someone would exclaim, after they had just created something utterly amazing.&lt;/p&gt;

&lt;p&gt;So in my mind, it meant &lt;em&gt;striving for the utmost&lt;/em&gt;. Doing your absolute best work.&lt;/p&gt;

&lt;p&gt;I surely wasn’t one of those, I thought. I just did what felt &lt;em&gt;normal&lt;/em&gt; to me. The standard I set for myself is what I’d always known, so I wasn’t aware that it was actually a struggle. That I was actually draining myself to the point of burn out, disguised as &lt;em&gt;just being very thorough&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;It wasn’t until later in my career did I realize the plague that perfectionism really is, and that I had been stuck in it &lt;em&gt;for most of my life without even realizing it&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The experience of perfectionism for me is a ruthless attempt at control and a nagging anxiety when something threatens that control. It’s also like turning up the volume knob on your focus and attention to detail. You take in every last drop of information (to reduce the risk of doing something wrong).&lt;/p&gt;

&lt;p&gt;If you’re like me, you’ve likely been doing this your whole life.&lt;/p&gt;

&lt;p&gt;If you’ve ever worked on some kind of feature at your job, taking 100% ownership of it, and then felt triggered by another developer “stepping into your territory”, it’s likely you have a similar issue with control and perfectionism.&lt;/p&gt;

&lt;p&gt;When I sit with it, I can see it really comes from fear.&lt;/p&gt;

&lt;p&gt;Fear that something will go terribly wrong. You may not have realized it at the time, but you put so much intensity and focus on getting it right &lt;em&gt;all on your own&lt;/em&gt;, that the idea of someone else stepping on your perfect sand castle brings you into turmoil.&lt;/p&gt;

&lt;p&gt;One of the things I’m having to learn though, is that you can’t do it &lt;em&gt;all on your own&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;You can try, but you can only get so far, and you’ll eventually burn out.&lt;/p&gt;

&lt;p&gt;It’s a &lt;em&gt;success barrier&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;When you work on a team, you have to develop trust in others, and you have to be okay with differences in skill, interpretation, attention to detail, and style.&lt;/p&gt;

&lt;p&gt;You have to be able to share the workload and let things go.&lt;/p&gt;

&lt;p&gt;If you’re like me, you’ve likely grown up thinking you’re all on your own. Thinking you can only depend on yourself. That no one else will help you, so ya might as well become superhuman and figure it out alone.&lt;/p&gt;

&lt;p&gt;If you don’t become aware of this pattern, you will continue to burn yourself out over and over and over.&lt;/p&gt;

&lt;p&gt;You don’t have to do it all on your own.&lt;/p&gt;

&lt;p&gt;Breathe.&lt;/p&gt;

&lt;p&gt;Let your teammates learn some things on their own. Give them the opportunity to try and succeed or try and fail.&lt;/p&gt;

&lt;p&gt;Now, looking at it another way, learning how to be okay with some amount of uncertainty and &lt;em&gt;mess&lt;/em&gt; is something to continually work on, but so is &lt;strong&gt;acceptance&lt;/strong&gt; of who you currently are.&lt;/p&gt;

&lt;p&gt;Despite it being born out of necessity as a child, your attention to detail is certainly a powerful advantage, so don’t be ashamed of it.&lt;/p&gt;

&lt;p&gt;I sometimes catch myself feeling like &lt;em&gt;the controlling tyrant&lt;/em&gt; when my precise awareness has me wagging my finger at my less detailed cohorts.&lt;/p&gt;

&lt;p&gt;Of course, you don’t want to be judging others as if they are &lt;em&gt;less than&lt;/em&gt;, but it is absolutely 100% okay and valid to express your concern and opinion based on your hyper-detailed experience.&lt;/p&gt;

&lt;p&gt;There’s a dichotomy at play.&lt;/p&gt;

&lt;p&gt;Be okay with some amount of mess, the human variance, yet don’t be afraid to use your advantage to maintain order and prevent the always impending chaos.&lt;/p&gt;

&lt;p&gt;Orderliness and attention to detail are powerful strengths, yet when they sink into anxiety from loss of control, it becomes perfectionism, and perfectionism controls YOU.&lt;/p&gt;

&lt;p&gt;Learn the difference.&lt;/p&gt;

&lt;h2&gt;
  
  
  Going further…
&lt;/h2&gt;

&lt;p&gt;If this resonated with you, I encourage you to take a look at &lt;a href="https://makingloops.com/boundaries/"&gt;my article on boundaries&lt;/a&gt; to deepen your understanding of other personality traits that could be affecting your growth as a person and as a developer.&lt;/p&gt;

</description>
      <category>career</category>
      <category>leadership</category>
    </item>
    <item>
      <title>The fastest way to sexy code using Visual Studio</title>
      <dc:creator>Joe Petrakovich</dc:creator>
      <pubDate>Fri, 22 Mar 2019 00:00:00 +0000</pubDate>
      <link>https://forem.com/makingloops/the-fastest-way-to-sexy-code-using-visual-studio-37fo</link>
      <guid>https://forem.com/makingloops/the-fastest-way-to-sexy-code-using-visual-studio-37fo</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fwtguz44h6pl1i4zj9wky.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fwtguz44h6pl1i4zj9wky.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here it comes.&lt;/p&gt;

&lt;p&gt;Another spicy new update to Visual Studio.&lt;/p&gt;

&lt;p&gt;And yet, it’ll come and go and I’ll most likely continue using the same damn subset of features I’ve gotten used to for years.&lt;/p&gt;

&lt;p&gt;Same as you, right?&lt;/p&gt;

&lt;p&gt;Code highlighting, &lt;em&gt;Find By References&lt;/em&gt;, and &lt;em&gt;Go To Declaration&lt;/em&gt;. A glorified text editor with enhanced file search.&lt;/p&gt;

&lt;p&gt;It takes &lt;strong&gt;time&lt;/strong&gt; and &lt;strong&gt;deliberate effort&lt;/strong&gt; to learn a new feature, let alone implement it regularly enough that its true value is realized.&lt;/p&gt;

&lt;p&gt;Time we just DON’T have.&lt;/p&gt;

&lt;p&gt;Which sucks because there are &lt;em&gt;treasure troves of wizardy&lt;/em&gt; hidden beneath Visual Studio’s hefty mass that could improve our code, our software overall, and make us better devs in the process.&lt;/p&gt;

&lt;p&gt;But there is one feature that you don’t have to look too deep to find.&lt;/p&gt;

&lt;p&gt;One that will &lt;em&gt;massively&lt;/em&gt; boost the quality of your code.&lt;/p&gt;

&lt;p&gt;In this article I want to share with you not only the &lt;em&gt;Extract Method&lt;/em&gt; refactoring feature, but also how to perform it &lt;em&gt;at lightspeed&lt;/em&gt;, and how to use it most effectively, hopefully resulting in a surge of brilliantly clear code from your fingertips and immense gratitude from your teammates and future developers.&lt;/p&gt;

&lt;p&gt;Did I sell ya on it?&lt;/p&gt;

&lt;h2&gt;
  
  
  Readability: an aside
&lt;/h2&gt;

&lt;p&gt;Before we jump in to the technicals, let’s back it up with a bit of context.&lt;/p&gt;

&lt;p&gt;Code &lt;em&gt;readability&lt;/em&gt; is a human factor.&lt;/p&gt;

&lt;p&gt;We strive to make our code as readable as possible, often over all other considerations, so that the next developer (which could be you) has the best possible chance of understanding &lt;strong&gt;what it does&lt;/strong&gt; and &lt;strong&gt;what you intended&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Despite code being the literal manifestation of pure logic, it is still an exhausting endeavor for any human to decypher for too long.&lt;/p&gt;

&lt;p&gt;For that, we compose our code to be more like prose in a book.&lt;/p&gt;

&lt;p&gt;Its algorithms, object interactions, and architectures ideally can be read aloud to a senior dev, an intern, and a business analyst, making up a coherent &lt;em&gt;ubiquitous language&lt;/em&gt;, as they say in the DDD world.&lt;/p&gt;

&lt;p&gt;With this in mind, I believe &lt;em&gt;liberal&lt;/em&gt; use of clearly named methods is the easiest way to improve readability in our codebases.&lt;/p&gt;

&lt;p&gt;Clearly named methods can be added both at the time of development, or during refactoring.&lt;/p&gt;

&lt;p&gt;I often like to write an algorithm &lt;em&gt;first&lt;/em&gt;, then decide after how it will be composed and named.&lt;/p&gt;

&lt;p&gt;This is where the &lt;em&gt;extract method&lt;/em&gt; refactoring joins the party.&lt;/p&gt;

&lt;h2&gt;
  
  
  The way of the sloth
&lt;/h2&gt;

&lt;p&gt;The extract method refactoring simply means moving a block of code into a new method, and then calling that method from the original location.&lt;/p&gt;

&lt;p&gt;Replacing code with a method.&lt;/p&gt;

&lt;p&gt;Now, you could do this manually by creating a new method, highlighting, copying, and pasting the code block into that method, then deleting that original block and replacing it with the method call.&lt;/p&gt;

&lt;p&gt;It was exhausting just typing that…&lt;/p&gt;

&lt;p&gt;Below we can see that strategy in action on a particularly chubby method that binds a view model representing a quote for a loan to the fields of a web page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmakingloops.com%2Fpublic%2Fextract-method-slow.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmakingloops.com%2Fpublic%2Fextract-method-slow.gif" alt="Extracting methods the slow way."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Don’t feel bad if you still do it this way. It’s tried and true. I did it this way for the first &lt;strong&gt;7 years&lt;/strong&gt; of my dev career.&lt;/p&gt;

&lt;h2&gt;
  
  
  The slightly faster, mouse-clicker strategy
&lt;/h2&gt;

&lt;p&gt;The next bump in skillset comes when you realize Visual Studio has built in refactoring tools that make extracting methods &lt;em&gt;a lot&lt;/em&gt; faster.&lt;/p&gt;

&lt;p&gt;You can find the extract method shortcut three different ways: first by highlighting a block of code, then either waiting for that little yellow lightbulb (or blue screwdriver) icon to pop up and clicking it, right-clicking inside the highlighted region and selecting “Quick Actions and Refactorings”, or clicking CTRL+. to summon the suggestions manually.&lt;/p&gt;

&lt;p&gt;Below you can see me demonstrating the right-click strategy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmakingloops.com%2Fpublic%2Fextract-method-medium.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmakingloops.com%2Fpublic%2Fextract-method-medium.gif" alt="A slightly faster way to extract a method"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yep, juuuuust fast enough to let me demo &lt;em&gt;two&lt;/em&gt; method extractions instead of one!&lt;/p&gt;

&lt;p&gt;Now let’s go one final step to mastery.&lt;/p&gt;

&lt;h2&gt;
  
  
  The way of the superior developer
&lt;/h2&gt;

&lt;p&gt;The final step to take us to light-speed, boss-ass bitch mode is to map the extract method feature to a keyboard command so you can finger-flick your way to readable code in under a second.&lt;/p&gt;

&lt;p&gt;To do that, (open up VS and do it right now), go to Tools -&amp;gt; Options -&amp;gt; Environment -&amp;gt; Keyboard.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmakingloops.com%2Fpublic%2Fset-up-extract-method-shortcut.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmakingloops.com%2Fpublic%2Fset-up-extract-method-shortcut.gif" alt="Assigning extract method to a key binding"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Filter the commands by typing “extract” and selecting the &lt;em&gt;Refactor.ExtractMethod&lt;/em&gt; command.&lt;/p&gt;

&lt;p&gt;Selecting “Text Editor” in the &lt;em&gt;Use new shortcut in&lt;/em&gt; dropdown so that the command only applies when the text editor is in context.&lt;/p&gt;

&lt;p&gt;And finally, typing the key command you’d like to use and selecting the &lt;em&gt;Assign&lt;/em&gt; button and clicking &lt;em&gt;OK&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;You’ll want to choose a command that is easy for you to type quickly and comfortably (so that you remember it and use it.) I type using the Dvorak keyboard layout, so CTRL+; is the same as typing CTRL+Z on QWERTY, making it a quick flick of my pinky and ring finger.&lt;/p&gt;

&lt;p&gt;Once that’s in place, try it out! And &lt;em&gt;practice it&lt;/em&gt; so you don’t forget it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmakingloops.com%2Fpublic%2Fextract-method-fast.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmakingloops.com%2Fpublic%2Fextract-method-fast.gif" alt="The pro's way of extracting a method"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Oops, went so lightning quick I misspelled producer!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now, I have to say, it’s not wise to go this fast, but you get my point. The faster you can perform something, the more likely it is that you’ll use it.&lt;/p&gt;

&lt;p&gt;Okay, now we’re playing pro level, but how can we use this new skill most effectively?&lt;/p&gt;

&lt;h2&gt;
  
  
  What I mean when I say liberal use
&lt;/h2&gt;

&lt;p&gt;To close up, I’ll leave you with a few tips for improving the readability of your code.&lt;/p&gt;

&lt;h3&gt;
  
  
  1) Don’t be shy.
&lt;/h3&gt;

&lt;p&gt;I would almost go as far as saying you can’t have too many methods. Readability &amp;gt; method count in my book.&lt;/p&gt;

&lt;p&gt;On &lt;strong&gt;occasion&lt;/strong&gt; , I will leave a method somewhat big instead of breaking it into several smaller methods, usually because I determined that seeing all of the logic on the screen at once was more comforting and allowed better reasoning, but this is a &lt;em&gt;rare&lt;/em&gt; occasion.&lt;/p&gt;

&lt;p&gt;You can even be as bold as to convert a single line of code into a method call if the method’s name is easier to understand than the statement alone. This works especially well with multi-conditional branching statements.&lt;/p&gt;

&lt;p&gt;For example, what reads better?&lt;/p&gt;

&lt;p&gt;This?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (vehicles.Sum(v =&amp;gt; v.Price) &amp;lt; loan.AmountRequested &amp;amp;&amp;amp; Settings.IsExcessFundingAllowed)
{
    //fund excess amount...
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or This?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (IsLoanFundingExcessAmount)
{
  //...
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Methods also give you the opportunity to &lt;strong&gt;speak in the language of the business&lt;/strong&gt;. The second example is easily understandable to all parties, whereas the first example needs a few more mental clock cycles to parse and understand, even by a developer.&lt;/p&gt;

&lt;p&gt;The easier it is to understand, the easier it is to change in the future.&lt;/p&gt;

&lt;h3&gt;
  
  
  2) Pay attention to blank spaces.
&lt;/h3&gt;

&lt;p&gt;Whenever I add a blank space, it’s usually to signify a new grouping of logic, just like paragraphs in an article.&lt;/p&gt;

&lt;p&gt;Often times you can replace that grouping with a single method call that reads better than the grouping does in its raw state. The example in the animated GIFs above show this idea in action.&lt;/p&gt;

&lt;h3&gt;
  
  
  3) Create &lt;em&gt;composed methods&lt;/em&gt; as a first line of defense
&lt;/h3&gt;

&lt;p&gt;Notice in the example above how we &lt;em&gt;composed&lt;/em&gt; a large method into several smaller ones. This is called the &lt;em&gt;composed method&lt;/em&gt; refactoring, and it’s a great way to bring greater understanding to an algorithm or class. It’s &lt;em&gt;much&lt;/em&gt; easier to understand a small bundle of clearly named methods than a blob of seemingly random statements.&lt;/p&gt;

&lt;p&gt;It’s kind of like pruning a tree or picking weeds. Keep methods from growing in size by composing them into smaller ones as soon as it makes sense.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practice, practice, practice
&lt;/h2&gt;

&lt;p&gt;Alright, new skill in your pocket, now all ya gotta do is use it a few times until it that finger flick becomes muscle memory.&lt;/p&gt;

&lt;p&gt;The extract method refactoring feature is easily one of my top three favorite and most used features, so I hope it brings some value to you and your team.&lt;/p&gt;

&lt;p&gt;If you found this helpful, I’m going to be creating &lt;strong&gt;one new practical Visual Studio tip per month for the next 6 months&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It’ll be a mix of old (yet hidden) tools and brand spanking new tools, but only things I actually use &lt;em&gt;regularly&lt;/em&gt;. If you’d like to skill-up with more power tools like this one, join my list below and I’ll email them to you as they come out, along with two to three other emails per month on related helpful articles and essays.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://makingloops.com/extract-method/#mc_embed_signup" rel="noopener noreferrer"&gt;Hop over to the original article site to sign-up.&lt;/a&gt;&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>devtips</category>
      <category>codequality</category>
    </item>
    <item>
      <title>Visualizing thin ASP.NET Controllers via SOLID Principles</title>
      <dc:creator>Joe Petrakovich</dc:creator>
      <pubDate>Mon, 11 Mar 2019 00:00:00 +0000</pubDate>
      <link>https://forem.com/makingloops/visualizing-thin-aspnet-controllers-via-solid-principles-33nl</link>
      <guid>https://forem.com/makingloops/visualizing-thin-aspnet-controllers-via-solid-principles-33nl</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fn5qjmssanu75ossseym4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fn5qjmssanu75ossseym4.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can explain a code smell like &lt;em&gt;fat&lt;/em&gt; controllers until you’re blue in the face, but some things just &lt;em&gt;click&lt;/em&gt; better with pictures.&lt;/p&gt;

&lt;p&gt;And to celebrate the newly available &lt;a href="https://makingloops.com/products/thin-controller-recipes" rel="noopener noreferrer"&gt;Thin Controller Recipes&lt;/a&gt;, I’d like to examine a particularly troubling issue for many .NET developers.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“I just don’t get the point of moving business logic from controllers into services…”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If it isn’t clear why you could benefit from biz-logic-free controllers, you may be missing some software design fundamentals that could make a boatload more sense after you read this article.&lt;/p&gt;

&lt;p&gt;With that said, if the gurus are gonna preach the almighty state of &lt;em&gt;thin controller-hood&lt;/em&gt;, we first have to understand why a &lt;em&gt;fat&lt;/em&gt; controller is a code smell, what &lt;em&gt;thin&lt;/em&gt; really means, and why it benefits us.&lt;/p&gt;

&lt;p&gt;And, since thin is a &lt;em&gt;relative&lt;/em&gt; adjective, we’ll use pictures (and some animated GIFs, woo!) to convey it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fat Controllers
&lt;/h2&gt;

&lt;p&gt;Before I bust out my trusty Microsoft Pen, let’s look at some code so you can mentally map it to the pictures (and vice-versa).&lt;/p&gt;

&lt;p&gt;Below is a theoretical controller for a bank loan application. This one processes requests for loans by calculating loan terms based on a the requested dollar amount and a person’s credit score.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LoanController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Controller&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;ILoanQuoteRepository&lt;/span&gt; &lt;span class="n"&gt;_loanQuoteRepository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;LoanController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ILoanQuoteRepository&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_loanQuoteRepository&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;HttpPost&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"api/loans/quotes/"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;CreateLoanQuote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LoanQuoteRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;quote&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;LoanQuote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreditScore&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CalculateLoanTerms&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;_loanQuoteRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveQuote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;LoanQuoteResponse&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestStatus&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Decision&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InterestRate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InterestRate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, we start out with all of the logic implementing the quote creation business process inside the controller’s action method.&lt;/p&gt;

&lt;p&gt;This is fat.&lt;/p&gt;

&lt;p&gt;But it’s not fat in a vacuum, it’s fat &lt;em&gt;relative to what it could be&lt;/em&gt; if SOLID principles were applied.&lt;/p&gt;

&lt;p&gt;To be honest, the above controller is pretty tame, but the thing about business logic in the controller is, &lt;strong&gt;it encourages even more as time goes on&lt;/strong&gt;. On a pinch, you or the next developer that needs to add some feature along the same code path will stick it &lt;em&gt;riiight&lt;/em&gt; there. As will the next, and the next, and the next.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmakingloops.com%2Fpublic%2Ffat-controller.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmakingloops.com%2Fpublic%2Ffat-controller.png" alt="A fat controller"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I don’t care for UML diagrams, and these soft pastels seem to cure my winter blues…&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And just so we’re on the same page, purple represents the &lt;code&gt;Controller&lt;/code&gt; class, green represents a controller &lt;em&gt;action&lt;/em&gt; method, and the reddish-salmon color represents the logic.&lt;/p&gt;

&lt;p&gt;Pay particular attention to the green &lt;em&gt;actions&lt;/em&gt; as they are the best visual indicator of controller &lt;em&gt;thickness&lt;/em&gt; (although the existence of other &lt;em&gt;non-action&lt;/em&gt; methods in your controllers could also be a code-smell.)&lt;/p&gt;

&lt;h3&gt;
  
  
  So, why is this a code smell?
&lt;/h3&gt;

&lt;p&gt;The definition of good code seems to change with the times. What was a good strategy 10 or 20 years ago is no longer as important today.&lt;/p&gt;

&lt;p&gt;Today the focus is on &lt;em&gt;flexibility&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Our software changes &lt;em&gt;rapidly&lt;/em&gt;, so &lt;em&gt;good&lt;/em&gt; code is code that is &lt;em&gt;easy to change&lt;/em&gt; and code that is &lt;em&gt;not&lt;/em&gt; so easy to &lt;em&gt;break&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I can think of a several reasons code starts to smell around the &lt;em&gt;fatness&lt;/em&gt; of the above controller:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1) There are many reasons it may need to change with actual code edits.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The controller is now the client of the repository, the request and response DTOs, and the &lt;code&gt;LoanQuote&lt;/code&gt; domain entity, so if any of those change, it could percolate up and require the controller to change.&lt;/p&gt;

&lt;p&gt;Change isn’t bad, of course, but it &lt;em&gt;is&lt;/em&gt; risky. Can it be written in a way that is &lt;em&gt;less&lt;/em&gt; risky? More testable, for example, or easier to understand?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2) You can’t test the controller in isolation.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To test the controller, you’d also have to test the &lt;code&gt;LoanQuote&lt;/code&gt;’s &lt;code&gt;CalculateLoanTerms()&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3) The inclusion of domain logic &lt;em&gt;invites&lt;/em&gt; the addition of more locally relevant logic by you and other developers.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I touched on this earlier. Big methods encourage laziness, and thus, even bigger methods.&lt;/p&gt;

&lt;p&gt;Have you ever had to try and wrap your head around a behemoth method or a god object.&lt;/p&gt;

&lt;p&gt;It ain’t fun.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4) Code inside a controller isn’t easily reusable.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I’ve changed my stance a bit on reusability, where I used to think it was a great thing to strive for in all cases, now – not so much. But, fat controllers &lt;em&gt;do&lt;/em&gt; hint at the possibility that you’ve got logic for some process that you’ll have to write in multiple places.&lt;/p&gt;

&lt;p&gt;Since controllers sit at the barrier between the user and the domain, and often applications have more than one way of executing the same task, you risk having duplication across each entry path.&lt;/p&gt;

&lt;h3&gt;
  
  
  What can we do about it?
&lt;/h3&gt;

&lt;p&gt;This is where SOLID principles come in.&lt;/p&gt;

&lt;p&gt;SOLID principles arose as a set of ideals that we can aim for to keep code adaptive and flexible.&lt;/p&gt;

&lt;p&gt;The mantra “controllers should be thin” is actually a beckoning to abide to several SOLID principles in the context of a controller to fight against the issues I mentioned above.&lt;/p&gt;

&lt;p&gt;It should be “Yo dawg, your controllers are doing TOO MUCH!”&lt;/p&gt;

&lt;p&gt;In this case, we can benefit from the &lt;strong&gt;S&lt;/strong&gt; , the &lt;strong&gt;O&lt;/strong&gt; , and the &lt;strong&gt;D&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thin Controllers via The Single Responsibility Principle
&lt;/h2&gt;

&lt;p&gt;The Single Responsibility Principle (SRP) states that components should ideally only have one reason to change.&lt;/p&gt;

&lt;p&gt;By &lt;em&gt;shrinking&lt;/em&gt; the scope of an object’s duty, you compartmentalize behavior so that &lt;strong&gt;it is easier to reason about&lt;/strong&gt; (making future changes easier), and you make it &lt;strong&gt;more stable&lt;/strong&gt; because it shouldn’t need to be modified unless there is a feature or bug that involves that single responsibility.&lt;/p&gt;

&lt;p&gt;When you have a controller with multiple responsibilities like in our example (mapping requests, responses, handling the quote entity and the repository), you have a large potential change surface.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmakingloops.com%2Fpublic%2Ffat-controller-changes.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmakingloops.com%2Fpublic%2Ffat-controller-changes.gif" alt="A fat controller with changes animated"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’ve used flashing red and green blocks to indicate areas of change. Green being code additions and red being existing code modifications or deletions.&lt;/p&gt;

&lt;p&gt;How can we improve upon this design so that it isn’t so inviting to entropy?&lt;/p&gt;

&lt;h2&gt;
  
  
  Shrinking the controller’s change surface with application services
&lt;/h2&gt;

&lt;p&gt;One immediate strategy is to simply move the logic that orchestrates the work between the DTOs and the &lt;code&gt;LoanQuote&lt;/code&gt; into what is typically called an &lt;em&gt;application service&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;An application service can then be assigned the single responsibility of that task’s orchestration, &lt;strong&gt;leaving the controller to be a simple gateway between the HTTP request the service method&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmakingloops.com%2Fpublic%2Fapp-service.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmakingloops.com%2Fpublic%2Fapp-service.png" alt="A controller making use of an application service"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You’ll notice a few things:&lt;/p&gt;

&lt;p&gt;1) The controller (via the green action) is visibly &lt;em&gt;thinner&lt;/em&gt; by a applying a better separation of concerns. That means the controller itself will be easier to reason about, and it won’t need to change as often.&lt;/p&gt;

&lt;p&gt;2) We now have two objects instead of one. That does increase complexity, so there is a trade-off.&lt;/p&gt;

&lt;p&gt;3) We’ve merely shoveled the girth of the controller somewhere else. Later you’ll see how application services can suffer the same problem of &lt;em&gt;bloat&lt;/em&gt; as our controllers.&lt;/p&gt;

&lt;p&gt;In code, we’ll create a service class that looks a lot like the original controller.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LoanService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;ILoanQuoteRepository&lt;/span&gt; &lt;span class="n"&gt;_loanQuoteRepository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;LoanService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ILoanQuoteRepository&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_loanQuoteRepository&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;LoanQuoteResponse&lt;/span&gt; &lt;span class="nf"&gt;CreateQuote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;creditScore&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;quote&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;LoanQuote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;creditScore&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CalculateLoanTerms&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;_loanQuoteRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveQuote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;LoanQuoteResponse&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestStatus&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Decision&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InterestRate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InterestRate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then shrink the controller down by merely delegating its old work to the new service.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LoanController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Controller&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;LoanService&lt;/span&gt; &lt;span class="n"&gt;_loanService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;LoanController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LoanService&lt;/span&gt; &lt;span class="n"&gt;loanService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_loanService&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;loanService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;HttpPost&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"api/loans/quotes/"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;CreateLoanQuote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LoanQuoteRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;loanQuoteResponse&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_loanService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateQuote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreditScore&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, this is better, but can we add some &lt;em&gt;flex&lt;/em&gt; to this design?&lt;/p&gt;

&lt;h2&gt;
  
  
  Programming &lt;em&gt;to&lt;/em&gt; interfaces to support extension and further reduce the change surface
&lt;/h2&gt;

&lt;p&gt;When creating application services, it is my preference to hide the implementation behind an interface to allow for easier testing, for extension via decorators, and to make my intention of loose coupling as clear as possible.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmakingloops.com%2Fpublic%2Fapp-service-interface.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmakingloops.com%2Fpublic%2Fapp-service-interface.png" alt="An application service interface"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I started to animate the implementations overlaying the interface but it ended up looking like a whole lotta controller debauchery…&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;An interesting observation about this design is that, provided the interface’s method signature stays the same, your change surface is reduced to the injection point. In theory you’d only ever have to add new implementations or extensions rather than modify old ones.&lt;/p&gt;

&lt;p&gt;I can’t say if it plays out that way very often in the real world, but it is a good example of the Open/Closed SOLID Principle.&lt;/p&gt;

&lt;p&gt;Your controller can now stay unchanged, while your application service can be extended via decorators or simply swapped out entirely, all managed within your dependency injection container’s configuration site.&lt;/p&gt;

&lt;p&gt;The interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;ILoanService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;LoanQuoteResponse&lt;/span&gt; &lt;span class="nf"&gt;CreateQuote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;creditScore&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The service simply implements the interface.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LoanService&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ILoanService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the controller specifies the interface for injection instead of the concrete implementation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LoanController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Controller&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;ILoanService&lt;/span&gt; &lt;span class="n"&gt;_loanService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;LoanController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ILoanService&lt;/span&gt; &lt;span class="n"&gt;loanService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_loanService&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;loanService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A drawback is that you do add complexity via more &lt;em&gt;things&lt;/em&gt; to manage by creating an interface that only has one implementation.&lt;/p&gt;

&lt;p&gt;A popular software engineering tenet is to program to abstractions, not implementations, but if we blindly put everything behind an interface, are we always better off? Some argue that if you find you &lt;em&gt;do&lt;/em&gt; need the benefits of an interface, than you can always create one &lt;em&gt;at that point in time&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I think that’s fair, but I sometimes will still work behind one to make my intent clear.&lt;/p&gt;

&lt;p&gt;Okay, now that we have our task delegated behind an interface, we’ve got a much more flexible and modular design. The class responsibilities are better defined, and we’ve provided for extension points and we’ve decoupled the service implementation from the controller.&lt;/p&gt;

&lt;p&gt;Is this all good, or can it sour?&lt;/p&gt;

&lt;h2&gt;
  
  
  Bloated application services
&lt;/h2&gt;

&lt;p&gt;I mentioned above that application services can also bloat.&lt;/p&gt;

&lt;p&gt;Instead of fat controllers, we get fat application layers.&lt;/p&gt;

&lt;p&gt;I’d say the latter is better than the former simply because the responsibilities are clearer, but this outcome is most likely a result of the application service doing too many things.&lt;/p&gt;

&lt;p&gt;The reality of controllers is usually far different than the simple single action controller that I showed at the start. We usually have at least a few actions, and each has potential to bloat.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmakingloops.com%2Fpublic%2Fmulti-action-fat-controller.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmakingloops.com%2Fpublic%2Fmulti-action-fat-controller.png" alt="A controller with multiple fat actions"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Remember, the size of the green actions are the indication of fat. Disregard the purple controller class wrapping.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As you can &lt;em&gt;see&lt;/em&gt;, once you’ve got a nice cozy home for that original orchestration logic, the tendency is to throw everything in there to satisfy &lt;em&gt;all&lt;/em&gt; the work your controller needs to execute.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LoanController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Controller&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;ILoanService&lt;/span&gt; &lt;span class="n"&gt;_loanService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;LoanController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ILoanService&lt;/span&gt; &lt;span class="n"&gt;loanService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_loanService&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;loanService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;HttpGet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"api/loans/quotes/{id}"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;GetQuote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;quote&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_loanService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetExistingQuote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;HttpPost&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"api/loans/quotes/"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;CreateLoanQuote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LoanQuoteRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;loanQuoteResponse&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_loanService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateQuote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreditScore&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;HttpPost&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"api/loans/quotes/{id}"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;DeleteQuote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_loanService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;DeleteQuote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;NoContent&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the application service, handling it all:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LoanService&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ILoanService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;ILoanQuoteRepository&lt;/span&gt; &lt;span class="n"&gt;_loanQuoteRepository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;LoanService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ILoanQuoteRepository&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_loanQuoteRepository&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;LoanQuoteResponse&lt;/span&gt; &lt;span class="nf"&gt;GetExistingQuote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;quoteId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;quote&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_loanQuoteRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetByID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quoteId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;LoanQuoteResponse&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestStatus&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Decision&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InterestRate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InterestRate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;LoanQuoteResponse&lt;/span&gt; &lt;span class="nf"&gt;CreateQuote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;creditScore&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;quote&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;LoanQuote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;creditScore&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CalculateLoanTerms&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;_loanQuoteRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveQuote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;LoanQuoteResponse&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestStatus&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Decision&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InterestRate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InterestRate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;DeleteQuote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;quoteId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_loanQuoteRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quoteId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When your application service has this many responsibilities, the benefits of putting it behind an interface are surely reduced.&lt;/p&gt;

&lt;p&gt;Rarely would you decorate a single method on something that big, and swapping it out would be pretty unlikely too, as you’d need a reason to re-implement every method.&lt;/p&gt;

&lt;p&gt;What can we do about this?&lt;/p&gt;

&lt;h2&gt;
  
  
  Breaking up fat application services via CQRS
&lt;/h2&gt;

&lt;p&gt;A good solution to these fat service layers is to again apply the single responsibility principle and break them up into several smaller, single purpose classes.&lt;/p&gt;

&lt;p&gt;A popular strategy that follows from that is &lt;em&gt;command query responsibility segregation&lt;/em&gt; (CQRS).&lt;/p&gt;

&lt;p&gt;Simplest way to explain it is that logic related to querying (database &lt;code&gt;SELECT&lt;/code&gt;’s, really) is separated from logic that modifies (database &lt;code&gt;UPDATE&lt;/code&gt;’s, &lt;code&gt;INSERT&lt;/code&gt;’s, and &lt;code&gt;DELETE&lt;/code&gt;’s)&lt;/p&gt;

&lt;p&gt;Consider how most standard language APIs differentiate between the &lt;code&gt;Reader&lt;/code&gt;’s and &lt;code&gt;Writer&lt;/code&gt;’s when it comes to I/O. By keeping these tasks separate they are easier to reason about and modify and they won’t contain code that interferes with the other.&lt;/p&gt;

&lt;p&gt;A very common trend I am seeing with CQRS is to use Jimmy Bogard’s Mediatr library and create small query and command classes that are propagated through an execution pipeline.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://lostechies.com/jimmybogard/2015/05/05/cqrs-with-mediatr-and-automapper/" rel="noopener noreferrer"&gt;This article&lt;/a&gt; from Jimmy himself shows that technique.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I’ve yet to get my hands dirty with Mediatr, so I will instead discuss a simplified way of implementing CQRS.&lt;/p&gt;

&lt;p&gt;Keeping CQRS simple just means keeping the query and command codepaths separate, and in relation to fixing our bloated application services, we can create separate classes for each query or command and inject them into the controller.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I also really liked &lt;a href="https://codeopinion.com/is-cqrs-complicated/" rel="noopener noreferrer"&gt;Derek Comartin’s article&lt;/a&gt; on this.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmakingloops.com%2Fpublic%2Fcqrs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmakingloops.com%2Fpublic%2Fcqrs.png" alt="Applying CQRS"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Awww, they’re like cute little application service minis!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This aligns nicely with the standard HTTP verbs that controllers are responsible for.&lt;/p&gt;

&lt;p&gt;Actions that handle commands typically work with HTTP POST, PATCH, DELETE, PUT (and maybe some obscure ones I can’t remember) and queries tend to be your GETs.&lt;/p&gt;

&lt;p&gt;Here is a very basic code example of these kind of single purpose command and query classes.&lt;/p&gt;

&lt;p&gt;First, one for the query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;QuoteQuery&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IQuoteQuery&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;ILoanQuoteRepository&lt;/span&gt; &lt;span class="n"&gt;_loanQuoteRepository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;QuoteQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ILoanQuoteRepository&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_loanQuoteRepository&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;quoteId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;quote&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_loanQuoteRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetByID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quoteId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;LoanQuoteResponse&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestStatus&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Decision&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InterestRate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InterestRate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One for the quote creation command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreateQuoteCommand&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ICreateQuoteCommand&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;ILoanQuoteRepository&lt;/span&gt; &lt;span class="n"&gt;_loanQuoteRepository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;CreateQuoteCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ILoanQuoteRepository&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_loanQuoteRepository&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;creditScore&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;quote&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;LoanQuote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;creditScore&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CalculateLoanTerms&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;_loanQuoteRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveQuote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;LoanQuoteResponse&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestStatus&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Decision&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InterestRate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InterestRate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And one for the delete command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DeleteQuoteCommand&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IDeleteQuoteCommand&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;ILoanQuoteRepository&lt;/span&gt; &lt;span class="n"&gt;_loanQuoteRepository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;DeleteQuoteCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ILoanQuoteRepository&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_loanQuoteRepository&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;quoteId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_loanQuoteRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quoteId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And to use them, we inject them into the controller and call the execute method upon the relevant object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LoanController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Controller&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;IQuoteQuery&lt;/span&gt; &lt;span class="n"&gt;_getQuote&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;ICreateQuoteCommand&lt;/span&gt; &lt;span class="n"&gt;_createQuote&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;IDeleteQuoteCommand&lt;/span&gt; &lt;span class="n"&gt;_deleteQuote&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;LoanController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IQuoteQuery&lt;/span&gt; &lt;span class="n"&gt;quoteQuery&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ICreateQuoteCommand&lt;/span&gt; &lt;span class="n"&gt;createQuote&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IDeleteQuoteCommand&lt;/span&gt; &lt;span class="n"&gt;deleteQuote&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_quoteQuery&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;quoteQuery&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_createQuote&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;createQuote&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_deleteQuote&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;deleteQuote&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;HttpGet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"api/loans/quotes/{id}"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;GetQuote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;quote&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_quoteQuery&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;HttpPost&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"api/loans/quotes/"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;CreateLoanQuote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LoanQuoteRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;loanQuoteResponse&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_createQuote&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreditScore&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;HttpPost&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"api/loans/quotes/{id}"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;DeleteQuote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_deleteQuote&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;NoContent&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Now we have a nice visible design evolution that shows the progressive &lt;em&gt;thinning&lt;/em&gt; of a fat controller.&lt;/p&gt;

&lt;p&gt;By applying the single responsibility principle to each area of bloat, we break up the complexity into smaller, easier to manage classes that can change in isolation.&lt;/p&gt;

&lt;p&gt;We reduced the change surface to compartmentalized units that will ideally lead to a stabler codebase.&lt;/p&gt;

&lt;p&gt;One where changes aren’t as risky and can be made with comparative ease.&lt;/p&gt;

&lt;p&gt;If you’d like to go deep on applying the single responsibility to other controller tasks, you can &lt;a href="https://makingloops.com/visualizing-thin-controllers/#mc_embed_signup" rel="noopener noreferrer"&gt;hop on over to the original post&lt;/a&gt;, get on my personal list at the bottom and I’ll send you the &lt;em&gt;Mapping DTOs&lt;/em&gt; recipe from the Thin Controller Recipe Set.  &lt;/p&gt;

</description>
      <category>csharp</category>
      <category>aspnetcore</category>
    </item>
    <item>
      <title>6 types of code you shouldn't have inside your .NET controllers</title>
      <dc:creator>Joe Petrakovich</dc:creator>
      <pubDate>Sat, 16 Feb 2019 00:00:00 +0000</pubDate>
      <link>https://forem.com/makingloops/6-types-of-code-you-shouldnt-have-inside-your-net-controllers-3gml</link>
      <guid>https://forem.com/makingloops/6-types-of-code-you-shouldnt-have-inside-your-net-controllers-3gml</guid>
      <description>&lt;p&gt;“Your .NET controllers should be thin”&lt;/p&gt;

&lt;p&gt;The ever-repeated platitude with 3 metric tons of baggage to unpack.&lt;/p&gt;

&lt;p&gt;Why should they be thin? How does that benefit you? What steps can I take to get them thin if they aren’t already? How do I &lt;em&gt;keep&lt;/em&gt; them that way?&lt;/p&gt;

&lt;p&gt;All valid (and common) follow-up questions. I’ve discussed &lt;em&gt;part&lt;/em&gt; of the &lt;em&gt;why&lt;/em&gt; in &lt;a href="https://makingloops.com/why-should-controllers-be-thin-addendum/"&gt;some earlier articles&lt;/a&gt;, so in this article we are going to take another angle.&lt;/p&gt;

&lt;p&gt;To begin the process of identifying the steps needed to thin out controllers, &lt;strong&gt;we need to understand some of the common ways they can become fat.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In my experience, I’ve found &lt;strong&gt;6 common types of code that sneak into our controllers&lt;/strong&gt; that ultimately would be better suited &lt;em&gt;elsewhere&lt;/em&gt;. This list is not comprehensive though, as I’m sure there are even more.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Mapping Data Transfer Objects (DTOs)
&lt;/h2&gt;

&lt;p&gt;Because our controllers are on the figurative &lt;em&gt;front lines&lt;/em&gt; of the request pipeline, there is often a need to create request and response objects if the requirements of the request are more complicated than simply using URL arguments and HTTP status code responses.&lt;/p&gt;

&lt;p&gt;Any time you’ve got DTOs, you’ve also got a need to map the values they hold to something more functional like a domain entity.&lt;/p&gt;

&lt;p&gt;You know, this kind of stuff:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public IActionResult CheckOutBook([FromBody]BookRequest bookRequest)
{
    var book = new Book();

    book.Title = bookRequest.Title;
    book.Rating = bookRequest.Rating.ToString();
    book.AuthorID = bookRequest.AuthorID;

    //...
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This &lt;em&gt;mapping&lt;/em&gt; logic is innocent enough, but it quickly bloats the controller and adds an additional responsibility. Ideally, our controller’s single responsibility is only to delegate the next action at the level of the HTTP request.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Validation
&lt;/h2&gt;

&lt;p&gt;Obviously we can’t have bad input making its way into the inner castle walls of our domain. Validation protects us from that, usually first on the client, but then again on the server.&lt;/p&gt;

&lt;p&gt;I like to treat controllers sort of like &lt;em&gt;head chefs&lt;/em&gt;. They have assistants that prepare all the ingredients for them, so they can work their magic on the final plating. There are plenty of ways you can set up validators along the request pipeline in ASP.NET MVC so that &lt;strong&gt;the controller can assume the request is valid and delegate the next action&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This type of code is unexcusable!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public IActionResult Register([FromBody]AutomobileRegistrationRequest request)
{
    // //validating that a VIN number was provided...
    if (string.IsNullOrEmpty(request.VIN))
    {
        return BadRequest();
    }

    //...
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Rubbish! &lt;em&gt;(spoken in a Gordon Ramsay voice)&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Business Logic
&lt;/h2&gt;

&lt;p&gt;If you’ve got anything business-related in the controller, you will likely have to write it again elsewhere.&lt;/p&gt;

&lt;p&gt;Sometimes there is overlap with validation too. If your validation logic has rules that a business person would be deciding (rather than simple things like number ranges or things a string can &lt;em&gt;be&lt;/em&gt;), you run the risk of having to repeat that code.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Authorization
&lt;/h2&gt;

&lt;p&gt;Authorization is similar to validation in that it is a protective barrier. Rather than bad requests making it into the domain, authorization prevents bad users from getting somewhere they are not allowed.&lt;/p&gt;

&lt;p&gt;Also like validation, ASP.NET offers &lt;em&gt;many&lt;/em&gt; ways to compartmentalize authorization (middleware and filters, for example.)&lt;/p&gt;

&lt;p&gt;If you’re checking properties on your &lt;code&gt;User&lt;/code&gt; within the controller action to grant/block something, you miiight have some refactoring to do.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Error Handling
&lt;/h2&gt;

&lt;p&gt;It burns, it BURNS!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public IActionResult GetBookById(int id)
{
    try
    {
      //the important stuff that a head chef would do...
    }
    catch (DoesNotExistException)
    {
      //stuff an assistant should be doing...
    }
    catch (Exception e)
    {
      //please, no...
    }
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This one is quite broad, and sometimes handling exceptions must be done in the controller, but I’ve found there is almost always a better, more localized place. And if there isn’t, you can take advantage of &lt;strong&gt;global exception handling middleware&lt;/strong&gt; to catch the most common errors and return something consistent to your clients.&lt;/p&gt;

&lt;p&gt;Going back to the head chef metaphor, I like my head chef not to have to worry about that kind of thing. Let them do their single responsibility and assume someone else will handle anything unexpected.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Data Storage/Retrieval
&lt;/h2&gt;

&lt;p&gt;Work like getting or saving entities using a &lt;code&gt;Repository&lt;/code&gt; often ends up in the controller to save time. If the controller’s are just CRUD endpoints, then hey, why the hell not.&lt;/p&gt;

&lt;p&gt;I even have an older article that shows controllers with this very trait.&lt;/p&gt;

&lt;p&gt;Rather than just calling this a bad behavior, I think pointing out an alternative way will highlight why this can bloat your controller’s unneccesarily.&lt;/p&gt;

&lt;p&gt;First, looking at it from a design perspective (with a focus on the Single Responsibility Principle), if you have objects that are designed for persistence being used by your controllers, your controllers are then have more than one reason to change.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public IActionResult CheckOutBook(BookRequest request)
{
    var book = _bookRepository.GetBookByTitleAndAuthor(request.Title, request.Author);

    // if you've got the above statement already, you'll be
    // tempted to add the book checkout biz logic here...
    //...

    return Ok(book);
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Beyond basic CRUD, persistence logic in the controller is a code smell for additional sprouts of logic that would be better suited deeper down the chain.&lt;/p&gt;

&lt;p&gt;This is where I like to use either some kind of application service (behind an interface) to handle the work, or delegate to some kind of CQRS command/query object.&lt;/p&gt;

&lt;h3&gt;
  
  
  That’s it!
&lt;/h3&gt;

&lt;p&gt;Can you think of any more types?&lt;/p&gt;

&lt;p&gt;If you’d like to go deeper, I’ve put together a set of &lt;em&gt;recipes&lt;/em&gt; with code samples and an exercise specifically for &lt;strong&gt;making your controllers thin&lt;/strong&gt;. It is not only the &lt;em&gt;how&lt;/em&gt;, but provides a deep exploration of the &lt;em&gt;why&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;You can get &lt;strong&gt;one&lt;/strong&gt; of the recipes &lt;a href="https://makingloops.com/fat-controller-causes/"&gt;at the bottom of the original article&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>aspnetcore</category>
    </item>
    <item>
      <title>Do you have weak developer boundaries?</title>
      <dc:creator>Joe Petrakovich</dc:creator>
      <pubDate>Fri, 25 Jan 2019 00:00:00 +0000</pubDate>
      <link>https://forem.com/makingloops/do-you-have-weak-developer-boundaries-42je</link>
      <guid>https://forem.com/makingloops/do-you-have-weak-developer-boundaries-42je</guid>
      <description>&lt;p&gt;Are you feeling totally and completely drained after work?&lt;/p&gt;

&lt;p&gt;Unable to work on the projects or hobbies you used to find fun?&lt;/p&gt;

&lt;p&gt;Is the thought of opening a text editor after the clock strikes five a laughable matter?&lt;/p&gt;

&lt;p&gt;Well guess what?&lt;/p&gt;

&lt;p&gt;Your boundaries probably SUCK!&lt;/p&gt;

&lt;p&gt;Like cheap swiss cheese. And let me tell you why…&lt;/p&gt;

&lt;p&gt;I’ve been doing a lot of self-work on personal boundaries lately.&lt;/p&gt;

&lt;p&gt;I grew up in a home where setting boundaries were an act of treason that in my dependent eyes, would result in abandonment, or worse – death.&lt;/p&gt;

&lt;p&gt;And because I couldn’t rely on any ounce of consistency, my strategy was instead to become utterly self-reliant. To the point where I was proud that “I’ll do it all on my own” and “never ask for help”.&lt;/p&gt;

&lt;p&gt;Does this remind you of anyone?&lt;/p&gt;

&lt;p&gt;When you combine permeable boundaries with a fixer-giver-hero complex, you get someone who says yes too much and invariably burns themselves out.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does this show itself in our everyday life as developers?
&lt;/h2&gt;

&lt;p&gt;Boundaries are everywhere.&lt;/p&gt;

&lt;p&gt;Every thought you have or action you take or don’t take is a boundary line being set or broken.&lt;/p&gt;

&lt;p&gt;An analogy I like is the maintenance of a ship whilst sailing across the ocean.&lt;/p&gt;

&lt;p&gt;If the ship is not properly cared for, boards begin to rot, and your attention must be put on trivial distractions instead of the great path forward.&lt;/p&gt;

&lt;p&gt;These are some of the ways we let our boundaries slip:&lt;/p&gt;

&lt;p&gt;When we wake up and immediately grab our phones and become reactive to the onslaught of texts or social media outrage, we are sanctioning that mental state as the frame of our day.&lt;/p&gt;

&lt;p&gt;When we leave slack notifications on while writing code, or worse – have the slack window open on one of our monitors.&lt;/p&gt;

&lt;p&gt;When we let the loud minority of customers who complain direct our development, constantly putting out fires instead of focusing on the customers that matter most.&lt;/p&gt;

&lt;p&gt;When we lower the bar for the quality of code we check-in. Increasing the number of times we say “I’ll clean it up later”.&lt;/p&gt;

&lt;p&gt;When we don’t write tests when we definitely had the opportunity to.&lt;/p&gt;

&lt;p&gt;When we accept a deadline without any push-back, despite knowing that it could result in a poor, rushed implementation.&lt;/p&gt;

&lt;p&gt;Over-volunteering to complete tasks when you’ve already got a full plate.&lt;/p&gt;

&lt;p&gt;I could go on for days.&lt;/p&gt;

&lt;p&gt;This is just a tiny list, there are &lt;em&gt;so&lt;/em&gt; many other things, micro and macro, that we do to compromise ourselves.&lt;/p&gt;

&lt;p&gt;So – what can we do about it?&lt;/p&gt;

&lt;p&gt;What can we do to start putting ourselves first and getting back the freedom to do more of what we love?&lt;/p&gt;

&lt;h2&gt;
  
  
  How can we begin to correct the course?
&lt;/h2&gt;

&lt;p&gt;Just as the problem of weak boundaries shows itself as a series of tiny, day to day compromises, the solution is to apply discipline and diligence in the other direction.&lt;/p&gt;

&lt;p&gt;First, understand that this selflessness (aka codependency) is not getting you anywhere.&lt;/p&gt;

&lt;p&gt;People do not respect (and are suspicious of) those who say yes to everything and let their build up of stress leak out as passive aggression and resentment.&lt;/p&gt;

&lt;p&gt;Your work starts to suffer, you stop doing things you love, you burn out and all you can do when you come home is veg out on television and cheap food (which keeps the cycle going).&lt;/p&gt;

&lt;p&gt;Next, begin to apply tiny acts of discipline to your day that promote self-respect and really, self-love.&lt;/p&gt;

&lt;p&gt;You’re going to stop compromising and start being kind to YOU.&lt;/p&gt;

&lt;p&gt;Begin to identify loose ends. The areas where you accept sloppiness in yourself and others and draw a line in the sand.&lt;/p&gt;

&lt;p&gt;Frame your day on the right foot by making your bed (a tiny &lt;em&gt;win&lt;/em&gt;) instead of looking at your phone.&lt;/p&gt;

&lt;p&gt;When you’re writing code, keep slack notifications off. Give your team what they need to reach you in an emergency and &lt;em&gt;focus&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Focus your product’s features and support on the customers that champion what you do for them rather than the vocal minority of complainers.&lt;/p&gt;

&lt;p&gt;Choose to slow down and not check-in code that you know could be better.&lt;/p&gt;

&lt;p&gt;Be willing to say no to certain requests for your time when you can feel in your body that you’ve going to crash.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://makingloops.com/relax/"&gt;Implement end-of-day rituals to give your mind a real break.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Be willing to say no to features that would blur the core focus of your application.&lt;/p&gt;

&lt;p&gt;Don’t be afraid to speak your concerns about a deadline being to soon.&lt;/p&gt;

&lt;p&gt;Unsubscribe from newsletters that don’t empower your mission.&lt;/p&gt;

&lt;p&gt;Unfollow “thought leaders” that bring needless drama and outrage to your feeds.&lt;/p&gt;

&lt;p&gt;Be willing to give your teammates the opportunity to learn and handle a task without you instead of volunteering or trying to get your hands in everything.&lt;/p&gt;

&lt;p&gt;Choose to ignore the latest hot framework and instead master your current tools.&lt;/p&gt;

&lt;p&gt;Batten down the hatches and sturdy the ship so you and your team are empowered to focus on true, value adding work that empowers others.&lt;/p&gt;

&lt;p&gt;So that when the day is done, you are not half brain-dead from all the fires you’ve been putting out.&lt;/p&gt;

&lt;p&gt;As you begin to implement these changes slowly over time, you’ll notice a new energy and freedom inside you.&lt;/p&gt;

&lt;p&gt;You’ll feel invigorated and capable.&lt;/p&gt;

&lt;p&gt;Your team will respect your time and trust that they can count on you.&lt;/p&gt;

&lt;p&gt;Your own physical health (and your codebase) will begin to improve.&lt;/p&gt;

&lt;p&gt;You’ll come home, take some time for yourself, and then be inspired to work on a personal project or hobby, or spend time with a loved one without feeling exhausted.&lt;/p&gt;

&lt;p&gt;Boundaries are everywhere.&lt;/p&gt;

&lt;p&gt;What are you allowing in?&lt;/p&gt;

&lt;h2&gt;
  
  
  Ready to go deeper?
&lt;/h2&gt;

&lt;p&gt;If this article resonated with you, you are probably the kind of person who recognizes the potential for greatness in yourself.&lt;/p&gt;

&lt;p&gt;If that’s the case, I’d like to offer you an additional resource to expound upon this idea. I’ve compiled my top tools, tips, and books on building stronger boundaries.&lt;/p&gt;

&lt;p&gt;These are resources that are dear to me that I have shared with my closest friends and family. I’ve also added summaries so you can decide whether or not it will be of value to you.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://makingloops.com/boundaries/#ready-to-go-deeper"&gt;Click to jump on over to the original article location to grab it.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;/end&lt;/p&gt;

</description>
      <category>career</category>
      <category>motivation</category>
      <category>discuss</category>
    </item>
    <item>
      <title>How to stop thinking about code after work</title>
      <dc:creator>Joe Petrakovich</dc:creator>
      <pubDate>Tue, 11 Dec 2018 01:50:35 +0000</pubDate>
      <link>https://forem.com/makingloops/how-to-stop-thinking-about-code-after-work-3ke2</link>
      <guid>https://forem.com/makingloops/how-to-stop-thinking-about-code-after-work-3ke2</guid>
      <description>&lt;p&gt;I decided to do a video and written version of the same content.  &lt;/p&gt;

&lt;p&gt;The video is just me free-form speaking about it.  While written is more structured.  &lt;/p&gt;

&lt;p&gt;The written can be found &lt;a href="https://makingloops.com/relax/"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>career</category>
    </item>
  </channel>
</rss>
