Book Image

Microservices Development Cookbook

By : Paul Osman
Book Image

Microservices Development Cookbook

By: Paul Osman

Overview of this book

Microservices have become a popular choice for building distributed systems that power modern web and mobile apps. They enable you to deploy apps as a suite of independently deployable, modular, and scalable services. With over 70 practical, self-contained tutorials, the book examines common pain points during development and best practices for creating distributed microservices. Each recipe addresses a specific problem and offers a proven, best-practice solution with insights into how it works, so you can copy the code and configuration files and modify them for your own needs. You’ll start by understanding microservice architecture. Next, you'll learn to transition from a traditional monolithic app to a suite of small services that interact to ensure your client apps are running seamlessly. The book will then guide you through the patterns you can use to organize services, so you can optimize request handling and processing. In addition this, you’ll understand how to handle service-to-service interactions. As you progress, you’ll get up to speed with securing microservices and adding monitoring to debug problems. Finally, you’ll cover fault-tolerance and reliability patterns that help you use microservices to isolate failures in your apps. By the end of this book, you’ll have the skills you need to work with a team to break a large, monolithic codebase into independently deployable and scalable microservices.
Table of Contents (16 chapters)
Title Page
Copyright and Credits
Packt Upsell
Contributors
Preface
Index

Refactoring your monolith


A common mistake when making the transition to microservices is to ignore the monolith and just build new features as services. This usually happens when a team feels that the monolith has gotten so out of control, and the code so unwieldy, that it would be better to declare bankruptcy and leave it to rot. This can be especially tempting because the idea of building green field code with no legacy baggage sounds a lot nicer than refactoring brittle, legacy code. 

Resist the temptation to abandon your monolith. To successfully decompose your monolith by business capability and start evolving it into a set of nicely factored, single-responsibility microservices, you'll need to make sure that your monolith code base is in good shape and is well factored, and well tested. Otherwise, you'll end up with a proliferation of new services that don't model your domain cleanly (because they overlap with functionality in the monolith), and you'll continue to have trouble working with any code that exists in your monolith. Your users won't be happy and your teams' energy will most likely start to decline as the weight of technical debt becomes unbearable. 

Instead, take constant, proactive steps to refactor your monolith using good, solid design principles. Excellent books have been written on the subject of refactoring (I recommend Refactoring by Martin Fowler and Working Effectively with Legacy Code by Michael Feathers), but the most important thing to know is that refactoring is never an all-or-nothing effort. Few product teams or companies will have the patience or luxury to wait while an engineering team stops the world and spends time making their code easier to change, and an engineering team that tries this will rarely be successful. Refactoring has to be a constant, steady process. 

However your team schedules its work, make sure you're reserving an appropriate time for refactoring. A guiding principle is, whenever you go to make a change, first make the change easy to make, then make the change. Your goal is to make your monolith code easier to work with, easier to understand, and less brittle. You should also be able to develop a robust test suite that will come in handy.

Once your monolith is in better shape, you can start to continuously shrink the monolith as you factor out services. Another aspect of most monolith code bases is serving dynamically generated views and static assets served through browsers. If your monolith is responsible for this, consider moving your web application component into a separately served JavaScript application. This will allow you to shrink your monolith from multiple directions.

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#createmethod 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.