One of the questions that every developer asks is where to start and what all is needed from my service other than the Business Requirements. Every organization nowadays is building small micro-services and these services are being build by various autonomous teams. The interesting part is that every team starts build the same chassis or initial part of the service on its own. This leads to the following issues
- Duplicated effort
- Inconsistent Implementation
- Standards being missed
- Engineering effort being wasted
Engineering organizations can easily avoid this waste by creating a Service Chassis or a Service Template – an org-specific service runtime which provides the base platform functionality that each service needs. In addition, Service Templates can be used to help teams easily adopt this shared Service Chassis when creating new services. In this article we’ll explore these ideas of Service Chassis and Service Templates, and briefly look at how they relate to the increasingly popular Service Mesh concept.
Lets look at the technical aspects (plumbing) of the services that we will need to build the service.
- Build Tools Integration (Maven, SBT, Gradle)
- Configurations for the Service
- Authentication and Authorization
- Logging Framework
- External Service Connection (can be database or file-system)
- Connecting to a Queue to retrieve and send messages (Kafka, RabbitMQ, MQ etc.)
- DevOps Scripts (DockerFiles, Helm-Chart)
- Metrics generation and observability.
- Finding the URL for a dependent service (i.e. service discovery)
- Many More
All of the above features are the required from the Technical perspective and it is nowhere related to the Business domain but still teams tend to spend more time building it rather than focusing on the features. This is where the Service Template can help us immensely by standardizing all of this in our Template which can be used to build the new services. Moreover we can keep on improving the template to build it better for the future development. All of the above plumbing is necessary but and its accidental complexity, rather than essential complexity. Let us look at the definition to make it more clear
Essential complexity is the core of the problem we have to solve, and it consists of the parts of the software that are legitimately difficult problems. Most software problems contain some complexity.
Accidental complexity is all the stuff that doesn’t necessarily relate directly to the solution, but that we have to deal with anyway.
So you see, with service most of the time we are trying to solve a Business problem e.g. when you are building an Order Service the main purpose of this service is to serve the orders – users can place the order and when the order is placed successfully , the payment service should be called whose sole purpose it to take payments from the Client using n number of methods. Here we are not talking about technical details at all and at the end of the day the success of the service will be determine solely by the functionality it is providing. So the functionality is the essential complexity that is related to your Product or Business, rest all is plumbing to make sure your service works which is the accidental complexity.
Why Do You Need a Template?
When you start writing a new service by identifying a domain (DDD) or by identifying the functionality, you might end up writing lots of common code. As you progress and create more and more services, it could result in code duplication, or even chaos, to manage such common concerns and redundant functionalities. Moving such logic to a common place and reusing it across different services would improve the overall lifecycle of your service. You might spend some initial effort in creating this component but it will make your life easier later on.
How can I go on and create a template?
Once you embark on building your primary service, think of all the common concerns that you have across all the services that you will be building. Don’t worry if your list is not exhaustive but its a good start.
When you start designing your architecture and use a whiteboard, pull out all the common concerns which can spread across your different domains. If you follow a DDD approach, each domain or subdomain can be a service out of which you can build a chassis framework which works across all your domains.
A few of the notable concerns as has been mentioned above are:
- Exception handling
- Interception logic
- Some common back-end services you connect across services (databases, external calls, MQ, etc.)
The base framework helps in creating new services easily and ensuring that they’re maintainable. You can only concentrate on the service boundary and let the chassis deal with all the other common infrastructure functionalities.
So, how do you start writing a framework? There are no hard and fast rules for creating your own custom framework. You can just build a framework with a snapshot version uploaded to your repository. All other services can just pull it from the repository and use it. Use a SpringBoot autoconfiguration with a starter parent to build a library.
Let’s say you came up with the chassis as a library of cross-cutting concerns — you can think of something like a Spring custom starter. In Spring Boot projects you can write a custom starter project with Spring auto-configure. Remember to add all the dependencies to the starter project and let the main project be plain and simple like “Hello World” with its own required dependencies, so that it gives you a kick-start to your main project.
Key Benefits of using Service Template / Chassis
Reduced Variable Cost of the Services
There will be some initial effort which will be spend on building the right template but anyway we would have to spend that effort to get it working with all the niceties. But, the effort on subsequent services will be a lot lower. This is particularly valuable when working in a micro-services architecture, for two distinct reasons. Firstly, micro services have relatively more plumbing code, since you have a large number of distinct services and each service needs its own plumbing. Secondly, a micro-services architecture is a complex distributed system, and has more complex operational requirements. Each service needs to support capabilities like distributed tracing (correlation ID), service discovery, health-checks, and so on. All of these capabilities require additional code and/or configuration so if you are able to build a template that takes care of most of the things we will be able to reduce the Variable cost to a Fixed cost for each service.
Consistency in the Code
I have worked with large teams that are working on different services and most of the time the code varies a lot between one service or another making it very difficult to review or follow the code, if most of the services are build from same chassis the teams will be forced to adopt the pattern and than all the services will be much more similar at-least in technical aspects. Beyond avoiding some uninteresting decisions, a consistent approach also allows the ecosystem supporting our services to make more assumptions and be less complex. If every service listens on the same port (because that decision is baked into shared plumbing) then the infrastructure to deploy services and route traffic to those services can become slightly simpler. The same logic applies to a myriad of other small decisions, such as how service configuration is injected, how logging is performed, and so on.
Governance as a Code
This is the part that I liked most as by creating the template we are laying the Technical Foundation for our services. It can influence how every service in your architecture is built and operated. You can apply some gentle architectural governance by giving teams an easy, out-of-the-box setup which also aligns with how you’d like services to behave.This approach can work well in organizations that lean towards autonomy and resist top-down architectural guidance. Rather than mandating a certain approach, you instead just make it easier for things to be done that way.
A free, pre-packaged implementation of a service’s basic technical underpinnings reduces the friction in taking the preferred approach to building and running services.
Now we have understood the concept and the benefits of the Service Template, lets look at certain Do’s and Don’ts
Some Do’s and Don’ts
- We should only put the common concerns across service into the Chassis like configurations, security, observability and other cross-cutting concerns.
- Never put business logic in to the chassis as we want the service to follow the Single Responsibility Principle. Similarly if you feel there is some logic that needs to be part of the framework make a common package and put that logic there. But remember any changes in the framework will necessitate complete testing of the entire application.
- Do not build or add unnecessary technical features in your chassis as it will make the framework bulky.
- As micro-services deal with de-centralizing responsibility of data, it is your choice to move your database connectivity to a base framework or not. It depends on whether you have a database setup for each service individually or if you have a single database with multiple schemas distributed across.