<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>design pattern Archives | Clever Cloud</title>
	<atom:link href="https://www.clever.cloud/blog/tag/design-pattern/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.clever.cloud/blog/tag/design-pattern/</link>
	<description>From Code to Product</description>
	<lastBuildDate>Fri, 14 Jun 2019 14:35:00 +0000</lastBuildDate>
	<language>en-GB</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://cdn.clever-cloud.com/uploads/2023/03/cropped-cropped-favicon-32x32.png</url>
	<title>design pattern Archives | Clever Cloud</title>
	<link>https://www.clever.cloud/blog/tag/design-pattern/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Clean up your Rails Controllers with Service Objects</title>
		<link>https://www.clever.cloud/blog/engineering/2019/06/14/clean-controllers-service-object-rails/</link>
		
		<dc:creator><![CDATA[Valeriane Venance]]></dc:creator>
		<pubDate>Fri, 14 Jun 2019 14:35:00 +0000</pubDate>
				<category><![CDATA[Engineering]]></category>
		<category><![CDATA[design pattern]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[separation of concerns]]></category>
		<guid isPermaLink="false">https://www2.cleverapps.io/wp/blog/technology/2019/06/14/clean-controllers-service-object-rails/</guid>

					<description><![CDATA[<p><img width="1400" height="540" src="https://cdn.clever-cloud.com/uploads/2021/08/banner-service-objects-1.jpg" class="attachment-post-thumbnail size-post-thumbnail wp-post-image" alt="banner service objects 1" decoding="async" fetchpriority="high" srcset="https://cdn.clever-cloud.com/uploads/2021/08/banner-service-objects-1.jpg 1400w, https://cdn.clever-cloud.com/uploads/2021/08/banner-service-objects-1-300x116.jpg 300w, https://cdn.clever-cloud.com/uploads/2021/08/banner-service-objects-1-1024x395.jpg 1024w, https://cdn.clever-cloud.com/uploads/2021/08/banner-service-objects-1-768x296.jpg 768w, https://cdn.clever-cloud.com/uploads/2021/08/banner-service-objects-1-1368x528.jpg 1368w" sizes="(max-width: 1400px) 100vw, 1400px" /></p><h2 id="fat-models-and-skinny-controllers-right">Fat Models and skinny Controllers right?</h2>
<p>You know what they say about the MVC design pattern, keep your Controllers light.<br>So at the beginning of your project, you added oil and gears to your Models and it was rolling just fine. Even if I&#39;m not a big fan of the big Model that even makes coffee (sending an email, really?), I can say that you were in perfect harmony with the framework principles.<br>Well, a few iterations later, this idyllic codebase seems very far away and your Controllers and Models are way more &#39;200 line&#39;ish than they used to be.</p>
<span id="more-2821"></span>

<p>Service Objects is a good solution to extract some logic out of them by transferring code into simple services that handles only one thing. This way, you end up with maintainable, super understandable and separated critical code.<br>On this article I&#39;m focusing on Controllers, but the principles are the same for Models. Now, some may ask, why not using Controller Helpers instead?</p>
<p>My answer to that would be: don&#39;t choose, use both!</p>
<h2 id="service-object-and-controller-helper">Service object and Controller helper</h2>
<p>Controller Helpers do hold code shared between Controllers and are also a good way to keep slim Controllers. They are great to respect the DRY (Don&#39;t Repeat Yourself) principles. If you target the same workflows in many Controllers, you must consider putting this logic into a helper. The code you put here is a simple succession of actions you do and conditions you are evaluating. They are useful only if used by at least two Controllers, otherwise, you should think of some code refactoring.<br>Anyway, we often hear that backend logic should not belong to the Controller and just moving everything into a helper won&#39;t solve the problem.</p>
<p>For every operation tied to your business logic (calling an API, calculation) performed in this duplicated code or present in any Controller, you should definitely consider creating a service object for each chunk of logic.</p>
<p>Okay, now you know when to use a service object, but what is it exactly?</p>
<figure>
  <img style="width:50%;margin-left:25%" alt="A League of Legends poro" src="https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/048fa9cc-2f1d-4920-b29d-c80d7aafb488/d63wx6c-8af07a43-f112-489d-9087-e9c10077b7c8.png?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOjdlMGQxODg5ODIyNjQzNzNhNWYwZDQxNWVhMGQyNmUwIiwiaXNzIjoidXJuOmFwcDo3ZTBkMTg4OTgyMjY0MzczYTVmMGQ0MTVlYTBkMjZlMCIsIm9iaiI6W1t7InBhdGgiOiJcL2ZcLzA0OGZhOWNjLTJmMWQtNDkyMC1iMjlkLWM4MGQ3YWFmYjQ4OFwvZDYzd3g2Yy04YWYwN2E0My1mMTEyLTQ4OWQtOTA4Ny1lOWMxMDA3N2I3YzgucG5nIn1dXSwiYXVkIjpbInVybjpzZXJ2aWNlOmZpbGUuZG93bmxvYWQiXX0.5MEQgeD-7_FV8NIh-rv8j8gw6FYgyQBuG_eOVUHq0Rc"/>
  <figcaption>A fluffy poro ! - Art by justduet on DeviantArt</figcaption>
</figure>

<p>A service object is a PORO. No, not that cute fluffy League of Legends NPC, but a Plain Old Ruby Object.<br>You just create a class not related to ActiveRecord (the RubyOnRails ORM) with it&#39;s own methods, put your code there, and WOW, you now have a PORO!<br>We will see how we construct it later, first we need to know where to put it in our project.</p>
<h2 id="break-it-to-rebuild-it">Break it to rebuild it</h2>
<p>First, let&#39;s take this Controller. One thing we can notice is an API call in this <code>get_weather</code> method and this should not be Controller business.</p>
<pre><code class="language-ruby">class HomeController &lt; ApplicationController
    require &#39;curb&#39;
    def index
    end

    def get_weather
        http = Curl.get(&quot;#{ENV[&quot;api_base&quot;]}#{ENV[&quot;api_key&quot;]}&quot;)
        if http.status = &quot;200 OK&quot;
            response_body = JSON.parse http.body_str
            @current_weather = response_body[&quot;weather&quot;][0][&quot;description&quot;]
            @current_temperature = &quot;#{(response_body[&quot;main&quot;][&quot;temp&quot;].to_f - 273.15).round(1)} °C&quot;
        else
            @current_weather = &quot;There was an error sorry: #{current_weather[:error]}&quot;
        end
    end
end
</code></pre>
<p>According to the name of the design pattern, we will create a <code>services</code> folder at <code>root_path/app/services/</code>.<br>Okay, looks like it&#39;s about getting weather informations so we will name another folder accordingly.<br>Finally, we can create our service object file. For this, we will be the most explicit and we will use something like <em>x_service.rb</em>.</p>
<p>Being precise on naming and using the <code>_service</code> suffix is here to help other code maintainers to easily understand what is the purpose of the service and where to find it in the project.</p>
<h3 id="skeleton">Skeleton</h3>
<p>This is how we should start every new service object. Here, we can note the import of the library &quot;ostruct&quot;. This allows us to use the OpenStruct object in order to return a rich object containing error messages and a <code>success?</code> method.</p>
<pre><code class="language-ruby">class FolderName::NameOfTheService
    require &quot;ostruct&quot;
    
    def initialize(params={})
        @params = params
    end

    def call
        begin
        rescue =&gt; exception
            OpenStruct.new(success?: false,
                           error: exception.message)
        else
            OpenStruct.new(success?: true, 
                           error: nil)
        end
    end

end
</code></pre>
<h3 id="filling-it-up">Filling it up</h3>
<p>Instead of just copying our Controller&#39;s code into our private method, we will use the wide known begin/rescue pattern to manage errors and will use it at our advantage to raise our own errors.  </p>
<pre><code class="language-ruby">class Weather::GetCurrentWeatherService
  require &quot;ostruct&quot;

    def initialize(params={})
      @params = params
    end

    def call
      @api_base = ENV[&quot;api_base&quot;]
      @api_key = ENV[&quot;api_key&quot;]

      begin
        http = Curl.get(&quot;#{@api_base}#{@api_key}&quot;)
        if http.status == &quot;200 OK&quot;
          response_body = JSON.parse http.body_str
          if response_body[&quot;weather&quot;] &amp;&amp; response_body[&quot;main&quot;]
            weather_string = response_body[&quot;weather&quot;][0][&quot;description&quot;]
            temperature = &quot;#{(response_body[&quot;main&quot;][&quot;temp&quot;].to_f - 273.15).round(1)} °C&quot;
          else
            raise
          end
        else
          raise
        end
      rescue =&gt; exception
          OpenStruct.new(success?: false,
                 error: exception.message)
        else
          OpenStruct.new(success?: true, 
                 weather_string: weather_string, 
                 temperature: temperature, 
                 error: nil)
        end
    end

end
</code></pre>
<h3 id="lets-refactor-this-controller">Let&#39;s refactor this Controller</h3>
<pre><code class="language-ruby">def get_weather
    current_weather = ::Weather::GetCurrentWeatherService.new.call(params: {})
    if current_weather.success?
        @current_weather = current_weather[:weather_string]
        @current_temperature = current_weather[:temperature]
    else
        @current_weather = &quot;There was an error sorry: #{current_weather[:error]}&quot;
    end
end
</code></pre>
<p>We simply call a new instance of our service in our Controller and use it&#39;s <code>success?</code> method to handle regular Controller rendering logic. Note the use of the prepending <code>::</code> it&#39;s for preventing the Controller to look for the service only in its own repository.<br>Here, the params argument is just an empty hash that I do not use but this was to show that, like every PORO, a service can take many parameters.</p>
<h2 id="this-pattern-is-so-great-but-ive-made-a-mess">This pattern is so great, but I&#39;ve made a mess!</h2>
<p>If our weather service was real, we would probably want to handle city search, forecast weather and so on. Maybe we would have extended our service object or created a bunch of others and now it&#39;s an obscure code to maintain and you can feel like you&#39;re drowning. No worries, it&#39;s just time for you to be rescued by NameSpacing!</p>
<p>In fact we already used it by putting our service in a weather folder in the first place. You should definitely classify all your new services by folders and never hesitate to create a sub folder when your folder holds too many service files.<br>If your services can be sliced by fields of usage or are really tied to a Model or Controller, create new folders and move your services under the corresponding ones.</p>
<p>For naming your folders, no master rule, but logic, efficiency, and consistency between the names.<br>Of course, you will need to rename your services. For instance, under a folder named <code>my_folder/my_folder_2</code> your <code>my_service</code> will be renamed <code>MyFolder::MyFolder2::MyService</code> and you will now use the same name in your Controllers to use them.</p>
<h2 id="so-so-or-not">So, SO or not?</h2>
<p>As you may have notice, I was mostly giving advices on how to implement it and never refer to the official documentation.<br>This is because this pattern does not have any convention and everyone can implement it its own way. In order to avoid maintainability issues, I would really recommend keeping these concerns in mind:</p>
<ul>
<li>always think concerns separation.</li>
<li>don&#39;t let your services grow big.</li>
<li>always return rich objects.</li>
<li>all your return objects should have the same structure.</li>
</ul>
<p>Happy refactoring!</p>
]]></description>
										<content:encoded><![CDATA[<p><img width="1400" height="540" src="https://cdn.clever-cloud.com/uploads/2021/08/banner-service-objects-1.jpg" class="attachment-post-thumbnail size-post-thumbnail wp-post-image" alt="banner service objects 1" decoding="async" srcset="https://cdn.clever-cloud.com/uploads/2021/08/banner-service-objects-1.jpg 1400w, https://cdn.clever-cloud.com/uploads/2021/08/banner-service-objects-1-300x116.jpg 300w, https://cdn.clever-cloud.com/uploads/2021/08/banner-service-objects-1-1024x395.jpg 1024w, https://cdn.clever-cloud.com/uploads/2021/08/banner-service-objects-1-768x296.jpg 768w, https://cdn.clever-cloud.com/uploads/2021/08/banner-service-objects-1-1368x528.jpg 1368w" sizes="(max-width: 1400px) 100vw, 1400px" /></p><h2 id="fat-models-and-skinny-controllers-right">Fat Models and skinny Controllers right?</h2>
<p>You know what they say about the MVC design pattern, keep your Controllers light.<br>So at the beginning of your project, you added oil and gears to your Models and it was rolling just fine. Even if I&#39;m not a big fan of the big Model that even makes coffee (sending an email, really?), I can say that you were in perfect harmony with the framework principles.<br>Well, a few iterations later, this idyllic codebase seems very far away and your Controllers and Models are way more &#39;200 line&#39;ish than they used to be.</p>
<span id="more-2821"></span>

<p>Service Objects is a good solution to extract some logic out of them by transferring code into simple services that handles only one thing. This way, you end up with maintainable, super understandable and separated critical code.<br>On this article I&#39;m focusing on Controllers, but the principles are the same for Models. Now, some may ask, why not using Controller Helpers instead?</p>
<p>My answer to that would be: don&#39;t choose, use both!</p>
<h2 id="service-object-and-controller-helper">Service object and Controller helper</h2>
<p>Controller Helpers do hold code shared between Controllers and are also a good way to keep slim Controllers. They are great to respect the DRY (Don&#39;t Repeat Yourself) principles. If you target the same workflows in many Controllers, you must consider putting this logic into a helper. The code you put here is a simple succession of actions you do and conditions you are evaluating. They are useful only if used by at least two Controllers, otherwise, you should think of some code refactoring.<br>Anyway, we often hear that backend logic should not belong to the Controller and just moving everything into a helper won&#39;t solve the problem.</p>
<p>For every operation tied to your business logic (calling an API, calculation) performed in this duplicated code or present in any Controller, you should definitely consider creating a service object for each chunk of logic.</p>
<p>Okay, now you know when to use a service object, but what is it exactly?</p>
<figure>
  <img style="width:50%;margin-left:25%" alt="A League of Legends poro" src="https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/048fa9cc-2f1d-4920-b29d-c80d7aafb488/d63wx6c-8af07a43-f112-489d-9087-e9c10077b7c8.png?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOjdlMGQxODg5ODIyNjQzNzNhNWYwZDQxNWVhMGQyNmUwIiwiaXNzIjoidXJuOmFwcDo3ZTBkMTg4OTgyMjY0MzczYTVmMGQ0MTVlYTBkMjZlMCIsIm9iaiI6W1t7InBhdGgiOiJcL2ZcLzA0OGZhOWNjLTJmMWQtNDkyMC1iMjlkLWM4MGQ3YWFmYjQ4OFwvZDYzd3g2Yy04YWYwN2E0My1mMTEyLTQ4OWQtOTA4Ny1lOWMxMDA3N2I3YzgucG5nIn1dXSwiYXVkIjpbInVybjpzZXJ2aWNlOmZpbGUuZG93bmxvYWQiXX0.5MEQgeD-7_FV8NIh-rv8j8gw6FYgyQBuG_eOVUHq0Rc"/>
  <figcaption>A fluffy poro ! - Art by justduet on DeviantArt</figcaption>
</figure>

<p>A service object is a PORO. No, not that cute fluffy League of Legends NPC, but a Plain Old Ruby Object.<br>You just create a class not related to ActiveRecord (the RubyOnRails ORM) with it&#39;s own methods, put your code there, and WOW, you now have a PORO!<br>We will see how we construct it later, first we need to know where to put it in our project.</p>
<h2 id="break-it-to-rebuild-it">Break it to rebuild it</h2>
<p>First, let&#39;s take this Controller. One thing we can notice is an API call in this <code>get_weather</code> method and this should not be Controller business.</p>
<pre><code class="language-ruby">class HomeController &lt; ApplicationController
    require &#39;curb&#39;
    def index
    end

    def get_weather
        http = Curl.get(&quot;#{ENV[&quot;api_base&quot;]}#{ENV[&quot;api_key&quot;]}&quot;)
        if http.status = &quot;200 OK&quot;
            response_body = JSON.parse http.body_str
            @current_weather = response_body[&quot;weather&quot;][0][&quot;description&quot;]
            @current_temperature = &quot;#{(response_body[&quot;main&quot;][&quot;temp&quot;].to_f - 273.15).round(1)} °C&quot;
        else
            @current_weather = &quot;There was an error sorry: #{current_weather[:error]}&quot;
        end
    end
end
</code></pre>
<p>According to the name of the design pattern, we will create a <code>services</code> folder at <code>root_path/app/services/</code>.<br>Okay, looks like it&#39;s about getting weather informations so we will name another folder accordingly.<br>Finally, we can create our service object file. For this, we will be the most explicit and we will use something like <em>x_service.rb</em>.</p>
<p>Being precise on naming and using the <code>_service</code> suffix is here to help other code maintainers to easily understand what is the purpose of the service and where to find it in the project.</p>
<h3 id="skeleton">Skeleton</h3>
<p>This is how we should start every new service object. Here, we can note the import of the library &quot;ostruct&quot;. This allows us to use the OpenStruct object in order to return a rich object containing error messages and a <code>success?</code> method.</p>
<pre><code class="language-ruby">class FolderName::NameOfTheService
    require &quot;ostruct&quot;
    
    def initialize(params={})
        @params = params
    end

    def call
        begin
        rescue =&gt; exception
            OpenStruct.new(success?: false,
                           error: exception.message)
        else
            OpenStruct.new(success?: true, 
                           error: nil)
        end
    end

end
</code></pre>
<h3 id="filling-it-up">Filling it up</h3>
<p>Instead of just copying our Controller&#39;s code into our private method, we will use the wide known begin/rescue pattern to manage errors and will use it at our advantage to raise our own errors.  </p>
<pre><code class="language-ruby">class Weather::GetCurrentWeatherService
  require &quot;ostruct&quot;

    def initialize(params={})
      @params = params
    end

    def call
      @api_base = ENV[&quot;api_base&quot;]
      @api_key = ENV[&quot;api_key&quot;]

      begin
        http = Curl.get(&quot;#{@api_base}#{@api_key}&quot;)
        if http.status == &quot;200 OK&quot;
          response_body = JSON.parse http.body_str
          if response_body[&quot;weather&quot;] &amp;&amp; response_body[&quot;main&quot;]
            weather_string = response_body[&quot;weather&quot;][0][&quot;description&quot;]
            temperature = &quot;#{(response_body[&quot;main&quot;][&quot;temp&quot;].to_f - 273.15).round(1)} °C&quot;
          else
            raise
          end
        else
          raise
        end
      rescue =&gt; exception
          OpenStruct.new(success?: false,
                 error: exception.message)
        else
          OpenStruct.new(success?: true, 
                 weather_string: weather_string, 
                 temperature: temperature, 
                 error: nil)
        end
    end

end
</code></pre>
<h3 id="lets-refactor-this-controller">Let&#39;s refactor this Controller</h3>
<pre><code class="language-ruby">def get_weather
    current_weather = ::Weather::GetCurrentWeatherService.new.call(params: {})
    if current_weather.success?
        @current_weather = current_weather[:weather_string]
        @current_temperature = current_weather[:temperature]
    else
        @current_weather = &quot;There was an error sorry: #{current_weather[:error]}&quot;
    end
end
</code></pre>
<p>We simply call a new instance of our service in our Controller and use it&#39;s <code>success?</code> method to handle regular Controller rendering logic. Note the use of the prepending <code>::</code> it&#39;s for preventing the Controller to look for the service only in its own repository.<br>Here, the params argument is just an empty hash that I do not use but this was to show that, like every PORO, a service can take many parameters.</p>
<h2 id="this-pattern-is-so-great-but-ive-made-a-mess">This pattern is so great, but I&#39;ve made a mess!</h2>
<p>If our weather service was real, we would probably want to handle city search, forecast weather and so on. Maybe we would have extended our service object or created a bunch of others and now it&#39;s an obscure code to maintain and you can feel like you&#39;re drowning. No worries, it&#39;s just time for you to be rescued by NameSpacing!</p>
<p>In fact we already used it by putting our service in a weather folder in the first place. You should definitely classify all your new services by folders and never hesitate to create a sub folder when your folder holds too many service files.<br>If your services can be sliced by fields of usage or are really tied to a Model or Controller, create new folders and move your services under the corresponding ones.</p>
<p>For naming your folders, no master rule, but logic, efficiency, and consistency between the names.<br>Of course, you will need to rename your services. For instance, under a folder named <code>my_folder/my_folder_2</code> your <code>my_service</code> will be renamed <code>MyFolder::MyFolder2::MyService</code> and you will now use the same name in your Controllers to use them.</p>
<h2 id="so-so-or-not">So, SO or not?</h2>
<p>As you may have notice, I was mostly giving advices on how to implement it and never refer to the official documentation.<br>This is because this pattern does not have any convention and everyone can implement it its own way. In order to avoid maintainability issues, I would really recommend keeping these concerns in mind:</p>
<ul>
<li>always think concerns separation.</li>
<li>don&#39;t let your services grow big.</li>
<li>always return rich objects.</li>
<li>all your return objects should have the same structure.</li>
</ul>
<p>Happy refactoring!</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
