Microservice Architecture design thinking
Introduction
This document explains microservices based architecture, which
should be treated as reference architecture while designing microservices based
architecture to solve given problem statement. Apart from explaining all
component shown in architecture this document suggests some tools which can be
used for those components. This document only presents a basic architecture of
a imaginary problem and must not be treated as solution for any problem
statement
Audience
The audience of this documents are all freshers and team lead
which are being involved in solution design activities for a given problem
statement.
Design Considerations
This section defines the identified major application design
goals, assumptions and constraints that make the basis of the internal design
and architecture of system. The technologies to be used for the development of
the application are also suggested as applicable.
- Assumptions
·
This is assumed that, this architecture design will only be used
as reference not as actual solution for any problem statement. Each problem
statement is unique and must have through to design and analysis
phase to have a solution architecture of its own.
- General
Constraints
- Application
response should not take more time than specified in problem statement
(if defined else assume it to be 5 Sec) to load.
- If
response take longer time than specified,
it should return the response with some promise of delivery of actual
response later (promise does not mean JavaScript “promise”).
- Only
users who are authenticated and authorised for the system can only access
the system.
- Sensitive
information will be encrypted for storing in database like user password.
- The
deletion of records will be soft deletion until specified in problem
statement.
Reference Architecture
Solution Architecture
Diagram
Component Description
A.
Firewall
A firewall is a network security device that monitors incoming
and outgoing network traffic and decides whether to allow or block specific
traffic based on a defined set of security rules.
Firewalls have been a first line of defence in network security
for over 25 years. They establish a barrier between secured and controlled
internal networks that can be trusted and untrusted outside networks, such as
the Internet.
B.
Load Balancer
A load balancer is a device that distributes network or
application traffic across a cluster of servers. Load balancing improves
responsiveness and increases availability of applications.
A load balancer sits between the client and the server farm
accepting incoming network and application traffic and distributing the traffic
across multiple backend servers using various methods. By balancing application
requests across multiple servers, a load balancer reduces individual server
load and prevents any one application server from becoming a single point of
failure, thus improving overall application availability and responsiveness.
For more please follow this Link
C.
Web Apps
WebApp are UI part of the application which is responsible for
providing only user interface to users, where users can be human, another
hardware device or any virtual system which needs to interact with system. This
system holds only presentation logic and must only contain presentation logic
and must not have any other responsibility. For any other responsibilities to
perform they depends upon backend services which handle all business logic
processes and data manipulation tasks. These web Apps communicate over http/https
protocol, preferred way of communication is on https which is secured version
of http - Hyper Text Transfer Protocol.
D.
Session Provider
WebApp uses http as protocol to interact with user to provide
services, but http or https are stateless protocol. When we say stateless that
means WebApp has no way to know if you have requested anything before or not.
You can think of session as managed memory location which allows WebApp to know
what you requested before.
Let’s take example of group discussion in real life when
multiple people are talking to you and you are replying to everyone according
to what they are talking about. This “about” is context which is made up of
more than one statement they have given to you while talking you. To answer
each question, you remember what they have said earlier and what they are
saying now so you can respond accordingly. To remember you need your mind’s
memory so that you know what they said earlier, and this is what session for WebApp.
WebApp keep talking to multiple users and number can range to 0
user to million users. Each request that came to them are transformed in
suitable request object by underlying framework/library, session is also
assigned to requests in this process.
As we can understand that request must be created as fast as
possible because session creation/retrieval is part of that then it should be
as fast as possible. To achieve this we need to store the sessions in fastest
memory of computer, so that it can be created and retrieve as fast as possible.
Computer’s RAM is only fastest memory available in computer, but it has one
drawback that it does not retain the data if system crashes or system gets
power down.
Now these days we have efficient, fast and reliable in memory NoSQL
databases and are widely used for cache management and session
providers, like REDIS or GridGrain etc.
Tips: Using in-memory session provider or other providers decision
should be taken as per requirement, its not universal rule that one must use
In-memory provider or persistent provider. No option is bad, it just that trade
off for speed, security and reliability matters along with other features that
are important.
E.
Service Discovery
Services typically need to call one another. In a monolithic application,
services invoke one another through language-level method or procedure calls.
In a traditional distributed system deployment, services run at fixed, well
known locations (hosts and ports) and so can easily call one another using
HTTP/REST or some RPC mechanism. However, a modern microservice-based
application typically runs in a virtualized or containerized environment where
the number of instances of a service and their locations changes dynamically.
Consequently, you must implement a mechanism for that enables
the clients of service to make requests to a dynamically changing set of
ephemeral service instances.
·
Problem
How does the client of a service - the API
gateway or another service - discover the location of a service instance?
·
Forces
o Each instance of a service
exposes a remote API such as HTTP/REST, or Thrift etc. at a particular location
(host and port)
o The number of services
instances and their locations changes dynamically.
o Virtual machines and
containers are usually assigned dynamic IP addresses.
o The number of services
instances might vary dynamically. For example, an EC2 Autoscaling Group adjusts
the number of instances based on load.
·
Solution
When making a request to a service, the client
makes a request via a router (a.k.a load balancer) that runs at a well known
location. The router queries a service registry, which might be built into the router, and
forwards the request to an available service instance.
For more details please follow this link
![]() |
| Source:Google search |
An API gateway is programming that sits in front of an
application programming interface (API) and acts as a single point of entry for a defined group of microservices. Because a gateway handles protocol translation, this type of
front-end programming is especially useful when clients built with
microservices make use of multiple, disparate APIs.
A major benefit of using API gateways is that they allow developers
to encapsulate the internal structure of an application in multiple ways,
depending upon use case. This is because, in addition to accommodating direct
requests, gateways can be used to invoke multiple back-end services and
aggregate the results.
Because developers must update the API gateway each time a new
microservice is added or removed, it is important that the process for updating
the gateway be as lightweight as possible. Therefore when evaluating API
gateways, it's important for developers to look at features the vendor has
added to differentiate its product from the competition.
In addition to exposing microservices, popular API gateway
features include functions such as:
·
Authentication
·
Security policy enforcement
·
Load balancing
·
Cache management
·
Dependency resolution
·
Contract and service level agreement (SLA) management
For more details please follow this link
For example, we can use Kong API gateway, it’s a NodeJS based
open source gateway which is having multiple useful features. We can use it
here as API gateway and load balancer.
G.
Microservices
This design suggests backend (Business logic and Data Access
logic) should be encapsulated in microservices depending upon problem
statement.
We divide different module or component to smallest service and
that service is called “Microservices”, normally these services are developed
to be REST services, which provide wider spectrum of client accessibility as
almost all devices which has network access to service can send request via
http and get response.
According to this design we have multiple WebApp(s) deployed
under load balancer and they have sole responsibility of UI handling no other
processing, all required business logic processing and data related operations
are handled by microservices.
We have multiple set of microservices configured under load
balancer(s) which act kind of availability zones for application as if one
availability zone dies or get cut off by some reason then other one can be
trusted to deliver same response while suffering one can get treated and come alive
again.
H.
Database - DB
All application needs to store and process data for any request
made to it. Out design shows multiple DBs in “Solution Architecture Diagram”,
following master- slave replication of databases.
There are number of ways that one can use databases in
microservices, one suggestion which most the people follow for microservices is
that “Each microservice must have it own database and must not share it with
other”. It is being followed so widely that it seems like rule for
creating microservices, there is no such rules it all depends on the
requirement of problem statement. If system has such requirement that need such
deployment, then we can and should develop and maintain databases for each service
and all complexities that are induced must be handled properly.
There are different types of databases and each database offer
specific feature very well and have proven benchmark for certain type of use
case. One database can give you very robust transactions for data integrity and
other can provide you flexible schema to store records. There are database
which can be configured to give you different feature with trade off on feature
like MySQL. Configuration
Each web application, microservices has its own set of
environment related configuration and need them for providing services as per
the requirement and and these configuration need to be persisted somewhere
(either in file or in database) so that when application need to start or
restart then these configuration can be accessed with ease.
There are number of ways this can be implemented and most widely
used ways are
·
Configuration Service, this service can be either REST based or
soap based exposed via tcp protocol for faster access as its clients are
limited and defined.
·
Configuration Library, this library are generic library which
are directly linked with its host service/application and provide a means of
managing configurations
In microservice environment this design will recommend you to
have configuration service if possible and can fetch configuration from
database but again this largely depend on requirement how it should be.
I.
Caching
In computing, a cache is a high-speed data storage layer which
stores a subset of data, typically transient in nature, so that future requests
for that data are served up faster than is possible by accessing the data’s
primary storage location. Caching allows you to efficiently reuse previously retrieved
or computed data.
The
data in a cache is generally stored in fast access hardware such as RAM
(Random-access memory) and may also be used in correlation with a software
component. A cache's primary purpose is to increase data retrieval performance
by reducing the need to access the underlying slower storage layer.
Trading off capacity for speed, a cache typically stores a
subset of data transiently, in contrast to databases whose data is usually
complete and durable. We normally keep those records which are of static in
nature or which are not frequent changing, because we do so, then it will add
more cost as we may have to keep updating database and cache frequently more
CPU and more memory for each request as we are hitting not just database but cache
also.
We have in-memory database like REDIS to be used for such
requirement, we should analyse requirement and then choose cache management
strategy.
J.
Logging
Logging is very important part of any application it can tell
what application is doing now and what happened at what time in past. We can
develop application/services to have extensive logging and then configure the
level of log that should be generated, so if at times we need to have detailed
logging to solve or observe application behaviour then we can use logging.
Each development environment has its set of options for logging
for NodeJS we can use “Winston” logging package and it support console, file
and api logging.
K.
Security
This component provides encryption, encoding and other such
functionality to all the web applications and services. This is used while we
are dealing with sensitive information which needs to be hidden from public
world and need treatment even before storing it in secure database. Only
intended person should be able to get what he/she has or given ownership of.
While said that, application and services should be running on
secured protocol as per requirement.
We can use JWT for authentication of user requests and authorize
him/her. Authentication service will be responsible for authenticating the user
and generating Jwt, sending it to user. User will send that jwt in each request
header for further interaction.
L.
Exception Handling
Exception handling is one of foundation pillar of any
application which is expected to be of
·
Good performance
·
Robust
·
Developer friendly
·
Maintainable
·
Scalable
And list goes on.
This design suggest that exception handling should be planned
and standardized for whole application, error code must be defined and
documented with their reason of happening and how/what a user should do to
correct them if that applies to them, so that they can be available to not only
to developer but also to the end users if they ever need to know about them,
think of a case that you developed an application which has certain integration
service. Now client wants to integrate third party application and while
interacting with they get errors, they should be able to understand what is
happening and why.
Each technology stack has its own way of handling the exceptions
and reacting to it, Following are some basic rule of thumb for exception
handling
·
If your functions are named well, using verbs (actions) and
nouns (stuff to take action on) then throw an exception if your method can't do
what it says it can. For example, SaveTransaction(). If it can't save
the Transaction - it can't do what it promised - then throw an
exception. That might be for a number of reasons.
·
Never duplicate exception, means you have defined an exception
not sending parameter in method as argument in one module and now you need to
throw exception for same scenario in other module then don’t create another
exception type but use same one and for reason that exception handling
library/framework should be common to all application component and it is kept
in cross cutting concerns.
·
There are reasons to swallow exceptions (catch (Exception ex))
but they are few and far between and they should be logged if appropriate and
documented liberally. Remember always if you do
catch an exception and intend to rethrow it, then use throw; not throw ex; lest
you lose your call stack and good bits of context.
·
Create a global error handler that logs
everything.
M.
Communication
This design suggests us to develop multiple components having
separate responsibility to perform and manage. Now they need to work together
to achieve same services for which this whole solution is designed, and work
together they need to communicate, they need to be able to send
information/data to another component and receive response in reliable manner.
To make all these components, able to communicate each
other in standard way so that communication is reliable, secure and provide
required efficiency we need to have a standard generic library which should be
easy to integrate and use. Below are some points which should be kept in mind
while developing any library/framework for any solution (if tech stack allows)
·
Resiliency
There may be dozens of even hundreds of instances of any
microservices. An instance can fail for any number of reasons. There can be
node-level failure, such as hardware failure or a VM reboot. An instance might crash,
or be overwhelmed with requests and unable to process any new requests. Any of
these events can cause a network call to fail. There are two design patterns
that can help make services-to-service network calls more resilient:
·
Retry
A network call may fail because of a transient fault that goes
away by itself. Rather than fail outright, the caller should typically retry
the operation a certain number of times, or until a configured time-out period elapse.
However, if an operation is not idempotent, retries can cause unintended side
effects. The original call might succeed, but the caller never gets a response.
If the caller retries, the operation may be invoked twice. Generally, it’s not
safe to retry POST or PATCH methods, because these are not guaranteed to be
idempotent.
·
Circuit Breaker
Too many failed requests can cause a bottleneck, as pending
request accumulate in queue. These blocked requests might hold critical system
resources such as memory, threads, database connections , and so on , which can
cause cascading failures. The circuit Breaker pattern can prevent a service
from repeatedly trying an operation that is likely to fail.
·
Load Balancing
When service “A” calls Service “B” then it should reach to a
healthy service “B” to serve the response.
·
Distributed tracing
A single transaction may span multiple services. That can make
it hard to monitor the overall performance and health of the system. Even if
every service generated logs and metrics, without some way to tie them
together, they are of limited use.
·
Service versioning
When a team deploys a new version of a service, they must avoid
breaking any other services or external clients that depend on it. In addition,
you might want to run multiple versions of a service side-by-side, and route
requests to a particular
·
TLS encryption and mutual TLS authentication
For security reasons, you may want to encrypt traffic between
services with TLS and use mutual TLS authentication to authenticate callers.
There are two ways to communicate among services
·
Synchronous communication
Normally services communicate using request- response manner
which is called synchronous communication. It is most widely used way of
communication. Client get response within acceptable timeframe and if not then
request is aborted and client may try again.
·
Asynchronous Communication
While working with microservices we cannot always expect to get
response within milliseconds or minutes sometimes processing can take time and
that time can be from minutes to hours, for such scenarios we have option to go
for “Asynchronous Communication”. According to this concept, a client sends
request to another service or application and instead of final response it gets
acknowledgement or request received or kind of promise to get a final response.
This client does not get blocked by keep waiting for the response and keeping
network connection to that service/application alive.
Queue can be used to handle request when we are using event-based
communication. Event based communication are done in Publisher- Subscriber
environment where one service subscribe another service to listen to event
which are send in event bus (implemented via queue based solution like “RabbitMQ”,”
Azure Service Bus”, Database can always act as one) But we need to make sure
that queue system we are using, is matching the requirement in hand. Always
remember that nothing is infinite and has cost and availability to a limited
extent.
For
more details on communication please follow below links


Comments
Post a Comment