Hello, this is @ hairgai. This time, I will write my own usage of the pros and cons of the Service class.
A class group that describes a function that is derived from (and is self-recognizing) a service in DDD (Domain Driven Design). There are many people who explain in detail, so I will omit it, but I often use it because I can write business logic neatly between the model and the controller. This time (although my usage may be wrong), I will write my own usage and the part that seems to be its merit.
First of all, I would like to introduce the basic usage with a code example. (I'm not sure if this is the correct answer, but I think it's good because it's easy to see.) For example, if you describe the function of "following" on SNS etc. as a service layer in one file, it will look like this. ** * I'm repeating, but this is not the correct answer. ** **
class FollowService
attr_reader :user, :target_user
attr_accessor :follow
def initialize(user, target_user)
@user = user
@target_user = target_user
end
def perform
check!
create_follow!
run_after_worker!
end
private
def check!
check_following!
check_blocking!
end
def check_following!
return true unless user.following?(target_user)
raise ArgumentError, 'User following target user'
end
def check_blocking!
return true unless user.blocking?(target_user)
raise ArgumentError, 'User blocking target user'
end
def create_follow!
self.follow = user.follows.create!(target_user: target_user)
end
def run_after_worker!
AfterFollowWorker.perform_in(0.2.seconds, follow.id)
end
end
Basically, I don't make anything using Gem etc. By implementing it in pure Ruby, we are aiming for the effect of "lowering the barrier to understanding the implementation". The logic of the Service class tends to be complicated (when it comes to heavy functionality), so I try to keep it as simple as possible so that anyone can understand it immediately.
I think you like the naming, but since it is a class that symbolizes the function, it is unified with [verb]([object]) Service
.
Having a naming convention has the advantage of making it easier to infer functions.
I think there are many people who like it (many people call it call
), but basically, only one public method is used, and the others cannot be called.
This is because the Service class is a class that symbolizes a single function, and the functions that can be realized by using it are limited to a single one.
There are quite a lot of people saying this single public method, but I also adopted it because there was no hesitation at the time of design and the implementation speed increased dramatically.
This is also adopted because it improves the visibility + 1 The responsibility for each method becomes lighter and the guard clause becomes easier to use. I think this is where you have different tastes.
As I wrote in Follow Service above, I think about the response to the user first as long as it is used for business. As an approach in such a case, there is a method of "performing only necessary processing during one response", and subsequent processing etc. is stored in the queue as a job and processed by the job server etc. At that time, if the process of calling the job is scattered in various places such as Controller and Model, the visibility of the entire project will be poor.
Therefore, you can improve the overall outlook by using a naming convention such as ʻAfterHogeJob for post-processing jobs of
HogeService` and calling only in the Service class.
Needless to say.
class FollowsController < ApplicationController
def create
target_user = user.find(params[:target_user_id])
service = FollowService.new(current_user, target_user)
service.perform
redirect_to user_path(target_user)
end
end
Needless to say. Bring everything written in the Model to the Service class, and the model will be only data association, validation, and other methods related to a single model. As a result, there are no more logic-rich methods in the model, and no bloated, hard-to-see models.
When I write it again in this way, I think that I am writing in a way that blames the Service class in various ways. I think it is arguable that the Service class is not necessary, but since such a story of design is just one of the methods, it would be nice if it could be used appropriately according to our business.
Recommended Posts