Content posted here with the permission of the author Anil Kumar Maurya, who is currently employed at Josh Software. Original post available here.
What is Service Based Architecture ?
At first Micro Service Architecture & Service Based Architecture looks similar but both are different from each other.
Micro service architecture advocates smaller components. An application can consist of hundred or thousands of micro services whereas Service based architecture advocates breaking the code apart in the domain-centric way. An application can consist of 10–12 deployable services. These services may have separate database or they may share same database.
Managing few micro services is easy but as number of micro services increases, challenges to manage them is not an easy task. Number of network calls also increases.
In case of Service based architecture, number of services are limited therefore managing it is not a challenge. Also number of network call is less therefore should give better performance.
ThoughtWorks director Neal Ford argued in a talk that organizations transition more easily from a monolithic architecture to a service-based architecture than to a microservices architecture
Ref: https://www.infoq.com/news/2016/10/service-based-architecture
Why we chose Service Based Architecture Over Micro Services
Background: We are building an ERP Software. It is going to use by 50–100 people at a time. We are team of 3 developer and we need to deliver first release in 3 months.
We aim to build scalable and maintainable product. Monolith is out of option. We had 2 options, Micro Service OR Service Based Architecture. Micro Service requires complex setup and will double our efforts. As we have limited team size and our timelines are fixed therefore Service Based Architecture with common database made more sense for us.
Challenges we faced
We had 8 repository, one for each services. Setting up project on local for new developer is very time consuming. Every service needed to setup separately.
Apart from setting up all services. We need to install postgres, redis & elasticsearch. If you are stuck while installing any one of it then it may eat up whole day.
Also starting up application required starting all 8 services manually (which is not interesting thing to do everyday)
Docker for our rescue
We created a single repository for all services. Now getting all changes on local is just a git pull
command away.
With docker, we can setup all services with all dependency with just one command.
docker-compose build
And we start our application (all services) by
docker-compose up
Setting up docker compose for an application which consist of 8 services (4 Rails-Api Backend & 4 React Frontend)
Application Directory structure looks like:
project │ │ └───service-1-api │ |───service-1-web │ └───service-2-api │ |───service-2-web │ └───service-3-api │ |───service-3-web │ └───service-4-api │ |───service-4-web │ └───docker-compose.yml │ └───Dockerfile │ └───Dockerfile-React
*Dockerfile is for api images * Dockerfile-React is for react application images
Of course our services are not named as service-1 & service-2. I have changed it deliberately for privacy.
Our docker-compose.yml:
version: '3.6' services: db: image: postgres redis: image: 'redis:latest' elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch:6.5.4 container_name: elasticsearch environment: - cluster.name=docker-cluster - bootstrap.memory_lock=true - "ES_JAVA_OPTS=-Xms512m -Xmx512m" - "discovery.zen.ping.unicast.hosts=elasticsearch" ulimits: memlock: soft: -1 hard: -1 volumes: - esdata1:/usr/share/elasticsearch/data ports: - 9200:9200 service-1-api: build: context: './service-1-api' dockerfile: $PWD/Dockerfile volumes: - $PWD/service-1-api:/app command: bundle exec puma -p 3000 ports: - 3000:3000 depends_on: - db service-1-web: build: context: './service-1-web' dockerfile: $PWD/Dockerfile-React volumes: - $PWD/service-1-web/:/app/ ports: - 3001:3001 environment: NODE_ENV: development CHOKIDAR_USEPOLLING: 'true' service-2-sidekiq: depends_on: - db - redis - elasticsearch build: context: './service-2-api' dockerfile: $PWD/Dockerfile command: bundle exec sidekiq -C config/sidekiq.yml volumes: - $PWD/service-2-api:/app service-2-api: build: context: './service-2-api' dockerfile: $PWD/Dockerfile volumes: - $PWD/service-2-api:/app command: bundle exec puma -p 3002 ports: - 3002:3002 depends_on: - db - elasticsearch - service-2-sidekiq stdin_open: true tty: true service-2-web: build: context: './service-2-web' dockerfile: $PWD/Dockerfile-React volumes: - $PWD/service-2-web/:/app/ command: npm start ports: - 3003:3003 environment: - NODE_ENV=development - CHOKIDAR_USEPOLLING=true service-3-sidekiq: depends_on: - db - redis - elasticsearch build: context: './service-3-api' dockerfile: $PWD/Dockerfile command: bundle exec sidekiq -C config/sidekiq.yml volumes: - $PWD/service-3-api:/app service-3-api: build: context: './service-3-api' dockerfile: $PWD/Dockerfile volumes: - $PWD/service-3-api:/app command: bundle exec puma -p 3004 ports: - 3004:3004 depends_on: - db - elasticsearch - service-3-sidekiq stdin_open: true tty: true service-3-web: build: context: './service-3-web' dockerfile: $PWD/Dockerfile-React volumes: - $PWD/service-3-web/:/app/ command: npm start ports: - 3005:3005 environment: - NODE_ENV=development - CHOKIDAR_USEPOLLING=true service-4-api: build: context: './service-4-api' dockerfile: $PWD/Dockerfile volumes: - $PWD/service-4-api:/app command: bundle exec puma -p 3006 ports: - 3006:3006 depends_on: - db stdin_open: true tty: true service-4-web: build: context: './service-4-web' dockerfile: $PWD/Dockerfile-React volumes: - $PWD/service-4-web/:/app/ working_dir: /app command: npm start ports: - 3007:3007 environment: - NODE_ENV=development - CHOKIDAR_USEPOLLING=true volumes: esdata1: driver: local
*using this docker-compose.yml configuration, service restart is not required on code change.
Dockerfile:
FROM ruby:2.5.3-alpine RUN apk add --update bash build-base postgresql-dev tzdata RUN gem install rails -v '5.1.6' WORKDIR /app ADD Gemfile Gemfile.lock /app/ RUN bundle install COPY . /app/
Dockerfile-React
FROM node:11.6.0-alpine WORKDIR '/app' # Install yarn and other dependencies via apk RUN apk update && apk add yarn python g++ make && rm -rf /var/cache/apk/* COPY package.json yarn.lock /app/ RUN yarn install RUN yarn global add react-scripts COPY . ./ CMD ["npm", "run", "start"]
For adding new gems in rails api service, add gem in Gemfile and build new image for that service, example:
docker-compose build service-1-api
For adding new package in react app service, use
docker-compose run service-1-web yarn add `package-name`
Conclusion:
Service Based Architecture is good alternative for applications where Manpower & Time are constraints.
In Next Blog I will write about deploying this Application on Amazon ECS(Elastic Container Service).
One thought on “Why Setting Up A Docker Is Crucial For Service-Based Architecture Application”