<?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: klobermajer</title>
    <description>The latest articles on Forem by klobermajer (@klobermajer).</description>
    <link>https://forem.com/klobermajer</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%2F835410%2Ffacfffa2-ebd9-4472-ab45-89a84e8f4fb9.jpeg</url>
      <title>Forem: klobermajer</title>
      <link>https://forem.com/klobermajer</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/klobermajer"/>
    <language>en</language>
    <item>
      <title>Legacy code - strangle or tame?</title>
      <dc:creator>klobermajer</dc:creator>
      <pubDate>Mon, 18 Apr 2022 07:51:11 +0000</pubDate>
      <link>https://forem.com/accesto/legacy-code-strangle-or-tame-691</link>
      <guid>https://forem.com/accesto/legacy-code-strangle-or-tame-691</guid>
      <description>&lt;p&gt;For me, as probably for every programmer, &lt;strong&gt;legacy code is something that I do not want to deal with&lt;/strong&gt;. It is always not well written, hard to read, and very complicated. &lt;/p&gt;

&lt;p&gt;Unfortunately, the world is not a perfect place and we must do it on a daily basis. The first thing we want to do when we are starting to work with a new legacy project is to &lt;strong&gt;rewrite everything from scratch&lt;/strong&gt;. Such an approach in most cases will not work too well, it will take much time to deliver, it will stop introducing new features for a long time and do not bring any value to customers during this period.&lt;/p&gt;

&lt;p&gt;A better approach is to rewrite step by step, one module at a time. Let legacy live with a new codebase, arm in arm. It can be achieved by using a &lt;a href="https://docs.microsoft.com/en-us/azure/architecture/patterns/strangler" rel="nofollow"&gt;strangler pattern&lt;/a&gt;. Unfortunately, such an approach is also not always possible. Sooner or later you will face the situations when the &lt;strong&gt;requested feature will touch almost every part of the system&lt;/strong&gt; or there will be no part left that can be rewritten at once in a reasonable time.&lt;/p&gt;

&lt;h2&gt;
  
  
  From strangling to taming
&lt;/h2&gt;

&lt;p&gt;Let me tell you a story about one of our projects which we started two years ago. It is a task management software with a lot of features and integrations with external systems. This software was developed a few years before we took it. It was used by many customers back then and nowadays it is getting more popular from month to month. Our main goal at the beginning was to &lt;strong&gt;make it faster, more robust, and more scalable&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We started as always by collecting as much &lt;strong&gt;knowledge about the domain&lt;/strong&gt; as we can, then extracted some modules as refactoring candidates and get into coding. Refactoring a few modules took us almost a year. Almost a year with no big visible changes for the users.&lt;/p&gt;

&lt;p&gt;The big disadvantage of &lt;strong&gt;refactoring legacy code&lt;/strong&gt; is that it has no visible value for customers, even if it is a huge improvement for us, developers. Our hard work at this stage will pay off in the future for sure, but we should also give some &lt;strong&gt;value to users&lt;/strong&gt; during the whole process, not only at the end.&lt;/p&gt;

&lt;p&gt;This is why it is so important to properly design new codebase architecture and use possibilities it gives to &lt;strong&gt;parallelly refactor and introduce new features&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qRFGqt9L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8294ie17gfzcuekjlmqt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qRFGqt9L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8294ie17gfzcuekjlmqt.png" alt="Image description" width="547" height="180"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  New architecture
&lt;/h2&gt;

&lt;p&gt;Architecture should be always adjusted to project requirements, each project has specific characteristics and requirements. In our case, because of external systems integrations, and more integrations on the horizon, events were a very important part that we wanted to introduce. Based on that we decided to go with CQRS.&lt;/p&gt;

&lt;h4&gt;
  
  
  CQRS - Command Query Responsibility Segregation
&lt;/h4&gt;

&lt;p&gt;Probably you heard about this more than once. It is known for some time and a commonly used approach. It allows us to separate responsibilities, to separate domain, and make code clear and easily extensible. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Base principles:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;each change must be done by Command&lt;/li&gt;
&lt;li&gt;each Command has one Handler&lt;/li&gt;
&lt;li&gt;each change can dispatch Event&lt;/li&gt;
&lt;li&gt;each Event can have multiple Handlers&lt;/li&gt;
&lt;li&gt;fetching data from the database is performed by Query&lt;/li&gt;
&lt;li&gt;each Query has one Handler&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Taming
&lt;/h2&gt;

&lt;p&gt;At some point in time, we decided to &lt;strong&gt;reduce refactoring activities and focus more on introducing new features&lt;/strong&gt;, to finally make something visible to the customers. And so we did. We introduced a couple of new features in already refactored parts. Unfortunately, quite soon, we hit a wall. It was no longer possible to &lt;strong&gt;add something without touching legacy code&lt;/strong&gt;, and such refactoring will always require too much time and it will probably end up with refactoring the whole system. Too many parts are connected.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use events
&lt;/h3&gt;

&lt;p&gt;Is the refactoring always really needed? Maybe we can &lt;strong&gt;add new features without refactoring?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One of the features that we wanted to implement was the notifications module. It is obvious that notifications are connected with many modules. And it is also obvious that from a business perspective it is &lt;strong&gt;not acceptable to refactor all these modules just because of notifications&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Luckily it is not required, we can &lt;strong&gt;prepare domain events for legacy code&lt;/strong&gt; and dispatch such events when needed. The actual job will be done by a new code in event handlers.&lt;/p&gt;

&lt;h4&gt;
  
  
  Example
&lt;/h4&gt;

&lt;p&gt;In this example, I will show you how we can notify the user when he was assigned to a task in our system.&lt;/p&gt;

&lt;p&gt;Let’s start with the code currently responsible for assigning users to the task.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Legacy\Controller&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TaskController&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;function&lt;/span&gt; &lt;span class="n"&gt;assignUsers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$taskId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nv"&gt;$users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'users'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="nv"&gt;$taskHelper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'taskhelper'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

       &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;taskExists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$taskId&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'NOK'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;

       &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$users&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="nv"&gt;$usersIds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;explode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;','&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$users&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

           &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$usersIds&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
               &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;userIsAssignedToTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$taskId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                   &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
               &lt;span class="p"&gt;}&lt;/span&gt;

               &lt;span class="nv"&gt;$taskHelper&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assignUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$taskId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$id&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="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Legacy\Service&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TaskHelper&lt;/span&gt; 
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$repository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


    &lt;span class="c1"&gt;// … &lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;assignUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$taskId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$userId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assignUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$taskId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$userId&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;This is just an example code, but you can get the idea of what kind of legacy we are dealing with. Of course, we can inject some service from the new codebase to TaskHelper and use it to create a notification. Such a solution will work but will be less extensible. The solution which I recommend here is to &lt;strong&gt;dispatch a domain event and let the handler do the job&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Task\Domain\Event&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserWasAssignedToTask&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$taskId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$userId&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;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$taskId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$userId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;taskId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$taskId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$userId&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Notification\Infrastructure\Event\Handler&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Task\Domain\Event\UserWasAssignedToTask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NotifyUserAboutTaskAssignment&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;function&lt;/span&gt; &lt;span class="n"&gt;__invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;UserWasAssignedToTask&lt;/span&gt; &lt;span class="nv"&gt;$event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// here you can do everything you want&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;Now we have a nice event and handler in the new codebase, let use it in legacy to tie everything up.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Legacy\Service&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Task\Domain\Event\UserWasAssignedToTask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TaskHelper&lt;/span&gt; 
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$eventBus&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$repository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;assignUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$taskId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$userId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assignUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$taskId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$userId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;eventBus&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;UserWasAssignedToTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$taskId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$userId&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when we have it all in place, each time when a user is assigned to a task, a new event will be dispatched and handled. We can easily extend the functionality of our handler, &lt;strong&gt;we can also add more handlers&lt;/strong&gt;, e.g. log this in some event log, or propagate this change to some external system.&lt;/p&gt;

&lt;h3&gt;
  
  
  What if you want to reuse legacy code?
&lt;/h3&gt;

&lt;p&gt;Another new feature we wanted to implement were automated actions. The user can set some rules which will perform actions based on some condition. For example, when a task is finished it should be assigned to the proper user. Not too complicated, looks quite easy to implement. &lt;/p&gt;

&lt;p&gt;But what if all actions which should be performed are in legacy (in a non-reusable form)? E.g. Method in Symfony controller handles the whole logic.&lt;br&gt;
Should we refactor all those places? How much time will it take?&lt;/p&gt;
&lt;h3&gt;
  
  
  Use command
&lt;/h3&gt;

&lt;p&gt;What we can do is to &lt;strong&gt;add commands and handlers for all actions we need&lt;/strong&gt;. Commands will contain all data needed and the handler will wrap up legacy code. We will not refactor anything in this process, just take it as it is and put in a new shiny box called “command handler”. Of course, this will not pay off the &lt;a href="https://accesto.com/blog/technical-debt-the-silent-villain-of-web-development/"&gt;technical debt&lt;/a&gt; we had in legacy but allows to introduce new features easier and faster. And what is most important, will allow us to &lt;strong&gt;refactor smaller parts in the future&lt;/strong&gt;. One command handler is for sure easier to refactor than the whole legacy controller/service/module.&lt;/p&gt;

&lt;p&gt;Thanks to that we can easily use this code in a new codebase, not only it will be reusable but also it will be in line with the new approach. &lt;/p&gt;

&lt;p&gt;And this was what we did, code we needed was extracted to handlers, and replaced everywhere in legacy by dispatching command.&lt;/p&gt;
&lt;h4&gt;
  
  
  Example
&lt;/h4&gt;

&lt;p&gt;I will use code from the previous example. We will refactor TaskController to a new approach. Of course, this example will be shortened to just show the concept. In our project, there is much more logic behind this feature.&lt;/p&gt;

&lt;p&gt;Let first prepare the command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Task\Domain\Command&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AssignUserToTask&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$taskId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$userId&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;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$taskId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$userId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;taskId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$taskId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$userId&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;and handler&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Task\Application\CommandHandler&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Task\Domain\Command\AssignUserToTask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Task\Domain\Exception\TaskNotExistException&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AssignUserToTaskHandler&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$taskHelper&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

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

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;AssingUserToTask&lt;/span&gt; &lt;span class="nv"&gt;$command&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$taskId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$command&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getTaskId&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nv"&gt;$userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$command&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getUserId&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;taskExists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$taskId&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TaskNotExistException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$taskId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;userIsAssignedToTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$taskId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$userId&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;taskHelper&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assignUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$taskId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$userId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
        &lt;span class="c1"&gt;// now we can also move event dispatching from TaskHelper to&lt;/span&gt;
        &lt;span class="c1"&gt;// this command handler &lt;/span&gt;
            &lt;span class="c1"&gt;// (but I will leave this at it is for now)&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;Probably you noticed that I moved some parts of TaskController to the handler also, it is always a good idea to make such small improvements &lt;a href="https://accesto.com/blog/Boy-scout-rule-in-6-examples-the-basic-principle-of-web-development/"&gt;boy scout rule&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now we can adjust TaskController.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Legacy\Controller&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Task\Domain\Command\AssignUserToTask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TaskController&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;function&lt;/span&gt; &lt;span class="n"&gt;assignUsers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$taskId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nv"&gt;$users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'users'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="nv"&gt;$commandBus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'command_bus'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

       &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$users&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="nv"&gt;$usersIds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;explode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;','&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$users&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

           &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$usersIds&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="nv"&gt;$commandBus&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AssignUserToTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$taskId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$id&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="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&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;Looks quite easy, right? Probably you are wondering why we did this, not much changed in the code, the handler is still the same legacy code but in a different place. TaskController looks almost the same.&lt;/p&gt;

&lt;p&gt;Let me remind why we did this. We did this to introduce automated actions. &lt;br&gt;
Thanks to the command/handler approach we can easily do this.&lt;/p&gt;

&lt;p&gt;Our example automated action should work as follow: When the task is finished then assign the proper user to this task.&lt;/p&gt;

&lt;p&gt;Let first add TaskFinished event&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;
&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt; 

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Task\Domain\Event&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TaskFinished&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$taskId&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;Then we can add a handler for our automated action.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Automations\Infrastructure\Event\Handler&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Task\Domain\Command\AssignUserToTask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Task\Domain\Event\TaskFinished&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AssignUserOnTaskFinished&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nv"&gt;$commandBus&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

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

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;TaskFinished&lt;/span&gt; &lt;span class="nv"&gt;$event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$taskId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$event&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getTaskId&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;isActionEnabled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$taskId&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nv"&gt;$userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getUserIdForAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$taskId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;commandBus&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AssignUserToTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$taskId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$userId&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="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;This is of course some kind of compromise, &lt;strong&gt;we will not get rid of legacy, it will still be there, but tamed&lt;/strong&gt;. Now you can easily use legacy code even without knowing that in fact legacy code will be executed. Everything will look nice and as good as your new codebase. &lt;/p&gt;

&lt;p&gt;You can later (when pressure for new features will be lower)  take such AssignUserToTaskHanlder and refactor it properly to get rid of the legacy TaskHelper service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Working with legacy?
&lt;/h2&gt;

&lt;p&gt;And what is your approach to legacy? Strangling, taming, rewriting? To learn more on that topic, make sure to check our CTO's blogpost on &lt;a href="https://accesto.com/blog/handle-technical-debt-in-legacy-application-4-possible-scenarios/"&gt;possible scenarios of handling technical debt in legacy applications&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>php</category>
      <category>codequality</category>
      <category>programming</category>
      <category>symfony</category>
    </item>
    <item>
      <title>Docker on new MacBook Pro with M1 Pro chip in 2022?</title>
      <dc:creator>klobermajer</dc:creator>
      <pubDate>Fri, 25 Mar 2022 07:35:05 +0000</pubDate>
      <link>https://forem.com/accesto/docker-on-new-macbook-pro-with-m1-pro-chip-in-2022-dj</link>
      <guid>https://forem.com/accesto/docker-on-new-macbook-pro-with-m1-pro-chip-in-2022-dj</guid>
      <description>&lt;h2&gt;
  
  
  What changed in 2022?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Running Docker on Apple MacBook&lt;/strong&gt; computers has been a common problem for many developers since the beginning. In many cases it performs so badly that we are forced to look for different, alternative solutions. Probably there are also people who decided to replace their MacBooks with linux based computers. In my previous &lt;a href="https://accesto.com/blog/docker-on-mac-how-to-speed-it-up/" rel="noopener noreferrer"&gt;article&lt;/a&gt; I made a comprehensive approach to find the best option for those, who do not want to replace their Apple computers with anything else, but still are forced to use Docker.&lt;/p&gt;

&lt;p&gt;In this article I considered different volume configurations (default, cached, delegated) for Docker on Mac, using Docker on VPS, and mutagen (as a solution for files synchronization with Docker). &lt;strong&gt;My final decision for 2021&lt;/strong&gt; was to use external VPS, although approach with mutagen seemed promising.&lt;/p&gt;

&lt;p&gt;I changed a few things since that time, I changed my VPS, and what is most important,  &lt;strong&gt;I changed my MacBook Pro to a new model&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Wondering how did it impact Docker performance? Bear with me. But first, let’s have a look at changes in my setup.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;VPS&lt;/th&gt;
&lt;th&gt;Current&lt;/th&gt;
&lt;th&gt;Previous&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CPU&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;4 cores&lt;/td&gt;
&lt;td&gt;2 cores&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RAM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;8GB&lt;/td&gt;
&lt;td&gt;4GB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;MacBook&lt;/th&gt;
&lt;th&gt;Current&lt;/th&gt;
&lt;th&gt;Previous&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Model&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;MacBook Pro 14” (2021)&lt;/td&gt;
&lt;td&gt;MacBook Pro 13” &lt;br&gt;(2017 without touch bar)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CPU&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;M1 Pro - 10 cores&lt;/td&gt;
&lt;td&gt;Intel Core i5 - 2 cores&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RAM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;32GB&lt;/td&gt;
&lt;td&gt;16GB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Is it worth buying a new MacBook Pro with Apple Silicon?
&lt;/h2&gt;

&lt;p&gt;There is no doubt that the MacBook with the new Apple M1 Pro chip with ARM architecture is a very powerful machine. You can feel the difference right away, in any aspect. It is fast, quiet and it is not getting too warm even on high load. I’m very glad that I changed my old one to this model.&lt;/p&gt;

&lt;p&gt;But in the case of Docker there is no simple answer to that question.&lt;/p&gt;

&lt;h2&gt;
  
  
  What about Docker?
&lt;/h2&gt;

&lt;p&gt;There is no better way to find an answer to that question than some benchmark.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benchmark methodology
&lt;/h3&gt;

&lt;p&gt;I used the same methodology as in my &lt;a href="https://accesto.com/blog/docker-on-mac-how-to-speed-it-up/" rel="noopener noreferrer"&gt;previous article&lt;/a&gt;. Also tests were performed on the same project with the same data.&lt;/p&gt;

&lt;p&gt;Docker for Mac has some settings which might influence general performance. It is related to resources allocations. For example you can allocate 4 CPUs and 8GB of RAM or you can increase that number according to what you need. I used settings visible below.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg6l4q6g3eysm30t997gl.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg6l4q6g3eysm30t997gl.png" alt="Docker resources allocation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Building Docker images
&lt;/h3&gt;

&lt;p&gt;Let’s start with something that should not be influenced too much by volume performance - Docker builds.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F08ms2pdwni0v1lmv2zyr.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F08ms2pdwni0v1lmv2zyr.png" alt="Build time"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, the difference is huge. Docker image was built in only seven minutes on MacBook M1 Pro, which was even better than build time on my new VPS. This is not surprising, I gave Docker quite a lot of resources. But it also shows that if there are not too many I/O disk operations, performance is quite good. What is also important, my computer did not slow down during that process. I was able to use it normally and it did not even reach 60 degrees Celsius.&lt;/p&gt;

&lt;p&gt;Based on that you can think that there is hope that finally Docker can work fast on MacBook, but keep reading.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let’s check how fast Docker containers will handle requests on Apple Silicon
&lt;/h2&gt;

&lt;p&gt;This time I did not play too much with volume configuration, I checked only cached and delegated. Besides that I set up mutagen, because it was very promising last time.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz2dhsn1fh43n0fjcotkc.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz2dhsn1fh43n0fjcotkc.png" alt="Docker on mac M1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Volume setting has not too much impact on performance. It performs slightly better in comparison to my old MacBook but it is still slow for both cached and delegated. What is interesting here is how fast it is with mutagen. Looks like if we take out I/O disk operations out of the equation we will finally have a setup which can perform very well, maybe  even better than on Linux.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is Docker desktop with Mutagen still slower than running Docker on VPS?
&lt;/h3&gt;

&lt;p&gt;VPS was the ultimate winner of my &lt;a href="https://accesto.com/blog/docker-on-mac-how-to-speed-it-up/" rel="noopener noreferrer"&gt;previous tests&lt;/a&gt;. It has many advantages and it was the fastest one. Running Docker with Mutagen directly on MacBook was alternative solution which I rated very well. It was still a little bit slower, and what is most important - very resource consuming, but it had a chance to compete with linux based server. Because I have a new VPS, with similar number of CPU cores and similar amount of RAM that I allocated in Docker on Mac, let’s find out if it can beat M1 Pro.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8wo5ftqu61faceblb3xv.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8wo5ftqu61faceblb3xv.png" alt="Docker on new VPS"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Results are quite ok, it is faster than my previous VPS, but surprisingly it is significantly slower than Docker on MacBook Pro with Apple M1 Pro chip and mutagen. &lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts on Docker MacBook Pro M1
&lt;/h2&gt;

&lt;p&gt;To be honest when I was buying a new MacBook I was not thinking about running Docker on it. I assumed that my setup on VPS is good enough and I can still use it. And probably I wouldn't check its performance if I didn't want to write this article.&lt;/p&gt;

&lt;p&gt;After all these tests and checks I am really surprised. I did not expect that it could be so fast. Still VPS has some advantages (it can be used from multiple devices, it can be used for other purposes etc.) but it is slower and needs constant internet connection. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;So my decision for 2022 is: Docker on Mac M1 Pro with Mutagen &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;More info how to configure and use Mutagen with Docker can be found &lt;a href="https://accesto.com/blog/docker-on-mac-how-to-speed-it-up/#mutagen" rel="noopener noreferrer"&gt;in my previous article.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I don’t know how Docker on Mac with mutagen will perform in the long term, but I do not expect any problems as I’m using mutagen on a daily basis to sync files with VPS. So I will definitely give it a shot. &lt;/p&gt;

&lt;h3&gt;
  
  
  There is another aspect worth considering, money.
&lt;/h3&gt;

&lt;p&gt;New Apple computers are quite expensive, when I’m writing this (February 2022) you need to spend 1999$ on the cheapest version of MacBook Pro 14”. My VPS costs 4.99$ per month, so the cost of a new macbook is equivalent of around 33 years of using VPS. &lt;/p&gt;

&lt;p&gt;Taking that into consideration I would advise you to first check how fast is Docker with mutagen on your current MacBook and if it is not fast enough, but your Mac is still good and you are perfectly happy with it (besides Docker performance), use VPS, instead spending a lot of money on new machine.&lt;/p&gt;

&lt;h3&gt;
  
  
  Are there any other options to consider?
&lt;/h3&gt;

&lt;p&gt;Improving performance might be achieved also by optimizing the build process and container configuration. We have published a free ebook on that -  &lt;a href="https://accesto.com/books/docker-deep-dive/" rel="noopener noreferrer"&gt;Docker Deep Dive&lt;/a&gt;. Applying the techniques described in the ebook we optimized the build time from 17 to 5 minutes on the VPS, and to ~2 minutes on a bigger dedicated server that we use for our CI jobs. So if you struggle with build times, I strongly advise to download the ebook and have a look at some potential changes first.&lt;/p&gt;

&lt;p&gt;We plan to write a follow up article describing the mentioned Dockerfile optimization as a case-study article. Subscribe to our newsletter to not miss this update.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>apple</category>
      <category>macbook</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
