Microservice Definition and Architecture
This article walks through a set of patterns that leverage good engineering practices breaking down the development and deployment of microservices.
Join the DZone community and get the full member experience.Join For Free
You may be asking yourself, “Why another series of articles on microservice?” Yes. Microservices are a popular topic. Yes. Containerization has pushed the microservice discussion even more to the forefront. Why another series? It’s certainly not the case that other articles, blogs, books, etc. are not good. Most are very good. Then, why another series?
You may also like: Microservices: Are They For Me?
At the 50,000-foot view, the concept of microservices makes perfect sense; build applications as a collection of small, coordinated services. As always though, the devil is in the details. Dropping down a bit from that 50,000-foot view reveals a myriad of details that require thoughtful consideration. The good news is that, from a software engineering perspective, there’s often not a lot new to learn.
What’s new-er is that microservices are best developed with a more deliberate adherence to good software engineering principles. In this series, I will walk through you through a set of patterns that leverage good engineering practices breaking down the development and deployment of microservices into concrete, maintainable and repeatable steps.
What Is a Microservice?
If you’ve spent any time reading about microservices, you’ve likely run into multiple definitions of what constitutes a microservice. I believe that, based on the situation, the term microservice can have slightly different criteria.
What’s important is that for you and/or for your organization, is that there’s an agreed-upon definition of a microservice. It’s from this common definition that, regardless of tech stack or framework, all teams are working toward a unified approach to building microservice-based applications.
I look at microservices from a business function perspective. In the organizations with which I’ve worked through the definition of microservices, our discussion focused on contained units of business functionality.
For example, the invoice microservice performs every function required for processing invoices. The medical records microservice defines all interactions an application requires when dealing with medical records.
In the organizations with which I’ve worked through the definition of microservices, our discussion focused on contained units of business functionality.
Hopefully, those two examples seem pretty obvious. As is typical in our industry, it’s not always that cut and dry. There are grey areas that you will need to think through. Consider a service that provides user management. Would it be considered a microservice? Maybe it is, or maybe it’s not. In some situations, if applications interact with the user management system directly, it may be considered a microservice.
If the business layer front ends the user management system, tailoring it to business-specific needs, then you might consider the business layer the microservice and treat the user management system as a subsystem much like you would a database. There is no right or wrong answer here, but it’s why organizations need to identify a common definition.
It's More Than Slinging Code
As stated previously, microservices require a disciplined engineering approach. It’s not to say that discipline isn’t required when building monolithic applications. Monolithic applications execute in a single runtime where details including deployment, logging, metrics, monitoring, etc. are prescribed by the runtime environment.
With microservices, an organization must design an environment where many of these non-functional requirements are addressed cohesively; keeping in mind that a collection of microservices may very be using different tech stacks and/or frameworks.
Keeping this in mind, developing microservices requires attention to these details from the outset. How a microservice provides metrics or diagnostic logging are foundational considerations. To effectively function in an iterative development methodology, deployment patterns must be defined and implemented by the time the first line of code is written.
In order to effectively function in an iterative development methodology, deployment patterns must be defined and implemented by the time the first line of code is written.
The key to these disciplines is maintaining a separation of concerns. With a monolithic application, if the operations team determines that there’s a better system for tracking application metrics, the monolithic runtime environment can be updated to accommodate the change.
For microservices, each deployed microservice would have to be updated. In this example, by maintaining a separation between the tracking of metrics within a microservice and the mechanism by which they’re reported, system-wide evolution becomes more straightforward.
Tech Stacks and Frameworks
Most popular tech stacks and frameworks have evolved to consider microservice development and runtime environments. Beyond the discussion among developers on framework capabilities, choosing a framework to support a microservice environment requires a cross-discipline team approach. Beyond development considerations, operational aspects must be taken into account.
The question, “How well will microservices developed using a selected framework interact with the runtime environment and subsystems being used to coordinate an entire application?” will need to be answered. To provide the best overall solution, working through ad understanding the requirements of each perspective may require compromise.
Each stakeholder must keep the end goal in mind. What’s the purpose of developing software? Deploying it to serve its purpose. The big picture is that the total cost of operating an application is much larger than the initial development costs, so development and operational decisions made early on will have a ripple effect in the months and years ahead. For a general overview of this cycle, take a look at one of my previous DZone articles, Deployment Matters.
The big picture is that the total cost of operating an application is much larger than the initial development costs, so development and operational decisions made early on will have a ripple effect in the months and years ahead.
When deployed, microservices needs to deal with two primary constituents. A microservice has to integrate and interact with support subsystems such as metrics gathering systems, databases, security providers, and other supporting technologies that either support the microservice itself or assist the infrastructure in supporting the microservice.
On the other hand, a microservice is deployed to provide a service that contributes its functionality as part of an overall application, which implies that it’s accessible to consumers of its interface. Below is a depiction of this architecture:
I like to use Legos in diagrams as it conveys multiple concepts through a familiar metaphor. Between each set of Lego blocks is that thin Lego strip often used as the base for another set of Legos. This represents the strict separation of responsibility between each layer. Layers interact only through the provided contract/interfaces. Here, the layers are labeled with an understanding of what has been described so far.
The layer at the bottom is a framework. No framework is implied, but a selected framework should take into account these and other capabilities. Here is where attention must be given to supporting technology evolution. How straightforward would it be to replace the implementation of any supporting technology such as a database, metrics, authentication, and so on without impacting the microservices?
The middle layer represents the microservices themselves. This is the layer where business functionality is provided. The business functionality mustn’t drift into the other layers. That erodes the separation of concerns and creates unwanted dependencies. Finally, the top layer is labeled as API Servers.
These are the entry points to the microservices. Their purpose is to receive and respond to requests from service consumers translating data formats and protocol as required to invoke the actual microservice. Other than some validation and data transformation, there is no business logic at the API Server layer. This is what allows microservice to provide its capabilities to various consumers.
The diagram implies a couple of additional concepts. In the Microservice and the Framework layers, there’s a horizontal relationship meaning that, for example, one microservice can invoke another microservice or one framework capability can leverage the capability offered within the framework.
Also, the far right of the diagram represents an external system that may invoke a microservice through an API Server, or by directly embedding the microservice as an application module or library.
Show Me the Code!
Toward the beginning of this article, I mentioned that the devil is in the details. In my next article, we’ll move on to the anatomy of a microservice by reviewing an example project. The project is written in Java using the Quarkus framework. Quarkus is a relatively new Java framework with a focus on “A Kubernetes Native Java stack tailored for OpenJDK HotSpot and GraalVM…”
The code is available on GitHub at https://github.com/relenteny/microservice. As of the writing of this article, the sample microservice is complete to review and test. A few items remain. Details regarding the status of the project are provided in the project README.
It’s from this code base that we’ll work through various microservice development and deployment patterns through an auto-scalable Kubernetes deployment. Stay tuned!
Opinions expressed by DZone contributors are their own.