This is the second part of a multi-part series of blog posts on Micro-services. There seems to be a lot of ambiguity amongst the community around what these things are, when they should be used and how to approach them. This blog series is basically a regurgitation of 5 years of building and learning about micro-services. I imagine that not everybody will agree with everything I say here, but I hope that there is nothing so controversial as to cause serious disagreements.
So, you've read about these things called Micro-services. And they sound pretty interesting to you. How do you decide whether or not you should use micro-services on your project or if you should choose a different architecture? In this section, I'm going to outline what Duncan Cragg, another colleague of mine, and I discussed as some of the reasons for using a micro-service architecture. Ie: reasons for which you should consider having a micro-service. If, after reading this, you decide that the system you are building could use a micro-service or two, then, well, apparently you need to use a micro-service architecture. I personally would think that you could build a monolithic application that relies, for various bits of cross cutting business functionality, on several micro-services. Wait, though, you're thinking to yourself, shouldn't you either use micro-services or not? Well, I shrug in response, it's your system, the key concepts are 'maintainability' and 'cost'. As I mentioned earlier, there is a cost to every micro-service you create. There are no hard and fast rules, but making judgement calls is part of the game.
So, the following are some reasons why you might choose to create a micro-service.
a) The right tool for the job
Modern software developers have a huge amount of choice when it comes to tools and technologies that they can use to solve business problems. There is often too much choice and it is hard for developers to pick the right tool. We, due to inertia, lack of training, lack of general knowledge and probably a variety of other reasons tend to fixate on a single technology stack to solve all problems. This manifests itself in both data storage and programming language choice.
Giving yourself the flexibility to choose the correct tool for the job is a very compelling reason to create a micro-service. Often times in 'Architectures of Old'(TM) regardless of suitability of a given data storage technology, we would use the 'Golden Hammer'. The ubiquity of SQL backed relational databases often meant that it became the data storage technology even when the data stored wasn't terribly relational. The idea that micro-services brings to this discussion is that each micro-service can be backed by a different data storage technology. Different parts of a single system might be better served with differing data storage technologies depending on the needs of that particular service.
Similarly, a service that is solely responsible for delivering rendered html might be best built in ruby using Sinatra. Complex domain rules might be best served with C# or Java, and you might choose F#, clojure or NodeJS for a high performance event processing service to which functional programming fits very well. Micro-services give you the ability to choose the right technology for a given problem rather than following the defacto standard.
b) Rates of churn
Micro-services also provide a good separation if you need to be able to deal with differing rates of code churn. Consider an application with large amounts of integration work. Integration, as we know, is complex and challenging, especially if it's done with third parties that are out of your control. Change, and the associated risk of breaking integration partners, is something that is desirable to reduce. On the flip side, your look and feel might be something that you want to be able to change rapidly. Decoupling these two pieces of functionality, via micro-services might be a very good idea.
A clever reader may have noticed that I called out integration as a challenging aspect of software development and also realize that micro-services imply more, not less, integration. I'll be discussing this as one of the challenges in the next blog post.
c) Splitting teams/team skills
Scaling a team can be quite challenging, especially in a complex system. Splitting a system, via micro-services can be an effective way to provide additional scalability. The complex aspects of the system can be hidden behind various well defined interfaces and allow less experienced (in the domain) developers to provide value without necessarily understanding the complexity behind the interface.
Micro-services also allow the team to split in terms of team skills or location without the generally associated risks/pain involved with different teams working on the same code base. This allows large teams to be efficiently broken down and a nice separation in the code base keeping developers from stepping on each other's toes.
d) Differing scaling profiles
Different parts of the system might require differing amounts of scaling. The CQRS pattern is a good example of this concept. Writing and reading from a system often have different scaling requirements. It's very rare that there is an equivalent amount of reads and writes within a system and it would be nice to be able to scale the reads up independently from the writes. Using micro-services allows you to do this across various aspects of your system and gives you a lot more flexibility in terms of scaling.
e) Security zoning
In 'Architectures of Old'(TM), the security architects insisted on a layered approach to building a system. Essentially, they wanted to avoid the risk of having important code running on web facing servers. Micro-services can provide zoning that is analogous to the traditional layered approach. Business logic and critical data storage can be separated from the services that provide html rendering. Communication between the individual micro-services can be fire walled, encrypted, etc. until the security architects are happy.
f) Isolating legacy functionality
Large legacy systems, especially if the original developers have moved on, can be very difficult to change or add functionality. Given this, new functionality can be added to the system by adding micro-services. This article does a very good job of illustrating this concept.
g) Cross-cutting business functionality
Any piece of functionality that is in danger of being built more than once in an organization (think authentication, user management, etc.) in a classic stovepipe architecture is a candidate for a micro-service (or set of micro-services as the case may be). This was actually one of the ah ha moments in my micro-service journey, when James Lewis described a new architecture for a client using a set of cross-cutting business services that supported multiple different applications. This actually allowed the client to split up an originally monolithic application into several smaller and more maintainable applications utilizing these cross-cutting micro-services. This is actually a good example of when you could use a combination of both micro-services and classical applications to provide business value.
h) The unknown
Micro-services allow you the ability to isolate the unknown. If one were to ask me how I defined an 'Agile' architecture, I'd say one that allows the maximum amount of flexibility and provides the ability to responsibly defer decisions for as long as possible. A colleague of mine, Alan Grimes, says "You will never know less than you know now" fairly regularly when we are making architectural decisions. Micro-services provide you the ability to defer decisions and the flexibility to have your architecture grow as your understanding of the system and domain increases. They allow you to defer difficult decisions such as data storage technology until it's absolutely required. If the only thing that a service provides is a thin, domain aware wrapping over some data store, the most important thing to get right is the interface with which your other services interact. This means that initially and for quite a while during the development process, an in memory data store would allow you to tackle a lot of the more interesting/difficult architectural challenges. Fundamentally, micro-services provide you with the tools that a team needs to responsibly defer the decisions that make an architecture less able to respond to changing business requirements.
*) Easily rewrite complete services
I'm not convinced by some of the arguments I've heard around this reason, however, for the sake of completeness I've decided to include it. Small services that are easy to rewrite are, by definition, easy to maintain and change. It's easier to write code than it is to read and understand other peoples code. In the course of my current project, three of our micro-services have been torn up and rewritten without requiring major changes to the rest of the code base. This is actually a very compelling reason to use micro-services. However, it is probably the most difficult axiom to justify. YAGNI dictates that you will likely not have to rewrite a micro-service. Reality dictates that if you don't start with micro-services you will not move to them and therefore if you do have to rewrite areas that you've made serious mistakes in, you're screwed. I personally do not believe that we should walk into any code base with the intention of being able to completely rewrite large pieces of it. However, even with the best of intentions code bases can turn to mush. Mistakes are made, corners are cut and there will be parts of any sizable code base that need to be refactored. Micro-services, as they provide a very strong encapsulation model, provide a safe way to do this refactoring/rewriting. So, while, I would not choose to make a micro-service architecture based on the ability to rewrite a service, it is a consideration that you should take into account when making your decisions.
The caveat to this point is that you MUST have service level unit/integration tests or you've already lost the battle. Traditional class level unit tests fail miserably on this point as they don't actually give you any confidence that you can rewrite your service without introducing bugs.
It's tempting at this point to conclude that Micro-services are finally that silver bullet that will solve all of our architectural needs. Unfortunately, like all tools there are costs to using Micro-services. The next post of this series will talk about the challenges and costs that are associated with building micro-service architectures. Regardless of any mitigations, it IS possible to over engineer your system using micro-services. You know what they say, with great power comes great responsibility and fear leads to the dark side. Or something like that.
Copying of this content is allowed only with reference to the source and indication of the author of TQM systems' material.