Microservices Development Cookbook
上QQ阅读APP看书,第一时间看更新

How to do it...

Refactoring any code base is a process. For monoliths, there are a few techniques that can work quite well. In this example, we'll document the steps that can be taken to make refactoring a Ruby on Rails code base easy:

  1. Using the techniques described in previous recipes, identify business capabilities and bounded contexts within your application. Let's focus on the ability to upload pictures and videos. 

 

  1. Create a directory called app/services alongside controllers, models, and views. This directory will hold all of your service objects. Service objects are a pattern used in many Rails applications to factor out a conceptual service into a ruby object that does not inherit any Ruby on Rails functionality. This will make it easier to move the functionality encapsulated within a service object into a separate microservice. There is no one way to structure your service objects. I prefer to have each object represent a service, and move operations I want that service to be responsible for to that service object as methods. 
  2. Create a new file called attachments_service.rb under app/services and give it the following definition:
class AttachmentsService

def upload
# ...
end

def delete!
# ...
end

end
  1. Looking at the source code for the AttachmentsController#create method in the app/controllers/attachments_controller.rb file, it currently handles the responsibility for creating the Attachment instance and uploading the file data to the attachment store, which in this case is an Amazon S3 bucket. This is the functionality that we need to move to the newly created service object:
# POST /messages/:message_id/attachments
def create
message = Message.find_by!(params[:message_id], user_id:
current_user.id)
file = StorageBucket.files.create(
key: params[:file][:name],
body: StringIO.new(Base64.decode64(params[:file][:data]),
'rb'),
public: true
)
attachment = Attachment.new(attachment_params.merge!(message:
message))
attachment.url = file.public_url
attachment.file_name = params[:file][:name]
  attachment.save
json_response({ url: attachment.url }, :created)
end
  1. Open the newly created service object in the app/services/attachments_service.rb file and move the responsibility for uploading the file to the AttachmentsService#upload method:
class AttachmentsService

def upload(message_id, user_id, file_name, data, media_type)
message = Message.find_by!(message_id, user_id: user_id)
file = StorageBucket.files.create(
key: file_name,
body: StringIO.new(Base64.decode64(data), 'rb'),
public: true
)
Attachment.create(
media_type: media_type,
file_name: file_name,
url: file.public_url,
message: message
)
end

def delete!
end
end
  1. Now upload the AttachmentsController#create method in app/controllers/attachments_controller.rb to use the newly created AttachmentsService#upload method:
# POST /messages/:message_id/attachments
def create
service = AttachmentService.new
attachment = service.upload(params[:message_id], current_user.id,
params[:file][:name], params[:file][:data],
params[:media_type])
json_response({ url: attachment.url }, :created)
end
  1. Repeat this process for code in the AttachmentsController#destroy method, moving the responsibility to the new service object. When you're finished, no code in AttachmentsController should be interacting with the Attachments model directly; instead, it should be going through the AttachmentsService service object.

You've now isolated responsibility for the management of attachments to a single service class. This class should encapsulate all of the business logic that will eventually be moved to a new attachment service.