Docker and Kubernetes for Java Developers
上QQ阅读APP看书,第一时间看更新

The microservices architecture

The microservices architecture is designed to address the issues we've mentioned with monolithic applications. The main difference is that the services defined in the monolithic application are decomposed into individual services. Best of all, they are deployed separately from one another on separate hosts. Take a look at the following diagram:

When creating an application using the microservices architecture, each microservice is responsible for a single, specific business function and contains only the implementation that is required to perform exactly that specific business logic. It's same as a divide and conquer way of creating a system. This may seem similar to the SOA-oriented architecture. In fact, traditional SOA and microservices architecture share some common features. Both organize fragments of the application into services and both define clear boundaries at which a service can be decoupled from the other. SOA, however, has its roots in the need to integrate monolithic applications with another one. This has been done, usually, using an API that was usually SOAP-based, using heavy XML messaging. In SOA, this integration was relying heavily on some kind of middleware in between, usually Enterprise Service Bus (ESB). Microservices architecture can also utilize the message bus, with significant differences. In microservices architecture there is no logic in the messaging layer at all, it is purely used as a transport for messages from one service to another. This is a total contrast to ESB, which needed a lot of logic for message routing, schema validation, message translation, and so on. As a result, microservices architecture is less cumbersome than traditional SOA.

When it comes to scaling, there's a huge difference when comparing microservices to monolithic applications. The key advantage of microservices is that a single service can be scaled individually, depending on the resource requirements. That's because they are self-sufficient and independent. As a microservice is usually deployed on smaller (in terms of resources) host; the host needs to contain only resources that are required for a service to function properly. As the resource requirement grows, scaling is easy both ways, horizontally and vertically. To scale horizontally, you just deploy as many instances as you need to handle load on a specific component.

We will get back to this concept in the coming chapters, when we will be getting to know Kubernetes. Scaling vertically is also a lot easier and cheaper in comparison to the monolithic systems, you upgrade only a host on which your microservice is being deployed. Also, introducing new versions of the service is easy, you don't need to stop the whole system just to upgrade a piece of functionality. In fact, you can do it on the fly. When deployed, microservices improve the fault tolerance for the entire application. For example, if there is a memory leak in one service or some other problem, only this service will be affected and can then be fixed and upgraded without interfering with the rest of the system. This is not the case with monolithic architecture, where one faulty component can bring down the entire application.

From a developer's perspective, having your application split into separate pieces deployed individually gives a huge advantage. A developer skilled in server-side JavaScript can develop its piece node.js, while the rest of the system will be developed in Java. It's all related to the API exposed by each microservice; apart from this API, each microservice doesn't need to know anything about the rest of the services. This makes the development process a lot easier. Separate microservices can be developed and tested independently. Basically, the microservices approach dictates that instead of having one giant code base that all developers are working on, which often becomes tricky to manage, there are several smaller code bases managed by small and agile teams. The only dependency services have on one another is their exposed APIs. There's a difference in storing data as well. As we have said before, each microservice should be responsible for storing its own data, because again, it should be independent. This leads to another feature of the microservices architecture, a possibility to have a polyglot persistence. Microservices should own their data.

While microservices communicate and exchange data with other microservices using REST endpoints or events, they can store their own data in the form that is best suitable for the job. If the data is relational, the service will be using a traditional, relational database such as MySQL or PostgreSQL. If a document database is better suited for the job, a microservice can use MongoDB for example, or Neo4j if it's graph as data. That leads to another conclusion, by implementing the microservices architecture we can now only choose the programming language or framework that will be best suited for the job, this applies to the data storage as well. Of course, having its own data can lead to a challenge in the microservices architecture, data consistency. We are going to cover this subject in a while in this chapter.

Let's summarize the benefits of using the microservices architecture from the development process perspective:

  • Services can be written using a variety of languages, frameworks, and their versions
  • Each microservice is relatively small, easier to understand by the developer (which results in less bugs), easy to develop, and testable
  • The deployment and start up time is fast, which makes developers more productive
  • Each service can consist of multiple service instances for increased throughput and availability
  • Each service can be deployed independently of other services, easier to deploy new versions of services frequently
  • It is easier to organize the development process; each team owns and is responsible for one or more service and can develop, release, or scale their service independently of all of the other teams
  • You can choose whatever programming language or framework you think is best for the job. There is no long-term commitment to a technology stack. If needed, the service can be rewritten in the new technology stack, and if there are no API changes, this will be transparent for the rest of the system
  • It is better for continuous delivery as small units are easier to manage, test, and deploy. As long as each team maintains backwards and forward API compatibility, it can work in release cycles that are decoupled from other teams. There are some scenarios where these release cycles are coupled, but this is not the common case