Small Is Big- Architecting microservices with Sinatra and RabbitMq
A traditional application architecture has Model, View and Controller or its variants. But over the years complexity of applications has increased exponentially, managing all the complexity in same code base created problems like scaling and code management. In recent past there has been a lot of attention towards micro-services architecture, in which we break the monolithic application into multiple independent components which are also known as micro-services.
There are different characteristics defined by Martin Fowler for Micro-services:-
- Componentization via Services
- Organized around Business Capabilities
- Products not Projects
- Smart endpoints and dumb pipes
- Decentralized Data Management
- Design for failure
Suppose we are going to build a system which manages Order, Shipping and Invoicing in a monolithic application. Now all these modules of Order, Shipping and Invoices communicate with each other synchronously and any change in the module affects the whole life-cycle of the application. Now if we break the monolith in orders, shipping and invoicing micro-services then we can independently deploy and scale individual micro-services.
Rather than focusing on pros and cons of micro-service architectural pattern, we will focus on building a micro-service based architecture for managing orders, shipping and invoicing services.
All these services are independently deployable and have their separate life-cycle for managing the data flow. Services are asynchronously communicating with each other using Broker in this case RabbitMq.
We are using following gems in the applications:-
- Sinatra- It’s a DSL for quickly creating web applications in Ruby with minimal effort.
- Bunny- Ruby Client for RabbitMq.
- Sneakers- High performance background jobs using RabbitMq.
- Mongoid- Mongoid is the officially supported ODM (Object-Document-Mapper) framework for MongoDB in Ruby.
We are building 3 microservices for order management system:-
- Order Microservice
- Shipping Microservice
- Invoicing Microservice
Let’s start with the setup of our applications-
If you have RVM and bundler installed on your machine then let’s create following folders and add Gemfile in their parent directory.
mkdir -p microservices/order-microservice microservices/shipping-microservice microservices/invoice-microservicetouch microservices/order-microservice/Gemfile
touch microservices/shipping-microservice/Gemfile
touch microservices/invoice-microservice/Gemfile
# bundle install
1. Order Microservice:-
Client is placing an order using API endpoints. If Order is created successfully a JSON message containing order details is published to broker.
We are going to expose two endpoints in order-microservice:-
- /list:- List all the orders in the system which will query all orders from mongodb.
- /create:- Create new Order in mongodb and notify shipping-microservice about the new order.
Add following gemfile in order-microservice
source 'https://rubygems.org'gem 'sinatra'
gem 'mongoid'
gem "bunny", ">= 2.7.0"group :test do
gem 'rspec'
end
2. Shipping Microservice:-
Client can list all the shipments using API endpoints and if there is any message placed in Broker this service will consume the order message, process it and publish a message in mesage broker that order has been shipped.
We are exposing one endpoint for shipping-microservice which will list all the shipped orders in application.
Add following gemfile in shipping-microservice
# Gemfile
source 'https://rubygems.org'gem 'sinatra'
gem 'mongoid'
gem "bunny", ">= 2.7.0"group :test do
gem 'rspec'
end
Here ShipmentWorker will fetch messages from ‘orders’ queue, create the shipment in database and push a message in ‘shipment’ queue that shipment has been created.
3. Invoice Microservice:-
This service will consume message from the broker and will generate an invoice and notify user using Push notification mechanism about the invoice.
Add following gemfile in invoice-microservice
# Gemfile
source 'https://rubygems.org'gem 'sinatra'
gem 'mongoid'
gem "bunny", ">= 2.7.0"group :test do
gem 'rspec'
end
InvoiceWorker will fetch messages from ‘shipment’ queue, parse message and then after validation it will inform the user about details of the Invoice.
Conclusion:- We have built a complete pipeline of message passing architecture into which different services are communicating with each other asynchronously using brokers. They are communicating with Clients using API endpoints which are synchronous calls.
We will discuss about deployment, scaling and service discovery of microservices in next articles.
Check out complete code from here.
References:-
- https://github.com/ewolff/microservice-kafka
- https://x-team.com/blog/how-to-create-a-ruby-api-with-sinatra/
- http://rubybunny.info/articles/exchanges.html
- https://martinfowler.com/articles/microservices.html
- https://martinfowler.com/microservices/#what
- https://blog.codeship.com/architecting-rails-apps-as-microservices/
- https://www.toptal.com/ruby/how-to-set-up-a-microservices-architecture
- http://coderascal.com/ruby/writing-a-microservice-in-ruby/