Microservices with Clojure - Anuj Kumar - E-Book

Microservices with Clojure E-Book

Anuj Kumar

0,0
35,99 €

-100%
Sammeln Sie Punkte in unserem Gutscheinprogramm und kaufen Sie E-Books und Hörbücher mit bis zu 100% Rabatt.

Mehr erfahren.
Beschreibung

The common patterns and practices of the microservice architecture and their application using the Clojure programming language.

Key Features

  • Relevance of the microservice architecture and benefits of Clojure's functional and simple features to implement it.
  • Learn best practices and common principles to avoid common pitfalls while developing microservices.
  • Learn how to use Pedestal to build your next microservices, secure them using JWT, and monitor them using the ELK stack

Book Description

The microservice architecture is sweeping the world as the de facto pattern with which to design and build scalable, easy-tomaintain web applications. This book will teach you common patterns and practices, and will show you how to apply these using the Clojure programming language.

This book will teach you the fundamental concepts of architectural design and RESTful communication, and show you patterns that provide manageable code that is supportable in development and at scale in production. We will provide you with examples of how to put these concepts and patterns into practice with Clojure. This book will explain and illustrate, with practical examples, how teams of all sizes can start solving problems with microservices.

You will learn the importance of writing code that is asynchronous and non-blocking and how Pedestal helps us do this. Later, the book explains how to build Reactive microservices in Clojure that adhere to the principles underlying the Reactive Manifesto. We finish off by showing you various ways to monitor, test, and secure your microservices. By the end, you will be fully capable of setting up, modifying, and deploying a microservice with Clojure and Pedestal.

What you will learn

  • Explore the pros and cons of monolithic and microservice architectures
  • Use Clojure to effectively build a reallife application using Microservices
  • Gain practical knowledge of the Clojure Pedestal framework and how to use it to build Microservices
  • Explore various persistence patterns and learn how to use Apache Kafka to build event-driven microservice architectures
  • Secure your Microservices using JWT
  • Monitor Microservices at scale using the ELK stack
  • Deploy Microservices at scale using container orchestration platforms such as Kubernetes

Who this book is for

You should have a working knowledge of programming in Clojure. However, no knowledge of RESTful architecture, microservices, or web services is expected. If you are looking to apply techniques to your own projects, taking your first steps into microservice architecture, this book is for you.

Anuj Kumar is the co-founder and chief architect of FORMCEPT, a data analytics startup based in Bangalore, India. He has more than 10 years of experience in designing large-scale distributed systems for storage, retrieval, and analytics. He has been in industry hacking, mainly in the area of data integration, data quality, and data analytics using NLP and machine learning techniques. He has published research papers at ACM conferences, got a few patents granted, and has spoken at TEDx. Prior to FORMCEPT, he has worked with the Oracle Server Technologies division in Bangalore, India.

Sie lesen das E-Book in den Legimi-Apps auf:

Android
iOS
von Legimi
zertifizierten E-Readern

Seitenzahl: 338

Veröffentlichungsjahr: 2018

Bewertungen
0,0
0
0
0
0
0
Mehr Informationen
Mehr Informationen
Legimi prüft nicht, ob Rezensionen von Nutzern stammen, die den betreffenden Titel tatsächlich gekauft oder gelesen/gehört haben. Wir entfernen aber gefälschte Rezensionen.



Microservices with Clojure

 

 

Develop event-driven, scalable, and reactive microservices with real-time monitoring

 

 

 

 

 

 

 

 

 

 

 

 

Anuj Kumar

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BIRMINGHAM - MUMBAI

Microservices with Clojure

Copyright © 2018 Packt Publishing

All rights reserved. No part of this book may be reproduced, stored in a retrieval system, or transmitted in any form or by any means, without the prior written permission of the publisher, except in the case of brief quotations embedded in critical articles or reviews.

Every effort has been made in the preparation of this book to ensure the accuracy of the information presented. However, the information contained in this book is sold without warranty, either express or implied. Neither the author, nor Packt Publishing or its dealers and distributors, will be held liable for any damages caused or alleged to have been caused directly or indirectly by this book.

Packt Publishing has endeavored to provide trademark information about all of the companies and products mentioned in this book by the appropriate use of capitals. However, Packt Publishing cannot guarantee the accuracy of this information.

Commissioning Editor: Richa TripathiAcquisition Editor: Aiswarya NarayananContent Development Editor: Akshada IyerTechnical Editor: Abhishek SharmaCopy Editor: Safis EditingProject Coordinator: Prajakta NaikProofreader: Safis EditingIndexer: Francy PuthiryGraphics: Jason MonteiroProduction Coordinator: Deepika Naik

First published: January 2018

Production reference: 1230118

Published by Packt Publishing Ltd. Livery Place 35 Livery Street Birmingham B3 2PB, UK.

ISBN 978-1-78862-224-0

www.packtpub.com

To my mother, Mrs. Indu Srivastava, my father, Mr. Dilip Kumar, and to my lovely wife, Aishwarya, for their continuous support and encouragement. All the time that I have spent on this book should have been spent with them. For trips that we canceled and for weekends that I spent at my desk.To my family, teachers, and colleagues. They have extended their continuous support, provided critical feedback, and made it possible for me to focus on this book.
mapt.io

Mapt is an online digital library that gives you full access to over 5,000 books and videos, as well as industry leading tools to help you plan your personal development and advance your career. For more information, please visit our website.

Why subscribe?

Spend less time learning and more time coding with practical eBooks and Videos from over 4,000 industry professionals

Improve your learning with Skill Plans built especially for you

Get a free eBook or video every month

Mapt is fully searchable

Copy and paste, print, and bookmark content

PacktPub.com

Did you know that Packt offers eBook versions of every book published, with PDF and ePub files available? You can upgrade to the eBook version at www.PacktPub.com and as a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at [email protected] for more details.

At www.PacktPub.com, you can also read a collection of free technical articles, sign up for a range of free newsletters, and receive exclusive discounts and offers on Packt books and eBooks.

Contributors

About the author

Anuj Kumar is the co-founder and chief architect of FORMCEPT, a data analytics startup based in Bangalore, India. He has more than 10 years of experience in designing large-scale distributed systems for storage, retrieval, and analytics.

He has been in industry hacking, mainly in the area of data integration, data quality, and data analytics using NLP and machine learning techniques. He has published research papers at ACM conferences, got a few patents granted, and has spoken at TEDx.

Prior to FORMCEPT, he has worked with the Oracle Server Technologies division in Bangalore, India.

I would like to thank my technical reviewer, Michael Vitz, for his valuable feedback and the Packt editorial team for an excellent feedback loop to come up with good quality content. I would also like to thank my teachers and FORMCEPT team members, who have helped me on various topics covered in this book. And especially, I would like to thank my parents, my wife, and my entire family for their continuous encouragement.

About the reviewer

Michael Vitz has many years of experience building and maintaining software for the JVM. Currently, his main interests include microservice and cloud architectures, DevOps, the Spring Framework, and Clojure.

As a senior consultant for software architecture and engineering at INNOQ, he helps clients by building well-crafted and value-providing software.

He also is the writer of a column in the German magazine, JavaSPEKTRUM, where he publishes articles about JVM, infrastructure, and architectural topics every two months.

 

Packt is searching for authors like you

If you're interested in becoming an author for Packt, please visit authors.packtpub.com and apply today. We have worked with thousands of developers and tech professionals, just like you, to help them share their insight with the global tech community. You can make a general application, apply for a specific hot topic that we are recruiting an author for, or submit your own idea.

Table of Contents

Preface

Who this book is for

What this book covers

To get the most out of this book

Download the example code files

Conventions used

Get in touch

Reviews

Monolithic Versus Microservices

Dawn of application architecture

Monolithic architecture

Microservices

Data management

When to use what

Monolithic applications to microservices

Identifying candidates for microservices

Release cycle and the deployment process

Summary

Microservices Architecture

Domain-driven design

Bounded context

Identifying bounded contexts

Organizing around bounded contexts

Components

Hexagonal architecture

Messaging and contracts

Direct messaging

Observer model

Service contracts

Service discovery

Service registry

Service discovery patterns

Data management

Direct lookup

Asynchronous events

Combining data

Transactions

Automated continuous deployment

CI/CD

Scaling

Summary

Microservices for Helping Hands Application

Design

Users and entities

User stories

Domain model

Monolithic architecture

Application components

Deployment

Limitations

Moving to microservices

Isolating services by persistence

Isolating services by business logic

Messaging and events

Extensibility

Workflows for Helping Hands

Service provider workflow

Service workflow

Service consumer workflow

Order workflow

Summary

Development Environment

Clojure and REPL

History of Clojure

REPL

Clojure build tools

Leiningen

Boot

Clojure project

Configuring a project

Running a project

Running tests

Generating reports

Generating artifacts

Clojure IDE

Summary

REST APIs for Microservices

Introducing REST

RESTful APIs

Status codes

Naming conventions

Using RESTful APIs via cURL

REST APIs for Helping Hands

Consumer and Provider APIs

Service and Order APIs

Summary

Introduction to Pedestal

Pedestal concepts

Interceptors

The interceptor chain

Importance of a Context Map

Creating a Pedestal service

Using interceptors and handlers

Creating routes

Declaring routers

Accessing request parameters

Creating interceptors

Handling errors and exceptions

Logging

Publishing operational metrics

Using chain providers

Using server-sent events (SSE) 

Creating interceptors for SSE

Using WebSockets

Using WebSocket with Pedestal and Jetty

Summary

Achieving Immutability with Datomic

Datomic architecture

Datomic versus traditional database

Development model

Data model

Schema

Using Datomic

Getting started with Datomic

Connecting to a database

Transacting data

Using Datalog to query

Achieving immutability

Deleting a database

Summary

Building Microservices for Helping Hands

Implementing Hexagonal Architecture

Designing the interceptor chain and context

Creating a Pedestal project

Defining generic interceptors

Interceptor for Auth

Interceptor for the data model

Interceptor for events

Creating a microservice for Service Consumer

Adding routes

Defining the Datomic schema

Creating a persistence adapter

Creating interceptors

Testing routes

Creating a microservice for Service Provider

Adding routes

Defining Datomic schema

Creating a persistence adapter

Creating interceptors

Testing routes

Creating a microservice for Services

Adding routes

Defining a Datomic schema

Creating a persistence adapter

Creating interceptors

Testing routes

Creating a microservice for Order

Adding routes

Defining Datomic schema

Creating a persistence adapter

Creating interceptors

Testing routes

Creating a microservice for Lookup

Defining the Elasticsearch index

Creating query interceptors

Using geo queries

Getting status with aggregation queries

Creating a microservice for alerts

Adding routes

Creating an email interceptor using Postal

Summary

Configuring Microservices

Configuration principles

Defining configuration parameters

Using configuration parameters

Using Omniconf for configuration

Enabling Omniconf

Integrating with Helping Hands

Managing application states with mount

Enabling mount

Integrating with Helping Hands

Summary

Event-Driven Patterns for Microservices

Implementing event-driven patterns

Event sourcing

Using the CQRS pattern

Introduction to Apache Kafka

Design principles

Getting Kafka

Using Kafka as a messaging system

Using Kafka as an event store

Using Kafka for Helping Hands

Using Kafka APIs

Initializing Kafka with Mount

Integrating the Alert Service with Kafka

Using Avro for data transfer

Summary

Deploying and Monitoring Secured Microservices

Enabling authentication and authorization

Introducing Tokens and JWT

Creating an Auth service for Helping Hands

Using a Nimbus JOSE JWT library for Tokens

Creating a secret key for JSON Web Encryption

Creating Tokens

Enabling users and roles for authorization

Creating Auth APIs using Pedestal

Monitoring microservices

Using ELK Stack for monitoring

Setting up Elasticsearch

Setting up Kibana

Setting up Logstash

Using ELK Stack with Collectd

Logging and monitoring guidelines

Deploying microservices at scale

Introducing Containers and Docker

Setting up Docker

Creating a Docker image for Helping Hands

Introducing Kubernetes

Getting started with Kubernetes

Summary

Other Books You May Enjoy

Leave a review - let other readers know what you think

Preface

The microservice architecture is sweeping the world as the de facto pattern for building scalable and easy-to-maintain web-based applications. This book will teach you common patterns and practices, showing you how to apply them using the Clojure programming language. It will teach you the fundamental concepts of architectural design and RESTful communication, and show you patterns that provide manageable code that is supportable in development and at scale in production. This book will provide you with examples of how to put these concepts and patterns into practice with Clojure.

Whether you are planning a new application or working on an existing monolith, this book will explain and illustrate with practical examples how teams of all sizes can start solving problems with microservices. You will understand the importance of writing code that is asynchronous and non-blocking, and how Pedestal helps us do this. Later, the book explains how to build Reactive microservices in Clojure, which adhere to the principles underlying the Reactive Manifesto. We finish off by showing you various techniques to monitor, test, and secure your microservices. By the end, you will be fully capable of setting up, modifying, and deploying a microservice with Clojure and Pedestal.

Who this book is for

If you are looking forward to migrate your existing monolithic applications to microservices or taking your first steps into microservice architecture, then this book is for you. You should have a working knowledge of programming in Clojure. However, no knowledge of RESTful architecture, microservices, or web services is expected.

What this book covers

Chapter 1, Monolithic Versus Microservices, introduces monolithic and microservice architecture and discusses when to use what. It also covers the possible migration plans of moving from monolithic applications to microservices.

Chapter 2, Microservices Architecture, covers the basic building blocks of microservice architecture and its related features. It discusses how to set up messaging and contracts, and manage data flows among microservices.

Chapter 3, Microservices for Helping Hands Application, introduces a sample Helping Hands application and describes the steps that will be taken in the rest of the book to build the application using microservices. Further, the chapter compares and contrasts the benefits of using a microservices-based architecture compared with a monolithic one.

Chapter 4, Development Environment, covers Clojure and REPL at a high level and introduces the concepts of Leiningen and Boot—the two major build tools for any Clojure project. The emphasis will be on Leiningen with a basic introduction to Boot on how to set up a Clojure project for implementing microservices.

Chapter 5, REST APIs for Microservices, covers the basics of the REST architectural style, various HTTP methods, when to use what, and how to give meaningful names to RESTful APIs of microservices. It also covers the naming conventions for REST APIs using the Helping Hands application as an example.

Chapter 6, Introduction to Pedestal, covers the Clojure Pedestal framework in detail with all the relevant features provided by Pedestal, including interceptors and handlers, routes, WebSockets, server-sent events, and chain providers.

Chapter 7, Achieving Immutability with Datomic, gives an overview of the Datomic database along with its architecture, data model, transactions, and Datalog query language.

Chapter 8, Building Microservices for Helping Hands, is a step-by-step, hands-on guide to build and test microservices for the Helping Hands application using the Pedestal framework.

Chapter 9, Configuring Microservices, covers the basics of microservices configuration and discusses how to create configurable microservices using frameworks such as Omniconf. It also explains the steps to manage the application state effectively using available state-management frameworks such as Mount.

Chapter 10, Event-Driven Patterns for Microservices, covers the basics of event-driven architectures and shows how to use Apache Kafka as a messaging system and event store. Further, it discusses how to use Apache Kafka brokers and set up consumer groups for the effective coordination of microservices.

Chapter 11, Deploying and Monitoring Secured Microservices, covers the basics of microservices authentication using JWT and how to set up a real-time monitoring system using the ELK Stack. It also explains the basic concepts of containers and orchestration frameworks such as Kubernetes.

To get the most out of this book

The Java Development Kit (JDK) is required to run and develop applications using Clojure. You can get the JDK from http://www.oracle.com/technetwork/java/javase/downloads/index.html. It is also recommended that you use a text editor or an integrated development environment (IDE) of your choice for implementation. Some of the examples in the chapters require Linux as an operating system.

Download the example code files

You can download the example code files for this book from your account at www.packtpub.com. If you purchased this book elsewhere, you can visit www.packtpub.com/support and register to have the files emailed directly to you.

You can download the code files by following these steps:

Log in or register at

www.packtpub.com

.

Select the

SUPPORT

tab.

Click on

Code Downloads & Errata

.

Enter the name of the book in the

Search

box and follow the onscreen instructions.

Once the file is downloaded, please make sure that you unzip or extract the folder using the latest version of:

WinRAR/7-Zip for Windows

Zipeg/iZip/UnRarX for Mac

7-Zip/PeaZip for Linux

The code bundle for the book is also hosted on GitHub at https://github.com/PacktPublishing/Microservices-with-Clojure. We also have other code bundles from our rich catalog of books and videos available athttps://github.com/PacktPublishing/. Check them out!

Conventions used

There are a number of text conventions used throughout this book.

CodeInText: Indicates code words in text, database table names, folder names, filenames, file extensions, pathnames, dummy URLs, user input, and Twitter handles. Here is an example: "The persistence protocol ServiceDB consists of upsert, entity, and delete functions."

A block of code is set as follows:

{ "query": { "term": { "status": "O" } }}

When we wish to draw your attention to a particular part of a code block, the relevant lines or items are set in bold:

(defn home-page [request]

(log/counter ::home-hits 1)

(ring-resp/response "Hello World!"))

Any command-line input or output is written as follows:

% lein run

Noname | Hello, World!

% lein run Clojure

Clojure | Hello, World!

Bold: Indicates a new term, an important word, or words that you see onscreen. For example, words in menus or dialog boxes appear in the text like this. Here is an example: "Click on the Create a visualization button."

Warnings or important notes appear like this.
Tips and tricks appear like this.

Get in touch

Feedback from our readers is always welcome.

General feedback: Email [email protected] and mention the book title in the subject of your message. If you have questions about any aspect of this book, please email us at [email protected].

Errata: Although we have taken every care to ensure the accuracy of our content, mistakes do happen. If you have found a mistake in this book, we would be grateful if you would report this to us. Please visit www.packtpub.com/submit-errata, selecting your book, clicking on the Errata Submission Form link, and entering the details.

Piracy: If you come across any illegal copies of our works in any form on the Internet, we would be grateful if you would provide us with the location address or website name. Please contact us at [email protected] with a link to the material.

If you are interested in becoming an author: If there is a topic that you have expertise in and you are interested in either writing or contributing to a book, please visit authors.packtpub.com.

Reviews

Please leave a review. Once you have read and used this book, why not leave a review on the site that you purchased it from? Potential readers can then see and use your unbiased opinion to make purchase decisions, we at Packt can understand what you think about our products, and our authors can see your feedback on their book. Thank you!

For more information about Packt, please visit packtpub.com.

Monolithic Versus Microservices

"The old order changeth yielding place to new"

- Alfred Tennyson

A well-designed monolithic architecture has been the key to many successful software applications. However, microservices-based applications are gaining popularity in the age of the internet due to their inherent property of being autonomous and flexible, their ability to scale independently, and their shorter release cycles. In this chapter, you will:

Learn about the basics of monolithic and microservices architectures

Understand the monolithic-first approach and when to start using microservices

Learn how to migrate an existing monolithic application to microservices

Compare and contrast the release cycle and deployment methodology of monolithic and microservices-based applications

Dawn of application architecture

Ever since Ada Lovelace (https://en.wikipedia.org/wiki/Ada_Lovelace) wrote the first algorithm for Analytical Engine (https://en.wikipedia.org/wiki/Analytical_Engine) in the 19th century and Alan Turing (https://en.wikipedia.org/wiki/Alan_Turing) formalized the concepts of algorithm and computation via the Turing machine (https://en.wikipedia.org/wiki/Turing_machine), software has gone through multiple phases in its evolution, both in terms of how it is designed and how it is made available to its end users. The earlier software was designed to run on a single machine in a single environment, and was delivered to its end users as an isolated standalone entity. In the early 1990s, as the focus shifted to application software, the industry started exploring various software architecture methodologies to meet the demands of changing requirements and underlying environments. One of the software architectures that was widely adopted was multitier architecture, which clearly separated the functions of data management, business logic, and presentation. When these layers were packaged together in a single application, using a single technology stack, running as a single program, it was called a monolithic architecture, still in use today.

With the advent of the internet, software started getting offered as a service over the web. With this change in deployment and usage, it started becoming hard to upgrade and add features to software that adopted a monolithic architecture. Technology started changing rapidly and so did programming languages, databases, and underlying hardware. Companies that were able to disintegrate their monolithic applications into loosely-coupled services that could talk to each other were able to offer better services, better integration points, and better performance to their users. They were not only able to upgrade to the latest technology and hardware, but also able to offer new features and services faster to their users. The idea of disintegrating a monolithic application into loosely-coupled services that can be developed, deployed, and scaled independently and can talk to other services over a lightweight protocol, was called microservices-based architecture (https://en.wikipedia.org/wiki/Microservices).

Companies such as Netflix, Amazon, and so on have all adopted a microservices-based architecture. If you look at Google Trends in the preceding screenshot, you can see that the popularity of microservices is rising day by day, but this doesn't mean that monolithic applications are obsolete. There are applications that are still suited for monolithic architecture. Microservices have their advantages, but at the same time they are hard to deploy, scale, and monitor. In this chapter, we will look at both monolithic and microservices-based architectures. We will discuss when to use what and also talk about when and how to migrate from a monolithic to a microservices-based architecture.

Monolithic architecture

Monolithic architecture is an all-in-one methodology that encapsulates all the required services as a single deployable artifact. It works on a single technology stack and is deployed and scaled as a single unit. Since there is only one technology stack to master, it is easy to deploy, scale, and set up a monitoring infrastructure for monolithic applications. Each team member works on one or more components of the system and follows the design principle of Separation of Concerns (SoC) (https://en.wikipedia.org/wiki/Separation_of_concerns). Such applications are also easier to refactor, debug, and test in a single standalone development environment.

Applications based on monolithic architecture may consist of one or more deployable artifacts that are all deployed at the same time. Such a monolithic architecture is often referred to as a Distributed Monolith.

For example, a very common monolithic application is a word processing application; Microsoft Word is installed via a single deployable artifact and is entirely built on Microsoft .NET Framework (https://www.microsoft.com/net/). There are various components within word processing application, such as templates, import/export, spell-checker, and so on, that work together to help create a document and export it the format of choice.

Monolithic architecture applies not only to standalone applications, but also to client-server based applications that are provided as a service over the web. Such client-server based applications have a clearly defined multitier architecture that provides the relevant services to its end users via a user interface.

The user interface talks to application endpoints that can be programmed using well-defined interfaces.

A typical client-server application may adopt a three-tier architecture to separate the presentation, business logic, and persistence layer from each other, as shown in the preceding diagram. Components of each layer talk strictly to the components of the layer below them. For example, the components of the presentation layer may never talk to the persistence layer directly. If they need access to data, the request will be routed via the business logic layer that will not only move the data between the persistence layer and the presentation layer, but also do the required processing to serve the request. Adopting such a component-based layered architecture also helps in isolating the effect of change to only the components of dependent layers instead of the entire application. For example, changes to the components of the business logic layer may require a change in the dependent components of the presentation layer but components of the persistence layer may remain intact.

Even though a monolithic application is built on SoC, it is still a single application on a single technology stack that provides all required services to its users. Any change to such an application requires to be compatible with all the encapsulated services and underlying technology stack. In addition to that, it is not possible to scale each service independently. Any scaling requirement is met by deploying multiple instances of the entire system as a single unit. A team working on such a monolithic application scales over time and has to adapt to newer technologies as a whole, which is often challenging due to the rapidly changing technology landscape. If they do not change with the technology, the entire software becomes obsolete over time and is discarded due to incompatibility with newer software and hardware, or a shortage of talent.

Microservices

Microservices are a functional approach well applied to software. It tries to decompose the entire application functionally into a set of services that can be deployed and scaled independently. Each service does only one job and does it well. It has its own database, decides its own schema, and provides access to datasets and services through well-defined application programming interfaces that are better known as APIs, often paired with a user interface. APIs follow a set communication protocols, but services are free to choose their own technology stack and can be deployed on hardware of choice.

In a microservice environment, as shown in the preceding diagram, there are no layers like in monoliths; instead, each service is organized around a bounded context (https://en.wikipedia.org/wiki/Domain-driven_design#Bounded_context) that adds a business capability to the application as a whole. New capabilities in such an application are added as new services that are deployed and scaled independently. Each user request in a microservices-based application may call one or more internal microservice to retrieve data, process it, and generate the required response, as shown in the following diagram. Such software evolves faster and has low technology debt. They do not get married to a particular technology stack and can adopt a new technology faster:

Data management

In a microservices-based application, databases are isolated for each business capability and are managed by only one service at a time. Any request that needs access to the data managed by another service strictly uses the APIs provided by the service managing the database. This makes it possible to not only use the best database technology available to manage the business capability, but also to isolate the technology debt to the service managing it. However, it is recommended for the calling service to cache responses over time to avoid tight coupling with the target service and reduce the network overhead of each API call.

For example, a service managing user interests might use a graph database (https://en.wikipedia.org/wiki/Graph_database) to build a network of users, whereas a service managing user transactions might use a relational database (https://en.wikipedia.org/wiki/Relational_database) due to its inherent ACID (https://en.wikipedia.org/wiki/ACID) properties that are suitable for transactions. The dependent service only needs to know the APIs to connect to the service for data and not the technology of the underlying database.

This is contrary to a monolithic layered architecture, where databases are organized by business capability, which may be accessed by one or more persistence modules based on the request. If the underlying database is using a different technology, then each of the modules accessing the databases have to comply with the same technology, thus inheriting the complexity of each database technology that it has access to.

Database isolation should be done at the database level and not at the database technology level. Avoid deploying multiple instances of the same relational database or graph database as much as possible. Instead, try to scale them on demand and use the isolation capability of these systems to maintain separate databases within them for each service.

The concept of microservices is very similar to a well-known architecture called service-oriented architecture (SOA) (https://en.wikipedia.org/wiki/Service-oriented_architecture). In microservices, the focus is on identifying the right bounded context and keeping the microservices as lightweight as possible. Instead of using a complex message-oriented middleware (https://en.wikipedia.org/wiki/Message-oriented_middleware) such as ESB (https://en.wikipedia.org/wiki/Enterprise_service_bus), a simple mode of communication is used that is often just HTTP.

"Architectural Style [of Microservices] is referred to as fine-grained SOA, perhaps service orientation done right"

- Martin Fowler on microservices

When to use what

The monolithic layered architecture is one of the most common architectures in use across the software industry. Monolithic architectures are well suited for transaction-oriented enterprise applications that have well-defined features, change less often, and have complex business models. For such applications, transactions and consistency are of prime importance. They require a database technology with built-in support for ACID properties to store transactions. On the other hand, microservices are suited better for Software-as-a-Service, internet-scale applications that are feature-first applications with each feature focused on a single business capability. Such applications change rapidly and are scaled partially per business capability on demand. Transactions and consistency in such applications are hard to achieve due to multiple services, as compared to monoliths that are implemented as single applications.

It is recommended to start with a well designed, modular monolithic application irrespective of the domain complexity or transactional nature. Generally, all applications start as a monolithic application that can be deployed faster as a single artifact and later split into microservices when the application's complexity begins to outweigh the productivity of theteam.

The productivity of the team may start decreasing when changes to the monolithic application start affecting more than one component, as shown in the preceding diagram. These changes may be a result of a new feature being added to the application, a database technology upgrade, or the refactoring of existing components. Any changes made to the application must keep the entire team in-sync, especially the deployment team, if there are any changes required in the deployment processes. Communicating such changes in a large team often results in a coordination nightmare, multiple change requests, and in-turn, reduces the overall productivity of the team working on the application.

Productivity also depends on the initial choices made with respect to the technology stack and its flexibility of implementation. For example, if a new feature requires a library that is readily available with a different technology stack or a programming language, it becomes challenging to adopt as it does not conform to the existing technology stack of the application components. In such cases, the team ends up implementing the same feature set for the current technology stack from scratch, and that in turn reduces productivity and further adds to the technology debt.

Before starting with microservices, first set up best design principles among team members. Next, try to evaluate the existing monolith with regard to components and their interaction. If refactoring can help reduce the dependency between the components, do that first instead of disintegrating your application into microservices.

Monolithic applications to microservices

Most applications start as a monolith. Amazon (http://highscalability.com/amazon-architecture) started with a monolithic Perl (https://en.wikipedia.org/wiki/Perl) /C++ (https://en.wikipedia.org/wiki/C%2B%2B) application, and Twitter (http://highscalability.com/blog/2013/7/8/the-architecture-twitter-uses-to-deal-with-150m-active-users.html) started with a monolithic Rails (https://en.wikipedia.org/wiki/Ruby_on_Rails) application. Both organizations have not only gone through more than three generations of software architectural changes, but have also transformed their organizational structures over time. Today, all of them are running on microservices with teams organized around services that are developed, deployed, scaled, and monitored by the same team independently. They have mastered continuous integration and continuous delivery pipelines with automated deployment, scaling, and monitoring of services for real-time feedback to the team.

Identifying candidates for microservices

The top-most challenge in migrating from a monolithic application to microservices is to identify the right candidates for microservices. A well structured and modularized monolithic application already has well-defined boundaries (bounded contexts) that can help disintegrate the application into microservices. For example, theUser, Orders, and Interest modules already have well-defined boundaries and are good candidates to create microservices for. If the application does not have well-defined boundaries, the first step is to refactor the existing application to create such bounded contexts for microservices. Each bounded context must be tied to a business capability for which a service can be created.

Another approach in identifying the right candidates for microservices is to look at the data access patterns and associated business logic. If the same database is being updated by multiple components of a monolithic application, then it makes sense to create a service for the primary component with associated business logic that manages the database and makes it accessible to other services via APIs. This process can be repeated until databases and the associated business logic are managed by one and only one service that has a small set of responsibilities, modeled around a business capability.

For example, a monolithic application consisting of User, Interest, and Orders components can be migrated into microservices by picking one component at a time and creating a microservice with an isolated database, as shown in the preceding diagram. To start with, first pick the one with the least dependency, the User module, and create the User Service service around it. All other components can now talk to this new User Service for User Management, including authentication, authorization, and getting user profiles. Next, pick the Orders module based on the least dependency logic, and create a service around it. Finally, pick the Interest module as it is dependent on both the User and Orders modules. Since we have the databases isolated, we can also swap out the database for Interest with maybe a graph database that is efficient to store and retrieve user interests due to its inherent capability of storing relationships as a graph.

In addition to organizing your microservices around business capabilities and database access patterns, look for common areas, such as authentication, authorization, and notification, that can be perfected once as a service and can be leveraged by one or more microservices later.

Release cycle and the deployment process

Once a monolithic application is disintegrated into microservices, the next step is to deploy them into production. Monolithic application are mostly deployed as a single artifact (JARs, WARs, EXEs, and more) that are released after extensive testing by the quality assurance (QA) team. Typically, developers work on various components of the application and release versions for the QA team to pick and validate against the specification, as shown under the Org Structure of monolithic architecture in the following diagram. Each iteration may involve the addition or removal of features and bug fixes. The release goes through multiple developers (dev) and QA team iterations until the QA team flags off the release as stable. Once the QA team flags off the release, the released artifact is handed over to the IT ops team to deploy it in production. If there are any issues in production, the IT ops team asks the dev team to fix them. Once the issues are fixed, the dev team tags a new release for QA that again goes through the same dev-QA iterations before being marked as stable and eventually handed over to IT/ops. Due to this process, any release for a monolithic applications may easily take up to a month, often three months.

On the other hand, for microservices, teams are organized into groups that fully own a service. The team is responsible for not only developing the service, but also for putting together automated test cases that can test the entire service against each change submitted for the service. Since the service is to be tested in isolation for its features, it is faster to run entire test suites for the service for each change submitted by the developers. Additionally, the team itself creates deployable binaries often packaged into containers (https://en.wikipedia.org/wiki/Linux_containers), such as Docker (https://en.wikipedia.org/wiki/Docker_(software)), that are published to a central repository from where they can be automatically deployed into production by some well-known tools, such as Kubernetes (https://en.wikipedia.org/wiki/Kubernetes). The entire development to production timeline is cut short to days, often hours, as the entire deployment process is automated. We will learn more about deploying microservices in production and how to use these deployment tools in Part-4, the last part of this book.

There is a reason why a lot of microservice projects fail and only a few succeed. Migrating from a monolithic architecture to microservices must not only focus on identifying the bounded contexts, but also the organizational structure and deployment methodologies. Teams must be organized around services and not projects. Each team must own the service right from development to production. Since each team owns the responsibility for testing, validation, and deployment, the entire process should be automated and the organization must master it. Development and deployment cycles must be short with immediate feedback via fine-grained monitoring of the deployed microservices.

Automation is key for any successful microservices project. Testing, deployment, and monitoring must be automated before moving microservices to production. 

Summary

In this chapter, we learned about monolithic and microservices architectures and why microservices are becoming popular in the industry, especially with web-scale applications. We learned about the importance of database isolation with microservices and how to migrate a monolithic application to microservices by observing the database access pattern. We also discussed the importance of the monolith-first approach and when to move towards microservices. We concluded with a comparison of monolithic and microservices architectures with regard to the release cycle and deployment process.

The next chapter of this book will talk about microservice architecture in detail; we will learn more about domain-driven design and how to identify the right set of microservices. In Chapter 3, Microservices for Helping Hands Application, the last chapter of Part-1, we will pick a real-life use case for microservices and discuss how to design it using the principles of microservice architecture.

Microservices Architecture

"Gather together the things that change for the same reasons. Separate those things that change for different reasons."

- Robert Martin, Single Responsibility Principle

Software architecture plays a key role in identifying the behavior of the system before it is built. A well-designed software architecture leads to flexible, reusable, and scalable components that can be easily extended, verified, and maintained over time. Such architectures evolve over time and help pave the way for the adoption of next-generation architectures. For example, a well-designed monolithic application that is built on the principles of Separation of Concern (SoC) is easier to migrate to microservices than an application that does not have well-defined components. In this chapter, you will:

Learn a systematic approach to designing microservices using the bounded context

Learn how to set up contracts between microservices and isolate failures

Learn how to manage data flows and transactions among microservices

Learn about service discovery and the importance of automated deployment

Domain-driven design

Ideal enterprise systems are tightly integrated and provide all business capabilities as a single unit that is optimized for a particular technology stack and hardware. Such monolithic systems often grow so complex over time that it becomes challenging to comprehend them as a single unit by a single team. Domain-driven design advocates disintegrating such systems into smaller modular components and assigning them to teams that focus on a single business capability in a bounded context (https://en.wikipedia.org/wiki/Domain-driven_design#Bounded_context). Once disintegrated, all such components are made a part of an automated continuous integration (CI) process to avoid any fragmentation. Since these components are built in isolation and often have their own data models and schema, there should be a well-defined contract to interact with the components to coordinate various business activities.

The term Domain-driven design was first coined by Eric J. Evans as the title of his book in 2003. In Part-IV, Evans talks about the bounded context and the importance of continuous integration, which forms the basis of any microservices architecture.

Bounded context