Welcome to a monthly series that chronicles our journey from a traditional ASP.NET Web API solution to a microservices-based platform.
Interest in "microservices" has really taken off over the past couple years as this Google Trends graph shows:
Articles regularly appear discussing the wonders and pitfalls of "microservice architecture" while providing their own best practices. Here are some recent ones...
- Docker As Process, Paas As Machine, Microservices Architecture As Programming Model (Theme from November 2016 Technology Radar)
- Getting Started with Microservices (Oracle Developers Blog)
- Modules vs. Microservices (O'Reilly Ideas)
- Don't think of your microservices as "micro" services (Oracle Code)
and a few older ones...
- Microservices (Martin Fowler)
- Azure Service Fabric and the Microservices Arhictecture (MSDN)
- The Hidden Dividends of Microservices (acmqueue)
However, there is varying (and sometimes vague) advice about what exactly a microservice is. I believe this confusion is part of a larger problem — how to properly architect a system. This post outlines why we're shifting to microservices and how our new architecture helps us define what a microservice is.
So, why change what we have?
Three main reasons:
- After four years, the project has grown to a size where we need to be much clearer about separation of concerns at a system level.
- The upcoming US launch requires us to meet varying requirements at a state rather than country level, unlike Australia and the UK.
- As our feature set and partner list grow, we need more fine-grained deployment and configuration ability.
After our initial launch in Australia, we started exploring different options. Originally, it seemed that DDD would give us distinct code boundaries and adopting a queuing mechanism (message bus) would decouple runtime behaviour and simplify deployment. We experimented with several ideas and frameworks, including:
- domain modelling
- MassTransit, RabbitMQ, Azure Service Bus
- event sourcing/Event Store
Because the literature around microservices tended to be generic, we didn't spend too much time there and focused on decomposition and communication. Although we did go fairly far down the DDD path, after Kenny (a fellow architect) attended the Architect's Master Class, we completely changed direction.
The IDesign Method™
Juval Lowy generated a lot of
controversy conversation in 2007 with his declaration that "Every Class Should be a Service" and that WCF was really a "better" .NET. Regardless of your thoughts on WCF, his company has one of the few (if not only) clearly-defined, battle-tested guides to:
- properly decomposing a system
- using that decomposition to create the right services and architecture
- validating that architecture
- planning security boundaries and runtime allocation
- creating plumbing infrastructure that frees developers to concentrate on what's important to the business
- and more!
Here are a few links that explain these ideas in more detail:
- The IDesign Method™ (PDF)
- Software Architecture Decomposition - an in-depth explanation of why functional and domain decomposition do not work, and why volatility-based decomposition should be used instead
- Building Microservices in .NET - a presentation on how to effectively design microservices
Once we understood how to properly architect the system in a service-oriented manner, defining microservices was a no-brainer.
Our microservice definition
As an example, here's the static architecture for a simple billing system:
Each of those boxes is a service or component. There are a few different types of services, each with a clear role to play:
- Managers: encapsulate sequence in use cases and workflows
- Engines: encapsulate business rules and activities
- [Resource] Access: encapsulate resource access and can be shared across engines and managers
- Resource: physical resource (e.g. database)
- Utilities: common infrastructure
Since this is a closed architecture (i.e. calls are only made to the tier directly below), the clients can only "see" managers. This leads to our definition of microservices:
Microservice - A subsystem of the architecture that is publicly accessible. A microservice contains at least one manager and possibly other components. We could also just call them subsystems and drop the term microservice completely :)
In the billing system above, there are three microservices: "subscription", "invoice", and "notification".
Note: In his article Escaping Appland, Michael "Monty" Montgomery defined this same taxonomy and we feel it's a natural fit.
This approach to architecture and microservices is certainly different than others and much more rigid. In future articles we'll cover topics including:
- Data Access - Articles keep mentioning microservices "owning their own data" which seems a bit different than our access components.
- Testing - A subject always close to my heart, and there are certainly differences in testing services compared to classes.
- Infrastructure - What type of plumbing do we need?
- Queuing: How we use it and why.