Digifine Academy

What are microservices, and how are they used in modern web applications?

The evolution of web application development in the previous 10 years has greatly affected the way developers work on web applications because the old method of making web applications (a single huge codebase for your program) is no longer prevalent due to agile development, having a reliable cloud-based architecture, and the demand for an application that is always scalable. The new dominant architectural style is Microservices.

What are microservices, and why is this architecture the new standard architecture for large and innovative organizations? In this post, we will take an in-depth look at microservices, what’s different than traditional methods, and how microservices work together to create robust, scalable, maintainable, and flexible web applications we use daily.

What is a Microservice? The “Single Purpose” Principle

Microservices are small, independent applications that each have one or more than one specific functions at their heart. The size of the microservice is not an exact measure of lines of code rather, it is a conceptual limit to the functionality of the application, meaning that an application can only do one thing and can do that thing well. This follows the computer science guideline known as Separation of Concerns.

To illustrate this concept, envision an extensive e-commerce site. Instead of one large application that does everything, multiple smaller products make up the complete offering:

User Service – This service would be responsible for user profile management and authentication of users to the site.

Product Catalog Service – This service would provide a list of products for the customer, along with descriptions and inventory of the products.

Order Service – This service would complete customer orders for products.

Payment Service – This service would take care of all aspects of customers making payments to the e-commerce site for products.

Shipping Service – This service would calculate the cost of delivery for products and track their location from the time they leave the warehouse until they reach the customer.

All of these services can be classified as microservices. Each microservice has its own code, its own data store, and can be developed, tested, deployed, and scaled independently of the others. The only way these services interact with each other is through well-defined, vendor-neutral interfaces, such as HTTP/REST APIs or asynchronous message queues. These services represent a significantly different approach than what has been used previously.

 

Microservices vs. The Monolith: A Tale of Two Architectures

In order to fully understand microservices, one must look back on their previous evolution: the monolithic model. A “Monolith” refers to an early software development model that created one application as an entire application (one unit). As a result, the various aspects of each layer within the architecture, i.e., User Interface (Front-End), Business Logic (Back-End), and Data Access (Database), all run as part of one single process and are therefore considered to be one unit of code.

Monolithic Software Development:

In the early days of the development of Monoliths, it was relatively easy to write code, test the code, and deploy the application. The Developer would create a new project from scratch and create a three-tier (i.e., Presentation, Logic, and Data) architecture from a single code repository using a very small team of Developers. However, as the Software Product evolved and development became horizontal, operating with multiple development teams became more complex to develop and maintain the Monolith, hence leading to the inevitable demise of the developers involved in maintaining the application.

Challenges of the Monolith:

Inefficient scaling: When one component of an application (for instance, a payment processor) is overloaded with traffic and requires a large number of resources to handle the sudden demand, all other components in that same application will also need to be scaled out as well, wasting the vast majority of available resources while only a small portion will actually be utilized.

Development bottlenecks: When developing software applications with large codebases, complexity and maintainability will become increased due to the sheer number of lines of code. An example of this is the high probability of a developer working on one area of the application potentially breaking functionality somewhere else within the application’s codebase, as the relationship and connection between application components become distanced due to extensive modifications, causing loss of traceability in codebases having hundreds of controllers with inconsistent logic.

Technology lock-in: Since applications are typically based on a single technology stack (or set of technologies), it becomes nearly impossible for an organization to utilize newer, more effective programming languages or development tools and/or frameworks.

Decreased time to market: The complexities of large applications, along with their interdependencies, can cause delays in delivering new product features, ultimately resulting in longer total development cycles for the entire product.

Solution through microservices: Microservice architecture was developed as a way to address many of the issues discussed previously. A microservice architecture adopts the opposite approach of monolithic architectures and provides a solution for each of these application development pain points. Rather than have one large group of developers work together on one large application, microservices empower small, cross-functional teams developing microservices to take ownership of providing total lifecycle capabilities for the specific items of functionality or business services they deliver access to within the application.

Core Components of a Microservices Architecture

While microservices are usually described as an assortment of small services, it is important to also note that the microservices architecture represents a complete system that has an ecosystem supporting its operation. The principal components of the microservice ecosystem include:

  1. API Gateway:

As a single point of entry for client-side requests made from either a web or mobile application, the API gateway provides an effective way for clients to communicate with microservices and their respective instances. Rather than having to be aware of where each of the many microservice instances exists, a client simply requests access to an API Gateway. The API Gateway handles routing the client request to the proper microservice and can also perform additional functionality such as Authentication, Logging, and Rate Limiting in one place on behalf of all microservices.

  1. Service Discovery:

In an environment where services are frequently created, destroyed, and moved, such as the cloud environment, service discovery tools are used to maintain a registry that includes the location of services, thus enabling those services to be located and communicated to each other. Therefore, when a new service instance is provisioned, it registers its location (IP address) with the service discovery tool. When another service requires the ability to connect to the new service instance, the requesting service queries the service discovery tool for the new service’s location.

  1. Communication Protocols (Smart Endpoints and Dumb Pipes):

Microservices generally prefer to use data communications that are as lightweight and redundant as possible. The basic concept is that “Endpoints” are the applications/services with the actual Logic and the “Pipes” (or data transporters) carry data between these Endpoints.

Synchronous Communication:

Typically, HTTP/REST (with JSON) is the most common and very simple way to communicate with all applications and across different programming languages.

Asynchronous Communication:

Typically involves Message Queues (e.g., RabbitMQ or Apache Kafka). This type of Communication is very important for having loosely coupled applications. A Service is able to broadcast an Event (e.g., “orderPlaced”) and then other Services can listen for and respond to the Event, without having to wait for a direct response from the Service that broadcasted the Event.

  1. Containers & Orchestration:

Microservices run best on a containerized hosting structure with Docker as the predominant container platform. Containers encapsulate an individual microservice with its dependencies, creating a self-contained, lightweight, and portable unit that allows for a consistent experience across all environments (development, testing, production).

Manually managing containers would be impossible on a microservice architecture; therefore, you need an orchestrator to automate deployment, scale, manage, and network the microservices (e.g., Kubernetes). With Kubernetes, orchestrate your microservices by automating many things (e.g., load balancing, rolling updates, and self-healing of containers).

  1. Decentralized Data Management:

In comparison to a monolithic application where there is a single database for the entire application, microservices create decentralized ownership of data. This allows each microservice to have its own database, data schema, and data model that are best suited to each microservice’s needs. For example, the order microservice can use the relational database management system (RDBMS) PostgreSQL for its need to enforce ACID (atomicity, consistency, isolation, durability) transactions, while the product review microservice can use the NoSQL database MongoDB because of the flexibility of its schema. As a result, microservices do not create tight coupling, and only the owning microservice has direct access to its data through its own API.

 

How Microservices are Used in Modern Web Applications

Combining all different types of micro-services together offers true power in developing feature-rich, complex applications. So how do they get used in actual practice?

  1. They allow you to scale independently (highly available) and resiliently (fault-tolerant).

Modern web-based applications do not have all features with the same load profile. For example, Netflix’s video service may have spikes of usage when a new show launches, while its user subscription service remains steady. By using micro-services, you can independently scale only the video service by spinning up multiple instances of the same service. Thus, you can scale horizontally at a fraction of the cost of scaling an entire monolithic application.

In addition, microservice architecture provides isolation of failures (fault tolerance). If the recommendation engine for an e-commerce website goes down, users will still be able to search the product catalog, browse products, and place orders. This allows for isolation of the problem, thus eliminating the possibility of a cascading failure of the e-commerce application.

  1. Speeding up Development With Multi-Language Storage Systems And Different Technologies

Modern web applications require the right tools for the job. For example, when building a complicated search function, a development team might decide on using Elasticsearch due to its ability to index data efficiently/quickly. If they are working with a real-time stream of information, they might choose to build it using the Go programming language because it has better concurrency capabilities. The microservice architecture allows teams to choose which technologies to use for their particular service without having to adhere to a company-wide technology standard. This concept of polyglot persistence allows each microservice to use the best possible database for the data it stores.

  1. Providing Everyone With The Same Experience

One set of microservices can be used to provide multiple front ends with the same experience simultaneously. There is only one set of backend services (user service, product service, order service, etc.) that can be used by a React single-page app, an iOS mobile application, an Android mobile application, and/or an external API partner. As such, since the backend is no longer tied/associated with any specific front end (like a monolithic solution that only serves up server-rendered HTML pages), it is much easier to adapt to the ever-changing needs of users and businesses for omnichannel experiences today.

  1. Enable Continuous Delivery and DevOps

Microservices are self-contained units of software development that perfectly complement the concepts of DevOps (Development and Operations) and Continuous Integration / Continuous Delivery (CI/CD). With a microservice-based architecture, teams can rapidly develop and deploy their application components, run isolated tests on their service, and deploy the service into production all within hours, instead of waiting months for a coordinated production release with other development teams. The speed at which teams can develop, deploy, test, and iterate enables businesses to try new things, improve quickly, and deliver new functionality into the marketplace faster than their competitors.

 

Challenges and Considerations

Microservices come with many advantages, but they aren’t a magic bullet; they add a high degree of complexity referred to as “distributed system complexity”.

Operational Overhead: You’re no longer managing a single app but potentially many dozens or even hundreds of applications, which means you need mature operations teams and robust monitoring, as well as sophisticated logging and tracing infrastructure, to debug problems across multiple services.

Data Consistency: Maintaining data consistency across services when those services have separate databases is difficult. A single business transaction (for instance, placing an order) may involve updating three different services (Order, Payment, and Inventory). You cannot use a normal ACID transaction like you would for a traditional monolithic application. Instead, you have to employ more sophisticated patterns like the Saga pattern, which relies on performing some local transactions and then compensating for them in order to provide eventually consistent data.

Network Latency: The speed and reliability of in-process function calls are much faster and more reliable than talking over the internet. Services that need to talk to each other a lot (chatty services) can create a lot of performance problems for your application.

Service Boundaries: Establishing the correct size and boundaries of a service is something of an art form. If you make your services too small, you will create “nanoservices” and end up with a lot of architectural spaghetti. If you make your services too large, you will create a distributed monolith, which contains all of the characteristics of both a traditional monolith and a distributed application.



Conclusion

Microservices architecture has dramatically affected how we approach and construct modern web applications, enabling us to decompose vast systems into much smaller independent (and focused) services, offering us an unprecedented level of scalability, speed of development, and resiliency. Microservices provide teams with the capability to take ownership and to move at a fast pace, in addition to helping to create applications using the best tools for each task.

The operational complexities of microservices contrast with their power. Microservices are not a good fit for a simple blog or MVP; they are a strategic option for developing complex, changing applications that are expected to scale quickly and are developed by large teams.

Moving from a mono to microservices-based operation involves some trade-offs, including losing simplicity in exchange for control, and losing tight coupling in exchange for independence. For applications that surpass their mono capabilities, transitioning to a microservices-based application unlocks the ability to improve both agility and innovation within the rapidly changing world of the web.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top