Architecture for middle-sized Rails application

2017-03-08 22:40
2017-03-08 22:40

Guten Tag!

Are you working on Rails project? Do you have a problem with the architecture? If yes, this article might be helpful to you.

As your application getting bigger, your application getting messy and uncontrollable. This kind of problem is a famous problem as "Fat Controller", "Fat Model". You want to solve this problem and want to regain your clean codes. By the way, why this happens? It's because MVC architecture is not perfect all the time. Rails provides us a really easy way to build an application at least on early stage. You can just follow the MVC architecture. However, at some point, you would find that you need to change something, anyway. How and what you should change?

As one of the solutions, I'm gonna show you my strategy to solve it which is for a middle-sized Rails application.


Environment

  • Ruby 2.3.1
  • Rails 4.2.6


Blueprint


Controller Layer

Controller Layer receives all request. Basically, only this layer can call domain layers. This layer should be really simple.

  def save
    @request.assign_attributes(assignable_request_params)
    if @request.save
      RequestFlow::ReceiveFromAPI.execute(@request, @request_token)
      render json: { result: 'success' }
    else
      render json: { result: 'failed' }
    end
  end

Here, RequestFlow::ReceiveFromApi is a domain.

Domain Layer

This is one of the most important layers. A domain should be named understandable not only for IT people but also for business people and customer support etc. For example, I named like SelectXXX, SendXXX, CreateXXX. You can define here kind of hook events such as email notifications. Ideally, a domain should be a process.

module RequestFlow
  module Update

    module_function

    def execute(request, options={})
        GeocodeJob.perform_later(request) unless request.geocoded?
        Pipedrive::Updator.update_parameters(
          PipedriveStage.hash[request.status],
          request.pipedrive_deal,
          request
        )
        request.update_status
      end

      unless options[:skip_notification] == true
        # This is a Service Layer
        Notifier::Request.complete_updating(request_offer)
      end
    end
  end
end 

A domain can call models and services. But, shouldn't be called by them. It should be understandable what will happen in the domain. Then, we can talk like this.

CS: When a user update his/her request, what would happen?
IT: Let me check the domain... The request will be geocoded if not yet, and send the detail to pipedrive. Finally, update its status.

Sounds nice, doesn't it?

Service Layer

This is a stateless layer. Services should be kind of command. For example, geocode_setter, locale_finder, distance_calculator etc. In my experience, to put Notifier here works fine. Notifier calls action_mailer, sms sender etc.

  class Notifier::Request
    def initialize(request, timing = :wait, skip_validation = false)
    end

    ...

    def complete_request
      klass = new(request)
      klass.notify_internaly
      klass.via_sms
      klass.via_email
    end

    def via_email
      # call ActionMailer
    end
  end

Model Layer

Thanks to Service Layer and Domain Layer, Model Layer should be really thinner. A model has to concern only what it is. It includes only some hook methods, scopes and some simple methods which relate to its attributes. Don't afraid of creating a pure-Ruby class(not extended ActiveRecord::Base). They help you much to design clean and understandable architecture.

Validation Layer

This also can help to make Model Layer thinner. If you don't know how to implement this on Rails, let's google it. Just simply, you can extend ActiveModel::Validator class. Then define it to call on a model by validates_with etc. Let's focus on only validations here!




This is the basic idea. Of course, in real, it's more complicated, though, this works fine on my application. The important point is Direction of the flow. Take care who can call who. Who should be called by whom? This is the main topic. Otherwise, your code would be messy and cannot rollback anymore.

If you want to understand more, I recommend you to read books listed below.


Tschüs!

ca-pub-77730931628225157846773125
このエントリーをはてなブックマークに追加