Nowadays, software is not only used for providing services but is also used to develop business. The best-performing software allows a business to grow. Today's application software shouldn't focus on a specific device such as a computer; instead, it should be able to support any platform. As architects, we have to focus on the design of the software application at the time of development. It must be scalable, either vertically or horizontally, and it should be able to cope with millions, or even billions, of requests per second.
In this chapter, we will discuss application architecture patterns, such as the conventional monolithic architecture pattern and the microservice architecture pattern. We will look at where the concept of conventional monolithic architecture comes from and what advantages it has relating to the software development life cycle. We will also explain the limitations of the monolithic architecture, which leads to the need to build loosely-coupled systems, otherwise known as the microservice architecture.
By the end of this chapter, you will be able to understand the traditional approach to building software solutions by adopting a monolithic architecture pattern. You will read about the evolution of the architecture pattern from monolithic to microservice-related. We will also provide a detailed explanation of the microservice architecture and information about how it can be used in this chapter.
This chapter will cover the following topics:
- The monolithic architecture pattern:
- The microservice architecture pattern:
- Service-Oriented Architecture (SOA)
- SOA versus microservice architecture
Let’s get started and look at these topics in detail.
When I started my job at Paytm, an e-commerce and payment gateway company in India, it was a startup company. We began with monolithic application architecture because there was only two of us there at the time. Many startups begin application development by following monolithic application architecture due to the small size of their team. Monolithic architecture doesn't give you big operational overhead costs, and they often have just one massive codebase.
In a server-side application, you have to focus on development to provide support to a variety of different clients, such as desktop browsers, mobile browsers, and native mobile applications, including Android and iOS. A monolithic application must, therefore, have a complete code in order to support a variety of different clients. Let's discuss an example of a monolithic architecture pattern.
Suppose we are working on an e-commerce application that provides an online bookshop portal. It takes orders from customers, verifies the availability of the ordered book, places an order, and ships the ordered book to the customer. To build this application, we have to create several modules.
These include a Shop Front UI module, which provides a user interface to customers, and backend services, such as an Account Service, a Book Service, an Order Service, a Shipping Service, and so on. These services have various responsibilities, including verifying the customer, checking the availability of books, placing an order, and shipping the order.
All of these modules will be deployed as a single monolithic application either as a WAR file or JAR file. A Java web application as a WAR file runs on a web container such as Tomcat. This web application serves all HTTP requests that come from various clients, such as desktop or mobile browsers. The request comes first to Apache or Nginx and then to Tomcat.
You can also create multiple instances of this monolithic application to handle millions or billions of requests, or include a load balancer to scale and improve availability.
The following diagram illustrates the architectural design of a monolithic application:
As you can see in the preceding diagram, all modules of this traditional web application, such as Shop Front UI, Account Service, Order Service, Book Service, and Shipping Service, are single artifacts in the Tomcat container. This monolithic application has a massive codebase that includes all modules. If you introduce new modules to this application, you have to make changes to the existing code and then deploy the artifact with a different code to the Tomcat server.
Server-side application developers can include the following components in the architecture design:
- Presentation: This layer handles HTTP requests and responds with HTML, JSON, or XML
- Business logic: This is the business logic written into services such as the Account Service or Customer Service
- Data access: These objects are responsible for accessing the database for business logic
- Application integration: This component is responsible for integrating the application with other external services via messaging or the REST API
In monolithic application architecture, we place such components as a single artifact in the server-side application. This application architecture means that a new team member can be introduced to the application easily due to the close nature of the team. However, when you have to change the application due to scalability or availability requirements, you have to run multiple copies of the application on multiple machines.
Monolithic application architecture can be made logically modular by dividing it into different layers according to the types of components. All modules and different layers are packaged and deployed as a single artifact monolithic application. Let's now move on and look at the benefits of monolithic application architecture.
The monolithic solution has the following benefits:
- Simple to develop: Monolithic applications are very simple to develop because current development tools and IDEs support the development of monolithic applications
- Simple to test: As we have already discussed, monolithic applications have all of their modules in a single artifact, so you can easily carry out end-to-end testing by simply running the application either manually or with Selenium
- Simple to deploy: A monolithic application is a single artifact, so you can easily deploy it to the server as a WAR file
- Simple to scale: You can easily achieve scaling by copying the single artifact of the application to multiple running machines and setting up a load balancer behind the monolithic application
As you can see, monolithic applications have numerous benefits. They also have several disadvantages, which we will discuss shortly, but let's first have a look at the situations in which monolithic applications are useful.
Monolithic architecture can be used in the following situations:
- In the foundation stage of projects – most of the time, big, successful applications start with monolithic application architecture
- When building an unproven product or proof of concept
- When your development team is less experienced
Despite the benefits of monolithic application architecture, there are also a few disadvantages.
Monolithic application architecture can sometimes have the following disadvantages:
- A monolithic application has a large codebase, which can intimidate developers, especially those who are new to the team. The application can be difficult to understand and modify. As a result, development is typically quite slow.
- The application is large and complex, which makes it difficult to fully understand and make changes quickly and correctly.
- The impact of a change is usually not very well understood, which leads to carrying out extensive, additional manual testing.
- The architecture can be difficult to scale when different modules have conflicting resource requirements.
- Monolithic applications aren't very reliable; a bug in any module can bring down the whole application.
- They are not very adept at adopting new technologies. Since changes in frameworks or languages will affect an entire application, it is extremely expensive both time-wise and cost-wise.
Let’s now discuss which software development processes are better with monolithic architecture.
For a monolithic application, traditional software development processes, such as waterfall processes, are most suitable. This is because a monolithic application usually has large teams working on a single deployment artifact.
As we have discussed, a monolithic application is a single artifact, built as a single unit. This means that it handles the HTTP requests and executes business logic using DAOs to retrieve data from the underlying database at the server-side. However, with a monolithic application, if any changes are made to the application, another version of the entire application will need to be built. Fortunately, this is where the microservice architecture pattern can come to the rescue.
In the previous section, we discussed the monolithic application architecture – in other words, the collection of all modules of an application as a single artifact. There is another architecture pattern that structures an application so that all modules of that application are loosely-coupled, share their services, and are independently deployable. In this approach, each service must be focused on a set of narrowly-related functions and each service runs independently and as a unique process. An application might consist of services, such as the Order Service, the Account Service, and so on. This approach is known as microservice architecture.
Microservice architecture refers to a method of software development in which a large software application is divided into several independently deployable services. These services are small, modular, and follow the single responsibility principle of software development. Each service is treated as a unique process that communicates with each other through a well-defined mechanism. We will discuss this further in Chapter 4, Inter-Service Communication.
In microservice architecture, all services communicate with each other either synchronously, using HTTP or REST, or asynchronously, using AMQP or Kafka. Each service contains its own database. The basic idea behind microservice architecture is to split up your monolithic application into a set of smaller, interconnected services.
A microservice architecture pattern separates concerns on a process level. All processes in this architecture are loosely coupled with each other. They communicate using predefined rules to achieve a business goal. So, let's move on and look at an example of a microservice-based application.
We discussed a monolithic application in the previous section, in the form of an online book shop. In this section, we are going to discuss the same example using microservice architecture. The online bookshop application has four modules; Account Service, Book Inventory Service, Order Service, and Shipping Service. This application also has a user interface application, the Shop Front UI web application, which serves user requests and sends responses.
The following diagram illustrates the architecture of the application, which consists of a set of services:
As you can see, we have divided the earlier monolithic bookshop application into several independent services: Account Service, Book Service, Order Service, and Shipping Service. This is a microservices architectural style; according to this approach, we can develop a single application as a suite of small services.
Each service is built around a business capability and is independently deployable into the server. For example, the Account Service manages the customer’s account and has its own database, Account DB. Similarly, the Book Service manages the inventory of the books and has the database Inventory DB. The Order Service manages the customer’s orders using a separate database, Order DB. Finally, the Shipping Service manages shipping orders using the Shipping DB. Each module has its own dependency without depending on other services. This means that we can choose different technologies as well as develop separate services.
The server-side application must handle requests coming from various clients, such as desktop or mobile browsers and native mobile apps. The Shop Front UI web application handles the HTTP requests that come from browsers. Some APIs are exposed to the native mobile app, so the app doesn't call these APIs directly – instead, the app communicates through an intermediary known as an API Gateway. The API Gateway is responsible for handling these requests using load balancing. The API Gateway also offers caching, access control, and API monitoring.
We'll discuss the benefits of the microservice architecture pattern in the following section.
The following are some of the benefits of the microservice architecture pattern:
- Easy to maintain: It is very easy for the developer to understand; this way, you can start development faster, which in turn makes it more productive
- Easy to scale: You can easily scale individual components
- Technology diversity: Microservices allow you to mix libraries, frameworks, data storage, and languages
- Fault isolation: Component failure should not bring the whole system down
- Better support: The architecture is suitable for smaller, parallel teams
- Independent deployment: We can easily deploy each microservice independently without impacting other microservices in the architecture
Microservice-based architecture also has a few disadvantages, however, and we'll take a look at them in the following section.
Microservices provide several benefits, but there are also some challenges relating to microservice architecture when developing an enterprise application. These include the following:
- It is sometimes difficult to achieve strong consistency across services and transactions.
- Atomicity, Consistency, Isolation, Durability (ACID) transactions do not span multiple processes. ACID is a set of properties of database transactions intended to guarantee validity, even in the event of errors, power failures, and so on. This can be counteracted, however, using eventual consistency, which helps to manage transactions in a microservice application.
- A distributed system often:
- Is harder to debug or trace
- Has a greater need for end-to-end testing
- Requires you to expect, test for, and handle the failure of any process
- Has more components to maintain, which leads to issues such as redundancy or High Availability (HA)
- It typically requires a cultural change with regards to DevOps, such as how applications are developed and deployed, and the cooperation of Development and Operation teams
In light of its disadvantages, in the next section, we will discuss when to use microservice architecture for your project.
One of the challenges related to microservice architecture is deciding when to use it. The following scenarios are examples of when it is a good idea to start using microservice architecture in your project:
- New project development: Microservice architecture is more suitable when developing the first version of an application. You can plan your project and its associated modules from an initial level, whereas it can be challenging to convert an old or legacy project into a microservice-based application.
- Separating concerns in the business application: This architecture provides a better level of separation of concerns as the Spring framework provides separation of concerns at the level of the application’s components.
- Development of a cloud-native application: This architecture provides cloud-native patterns and supports distributed application development. We can create numerous independent services which can be deployed to a different platform on a different network, as can be done in the cloud.
- Quick development of independent service delivery: Microservice architecture allows us to develop a business application quickly by dividing it into the independent delivery of individual parts within a larger, integrated system.
- Efficient modules: In a business application, there are some modules that are very important for a business, and these modules must be developed in extremely efficient ways. Microservice architecture allows you to use better technologies for these modules to improve their efficiency.
- Alongside a fast-growing product or team: If you start with microservice architecture, it provides your team with the flexibility to develop a product quickly. It also provides scalability to your application from the beginning.
Essentially, microservice-based application development better follows an agile software development process, because different, small teams work on separate modules of the application. The teams are therefore able to release services with continuous delivery. Now, let's move on to another type of software application architecture.
SOA is another application architectural style. In SOA, architecture services are provided to other services and to vendor components using a communication protocol over a network. These services are discrete units of functionalities that can be accessed remotely. The following diagram shows an SOA in action:
As you can see in the preceding diagram, there are two main layers of the SOA: a service consumer layer and a service provider layer. The service consumer layer is the point at which all the consumers, such as human consumers and other service consumers, interact with the SOA. The provider layer is the point where all services are defined within the SOA.
In the preceding diagram, the Enterprise Service Bus (ESB) provides communication by a common communication protocol, or communication bus, which has connections between the consumers and providers. In SOA architecture, database storage is shared between all services.
SOA has more dependent ESBs. The ESBs implement a communication system between mutually interacting software applications with microservices. It also uses faster messaging mechanisms.
Let's now move on and take a look at the differences between SOA and microservice architecture.
The following table lists some of the differences between SOA and microservice architecture:
Service-oriented Architecture (SOA)
Focuses on imperative programming
Focuses on a responsive actor programming style
Its models tend to have an outsized relational database
Microservices frequently use NoSQL or micro-SQL databases (which can be connected to conventional databases)
In SOA, ESB implements communication between mutually-interacting software
In microservices, independent processes communicate with each other using language-agnostic APIs
It is easier to deploy new versions of services frequently, or scale a service independently
Services can operate and be deployed independently of other services
SOA has ESB, which could be a single point of failure that impacts the entire application
Microservice architecture has a much better fault-tolerance system
Data storage is shared between services
Each service has its own data storage
In this chapter, we discussed different software application architecture patterns, including monolithic, microservice, and SOAs. Monolithic architecture means building an application that includes all of its modules as a single artifact. It is better for simple and lightweight applications,
but it has various drawbacks, such as its large codebase, which can become difficult to manage. Even after making only a small change to the codebase, a new version of the complete application codebase must be built and deployed to the server. To resolve the problems of monolithic architecture, microservice architecture can be used.
Microservice-based architecture resolves many of the problems of monolithic architecture. This architecture pattern decomposes a monolithic application into several different and independent processes. These processes are known as microservices. A microservice architecture pattern is the better choice for complex, evolving applications. In essence, this architecture pattern handles a complex system better than monolithic architecture.
In Chapter 2, Anatomy of Microservice Decomposition Services, we'll look at how to decompose services in microservice architecture.