API Development: Turning Controller Actions into Services

When you're developing an API, controller actions have a tendency to get large and inefficient. You can keep chopping controller actions into smaller pieces, but turning them into services may be where you want to go. The main reason for this is because you can get validation of the data before you use it to perform the service, making code more reliable.

SRP keeps your code from behaving like this.

What is a service and when would you need to make one?
A service is a class that interacts with multiple models. If a method on a model is going to access more than itself and maybe one neighbor then it should be pulled out into a service. Single Responsibility Principle (SRP) essentially states that a class should do just one thing. With this, we’re making our class do just one thing: create an order. That way, we don’t have a “fat model” that violates SRP.

Here’s an example app on github to supplement the code below.

app/controllers/orders_controller.rb

class OrdersController < ApplicationController  
  def create
    service = OrderCreationService.new(current_user, params[:order])
    service.perform

    if service.successful?
      respond_with service.order
    else
      respond_with service.errors, :status => 422
    end
  end
end  

app/services/order_creation_service.rb

class OrderCreationService  
  include ActiveModel::Validations

  validates :name, :presence => true

  attr_reader :user, :params, :order, :name

  delegate :as_json, :to => :order

  def initialize(user, params)
    @user = user
    @params = params

    params.each do |param, value|
      instance_variable_set("@#{param}", value) if respond_to?(param)
    end
  end

  def perform
    return unless valid?

    # This can be done with only one line, or you can go nuts and make
    # this much more expansive if your desired functionality demands it
    @order = user.orders.create(params)
  end

  def successful?
    valid? && order.persisted?
  end
end  

Here’s how this gives you the nice ability of having validations around the parameters your API takes:

$ curl -X POST http://localhost:3000/orders
   { 'errors': { 'name': ["can't be blank"] } }

With this method, your API development results in cleaner, more reliable code that will be easier for both internal developers and external developers to work with. Also, be sure to test via RspecApiDocumentation and Raddocs.

Check out our other articles on API:

API Versioning: 3 Ways to Architect Your API to Handle Versioned Requests

API Planning and Proceeding: Tell Me What You're Working With

What Makes A Good API?

Image Source

comments powered by Disqus