Migrating a monolithic application to a microservices architecture - things to consider

So you have decided that your application needs to be migrated to a microservices architecture. Now you need to decide where to begin, what are the things that you need to consider upfront? My current organization’s customer is at a similar stage with their product. I have been working on the product for about an year now and I reckon these are some of the issues that need to be addressed before starting the microservices journey.

I’ve tried to address each of these issues below.

How do I go about breaking up the monolith into loosely coupled services?

Different strokes for different folks but for the current project that I’m working on I reckon the best way to move forward would be to identify the aggregates (DDD terminology) in your monolith and break it down along the aggregate boundaries. What is an aggregate you ask? Let me refer you to Martin Fowler’s introductory article on DDD aggregates. For a more detailed treatment on the subject, I refer you to chapter 6 of the book, “Domain Driven Design: Tackling Complexity In The Heart Of Software” by Eric Evans.

For the sake of completeness, let me quote an extract from the sixth chapter of this book -

An AGGREGATE is a cluster of associated objects that we treat as a unit for the purpose of data changes. Each AGGREGATE has a root and a boundary. The boundary defines what is inside the AGGREGATE. The root is a single specific ENTITY contained in the AGGREGATE. The root is the only member of the AGGREGATE that outside objects are allowed to hold references to, although objects within the boundary may hold references to each other. ENTITIES other than the root have local identity, but that identity needs to be distinguishable only within the AGGREGATE, because no outside object can ever see it out of context of the root ENTITY.

Eric Evans, Chapter 6 - The Life Cycle of a Domain Object. Domain Driven Design: Tackling Complexity In The Heart Of Software

All this seems very abstract. So let’s try to associate the above quote with an actual implementation. Let me walk you through a simplified domain model of the current project I’m working on. Our client is into surveys. And if you have taken any surveys in your life, you will know that each survey has a bunch of questions and each question has a bunch of answers. Of course, it is not that simple, there a bunch of rules that can be applied at the answer level, or the question level or the survey level itself. And of course, each survey is associated with a project. A project is an entity that that is used to represent all the internal house keeping that the client does to manage that particular engagement with their customer. And then of course, there is the customer. This can be summed up in the below class diagram (created using plant uml on plantuml.com) -

Sample Aggregates

In this diagram, you will see that the ‘Survey’ ENTITY can be considered an AGGREGATE ROOT. The ‘Question’ ENTITY cannot exist outside of a ‘Survey’ ENTITY. Same goes for the ‘Answer’ ENTITY - it cannot exist outside of a ‘Question’ ENTITY. So the ‘Survey’, ‘Question’, ‘Answer’ and ‘Rule’ ENTITIES would together form an ‘AGGREGATE’ and the ‘Survey’ ENTITY would be the AGGREGATE ROOT. This means that to access a ‘Question’ ENTITY, I’ll have to go through the ‘Survey’ ENTITY. I think that this lends itself nicely into a RESTful way of thinking. The ‘Survey’ AGGREGATE ROOT can be a RESTful resource and all the other ENTITIES can be nested resources. Here is the URL mappings generated by Grails for the above model.

Aggregates lend themselves nicely to RESTful way of thinking.

Notice how there is no direct route for questions and answers. For me to access a question, I have to go through a survey -

/surveys/{SurveyId}/questions/{questionId}

and for me to access an answer, I’ll have to go through the survey as well -

/surveys/{SurveyId}/questions/{questionId}/answers/{answerId}

Stairway to the RESTful purist’s heaven :)

Enough self indulgence. Back to the topic now. Luckily for us, the folks from whom we inherited the application, did a decent job of organizing their domain models cohesively. So all the entities related to one aggregate were clubbed together in a single module. The definition of the aggregates was clear but the boundaries between them wasn’t explicit. What do I mean by that? If you take a look at the class diagram above, you will notice that the ‘Survey’ AGGREGATE ROOT has a direct reference to ‘Project’ and ‘Customer’ ENTITIES which can - and in this case are - entire AGGREGATE ROOTS on their own. So there is no explicit boundary between these AGGREGATE ROOTS. If the boundary was explicit, then instead of having a reference to the ‘Customer’ and ‘Project’ AGGREGATE ROOTS themselves, the ‘Survey’ AGGREGATE ROOT would have two extra fields - ‘CustomerId’ and ‘ProjectId’ - treating them as ENTITIES from a separate system.

Alright, so hopefully the above paragraph did a decent job in explaining what an explicit aggregate boundary is. But why do we need one you ask? Well, not having explicit boundaries means that your code is assuming that the other AGGREGATE ROOTS come from the same system containing it. That may not always be true. Especially in a microservices architecture if you want to take advantage of polyglot persistance. Maybe I feel that one of my aggregate roots is best stored in a document db like MongoDB or RavenDB. But I’d still want to store my Customer information in a relational database. Decoupling your data by breaking down your domain models along aggregate boundaries is a great way to ensure that your data isn’t tightly coupled at the storage layer. This allows us to scale the application, both in terms of load and functionality, without different modules stepping on each others toes.

Having explicit aggregate boundaries also enables us to introduce HATEOS into our RESTful resources. So instead of our Survey resource looking like -

{
  Name: "...",
  FriendlyId: "...",
  Questions: [],
  Answers: [],
  Rules: [],
  Customer: {},
  Project: {}  
}

we can make it look something like this -

{
  Name: "...",
  FriendlyId: "...",
  Questions: [],
  Answers: [],
  Rules: [],
  Links: [
      { "rel": "self", "href": "/surveys/friendlyId" }
      { "rel": "customer", "href": "/customers/customerId" },
      { "rel": "project", "href": "/projects/projectId" }
  ]
}

Which (if you are willing to ignore that JSON is not a hypermedia format) allows us to embrace HATEOS in our RESTful design. Check out the ‘Richardson Maturity Model’ for more info on this.

I think this is enough precursor to get us started with splitting our domain models. Up next, we will be covering how to split up your data at the storage level.