Microservices. Miniservices. Nanoservices. There may not be a hotter term in tech right now — which is just the start as we soon dive into Containers (Docker, Rkt), Orchestration (Kubernetes, Mesos, Swarm, Rancher), and then all the challenges that come with them (monitoring, networking, security, etc).
But before we get ahead of ourselves, the first question we need to be able to answer is WHAT are microservices, and how does this approach differs from traditional software development methods.
The short answer is that unlike monolithic applications, microservices are self-contained, composable, and decoupled services. Or in other words, services that do a very specific function, do that function well, and can be easily added or removed from your system architecture.
To accomplish this, microservices require a shift in in traditional thinking, instead focusing on:
• Business value over technical strategy
• Strategic goals over project-specific benefits
• Intrinsic interoperability over custom integration
• Shared services over specific-purpose integrations
• Flexibility over optimization
• Evolutionary refinement over pursuit of initial perfection
There’s just one catch with this new way of thinking— it isn’t new. The above ideas actually come from the Manifesto for Services Oriented Architectures — or SOA. The very model that led us to heavily intertwined and coupled systems — often laced with expensive and archaic Enterprise Service Buses (ESBs).
Those who cannot remember the past are condemned to repeat it.
- George Santayana
One of the challenges of microservices is that they build on many of the ideas and patterns of SOA, leading Martin Fowler to weigh in on how microservices are different by stating:
This common manifestation of SOA has led some microservice advocates to reject the SOA label entirely, although others consider microservices to be one form of SOA, perhaps service orientation done right.
This is extremely important to understand, as in order to ensure we do not repeat our past, we need to understand why SOA — especially since its manifesto shares so many commonalities with micorservices — didn’t work.
The Problem with SOA
In his book, Building Microservices: Designing Fine-Grained Systems, Sam Newman shares his thoughts on why SOA failed, essentially breaking it down to four aspects:
• Communication Protocol (ie SOAP API)
• Vendor middleware (those expensive ESBs)
• Lack of guidance regarding service granularity
• Splitting systems improperly
In many respects I agree with Sam. However, I would advocate that hypothetically SOAP could have worked as a communication protocol. And that perhaps it wasn’t that we used SOAP APIs, but rather HOW the APIs were implemented and that they were tightly coupled to the underlying architecture (down to the specific methods).
At the same time, vendor middleware was designed to “solve” the problem of connecting numerous services, and dealing with constant changes (when the API changes just update the connector or code component once instead of having to update each individual application — hypothetically). While it became overused, it was designed to solve a real problem created by having a magnitude of services.
While ESBs are now a running joke in the microservices world, we are actually seeing their use pick up at rapid speeds in today’s market — both for the consumer with tools like Zapier and IFTTT, as well as for the enterprises with the emergence of MuleSoft, Informatica, Dell Boomi — and of course more traditional players including Tibco, Microsoft, and IBM.
While we think of ESBs and vendor middleware as a thing of the past, the truth is, it is very much our future.
This all begs the question — what’s actually changed with microservices, and how can we prevent replicating the problems created by SOA?
It’s All About Glue
Microservices are designed to fail. This should be common knowledge — your application is deployed and designed to be updated, changed, or even removed from your stack. Tools like Kubernetes allow for “self-healing” deployments, and solutions like Istio help handle load balancing and routing for different versions. All in all, in a weird way, your architecture shouldn’t care about your service at all.
But while microservices are designed to fail — APIs are not. Unlike the abstracted services, each application relies on the API to transmit data. It becomes the glue that holds your entire architecture together — in the very same way SOAP did.
An API that is tightly coupled to its service, is essentially the service itself.
An Innocuous Start
Take for example the use of a CRM — which we’ll call XYZ CRM. This CRM comes with its own vendor API, which you utilize to connect to your other services:
At first, all is great in the world and your service works perfectly. On a small scale, this traditional method of integrating services is all you need.
But pretty soon you find yourself adding another SaaS service, and then another. Just like 73% of organizations believe more than 80% of all their apps will be SaaS by 2020.
And then you add in your on-premise services, custom built applications, intranet applications, until all of a sudden (and usually before you can realize what’s happening), your architecture looks something like this:
Or in the real world, something like this:
Part of the challenge is even up to this point, everything is still working and we’re able to continue building on our service — right up until you need to change a SaaS provider or an internal service. It’s at this moment you realize just how glued into your architecture that CRM is, so when changing from XYZ CRM to ABC CRM we now have to discover, and update every service that relies on it.
Something must be glued into your architecture. This is inevitable.
Abstracting Away Services
One solution to this, of course, is simply not to connect any of your services to other services. Or only have one microservice — or one macroservice that does everything (ie the monolith).
But it may be a lot easier to solve than you think — by ensuring that the glue is a true abstraction of the service — not a tightly coupled representation. What I mean by this, is taking advantage of the full idea behind REST — abstracted representations that do not care what the underlying architecture is.
In the case of XYZ CRM, this is as simple (perhaps more easily said) as creating an abstraction API for our CRM. By hiding the CRM behind an abstraction API we do not have to worry about changes to our service.
Instead, when the service changes, we only need to update the backend of our API to translate the information to our service, and vise-versa. This means instead of updating all seven dependencies, we’re only updating the API layer — creating a seamless update across our entire architecture:
This method can not only be applied to SaaS services, but to any service — including our legacy DB2 database. By placing an API in front of it, we can essentially migrate the data to MySQL or Mongo, update our API, and rip out our legacy DB2 seamlessly (again, with no other service being effected).
The downsides to this approach is that it isn’t nearly as efficient as just connecting to the services directly, requires a lot of thought and planning in building your API, and perhaps most importantly transitions the biggest risk to your architecture from being the service (which is what we DO NOT want) to being the API that abstracts it.
Can’t wait? Check out some of the best practices in this Nordic APIs LiveCast: