Domain-Driven Design in the era of Microservices
There’s a lot more to it than at first glance
Oh how far DDD has brought us
There was a time when DDD was new. Back when Eric Evans first published his seminal work Domain-Driven Design: Tackling Complexity in
the Heart of Software, the ideas we encountered in the book felt like a balm on many a sore wound. What Evans had created was a better way of structuring complex logic so that not only our code, but our applications were far easier to understand, and far less scattered across and endless panoply of spaghetti code and duplicated behaviours.
That was then. Since then, we have all had our varying successes with DDD, be it in how you slice up a Bounded Context in your context map, to the implementation of domain services or infrastructure logic, or even (and this was the biggest one for me) teaching old devs new tricks: Reteaching developers to think DDD, not just copy and paste what I’d done previously.
So for quite some time, we’ve had this amazing thing called Domain-Driven Design. It is a decent investment to build a good DDD system (and thus why it is recommended you do NOT bring in DDD unless your application logic is reasonably complex), but what you gain from this investment is a far more structured and contained Domain.
Here’s a simple diagram of a Bounded Context in DDD (forgive me if this differs from your understanding. There are areas of DDD which vary between implementors)
If you have never worked otherwise, consider yourself fortunate. Those of us who worked on complex business apps without DDD can tell you horror stories of the bad old days. If you are currently working on such an ugly and old-school nightmare app, my condolences.
Along come Microservices
Now, not that long ago, this new term started circling the Serengeti of the IT world like some distant, hazy buzzard. The term was ‘microservice’. What’s a microservice? Well, any definition is as good as the next, so here’s Wikipedia’s take on it
Microservices are a software development technique — a variant of the service-oriented architecture (SOA) architectural style that structures an application as a collection of loosely coupled services. In a microservices architecture, services are fine-grained and the protocols are lightweight.
That’s pretty damned generic, I admit. Maybe a picture might help
Microservices take on a single responsibility, some part of the business flow which is theirs to own and control. They are able to be a part of the whole product by communicating with their siblings (and possibly the outside world) through light-weight comms and loose-coupling. So now my business logic is isolated to small little workers, each responsible for their own bailiwick, and all of them talking to each other to get a job done.
Now, on the surface this seems pretty nifty. My product is actually split into smaller, more discreet services, each fulfilling some part of my product offering, and chatting with other microservices to do its job effectively. There are clearly other aspects to a full-blown microservice architecture (authentication, service discovery, event publishing / subscribing, to name a few), but you get the basic idea.
Do Microservices play with DDD?
At a first glance, you’d assume they would play well, right? DDD and microservice architecture are all about creating discreet boundaries of responsibility, so that the application is evenly distributed, and ensure no one portion of your application is doing more than it should.
The microservice architecture seems like a nice refinement to DDD, a further isolation of responsibility in your subdomain / Bounded Context. Our first instinct is that it’s a marriage made in heaven. In theory, the use of DDD as a means of encapsulating business logic, and then the loose-coupling and single responsibilities of microservices sound idyllic.
But of course that’s just us looking at it in theory
The great tragedy of Science — the slaying of a beautiful hypothesis by an ugly fact.
When we dig down into the proverbial devil in those details, we start to see that it’s the HOW they play together where things can go askew.
Microservices as self-contained owners of data
It’s important to remember that a microservice is expected to be a self-contained entity. It is responsible not only for its own internal logic, but also ALL of its internal data. This might mean the retrieval and/or persistence of data to a data store, or it can also mean it’s own data structures (as in Entities and Aggregates from DDD). Sharing a microservice’s data (beyond what it exposes via it’s API) is a big ol’ no-no, just as it is a big ol’ no-no to let one Bounded Context to feed on the database of another Bounded Context. Which leads to our next issue
Microservices as self-deployable units of work
Wherever possible, each microservice in a microservice architecture should be self-deployable. The whole point of structuring your product into microservices is so that any one portion can be developed and deployed independent of the others. If I am required to deploy all three of my microservices every time I make a change to one of them, I might as well have not bothered with the architecture. The rule of thumb goes that if a change to one microservice requires the redeployment of x number of other microservices in order for all of them to be successful, then the higher x is, the worse your architecture is.
So where does the Domain Model reside?
I’d say one of the big issues one will face when looking at a microservice architecture is where does the Domain Model reside.
In a traditional DDD application, our Domain Model is a shared resource, a compiled assembly or package, which our Bounded Context consumes across all Application Services, Repositories, and Domain Services.
Yet, now we have a conundrum. If I wish to ensure that my microservices can be independently deployed from one another, how can this work? If I update the Domain Model, don’t ALL my microservices need to be accordingly updated? And how do I isolate my microservice data (including data models) in a DDD paradigm, and yet still keep my Domain Model cohesive?
For next time…
For my next post, I’ll take you through some of the possible ways you can work around this paradox of domain model, DDD, and microservices. There have been a number of approaches organisations have taken to tackle this question, and we’ll have a look at each of them, and their pros and cons.
Until next time :)