Sponsor: Do you build complex software systems? See how NServiceBus makes it easier to design, build, and manage software systems that use message queues to achieve loose coupling. Get started for free.
Start with a Monolith! Why? Finding boundaries is a difficult thing to do and even harder to get right. Having to define those boundaries up-front for a microservices architecture is a recipe for additional and unneeded complexity. If you develop a loosely Coupled Monolith by applying the same principles when developing Microservices, you’ll be in a better place to refactor and change your boundaries as you gain insights.
Check out my YouTube channel where I post all kinds of content that accompanies my posts including this video showing everything that is in this post.
When starting a greenfield project with likely a small team, the business will understand the domain, however, the developers will likely not.
Understanding the domain and the problem space takes time. It takes time to understand the domain, the capabilities of the system your building, and most importantly, defining boundaries for those capabilities.
My analogy for this process is walking into a pitch-black room with only a small flashlight. You have to shine the flashlight around the room to get some insights to the room. How big is the room, how high are the ceilings, are there any objects in the room, etc.
You don’t immediately know all this, it takes shining the light around to discover all these things.
If you do not know the domain your building a system for, it means you would know not know what the boundaries are. If you do not know what the boundaries are, you should not start with a microservices architecture.
Defining boundaries is one of the most important things to get right, yet the hardest thing to do. It takes iterations. As you gain more insights by shining the flashlight. As you discover more your models and boundaries will change.
Because of this iteration, it is much more feasible to make these refactoring and rewrites using a monolithic architecture.
That does not mean a big ball of mud. A monolith does not need to be a big ball of mud.
Define boundaries within your monolith. Understand how they need to interact and make this interactions loosely coupled.
Essentially, use the same principles you would in a microservices or service oriented architecture and apply them to a monolith.
Microservices and Service Oriented Architecture is about creating units that own a set of behaviors and backing data. This should be driven by business capabilities, not technical concerns.
Each bounded context is a boundary. For more info on finding them, check out my Context is King: Finding Service Boundaries series.
The other benefit of (micro)services is separation of schema/data. Each boundary must own it’s own data. In a monolith this could be accomplished by simply separating the schema either into separate physical databases or even in the same database instance.
Each boundary should own it’s own data. No other boundary should be able to read or write to it’s schema. Never. This is a conceptual idea.
As mentioned, you could use a single database instance but have clearly defined boundaries within it.
If you want to be as loosely coupled as possible between boundaries, then communicating via asynchronous messaging will allow you to ultimately split a monolith into (micro)services.
Each boundary will send command and publish events to a message broker. Other boundaries within the monolith will be consumers of those messages.
Messages will be going outbound from one boundary to a message broker, and back into the monolith to other boundaries.
If you choose for scaling reasons (either performance or developer teams) to carve off boundaries into independent services, that’s possible as you were never coupling beyond messages within the monolith.
Now you can see your monolith was loosely coupled. It was a single codebase to deploy a single unit but possible to separate into multiple codebases to multiple deployable units.
Start with a Monolith
You’re going to get boundaries wrong. And since you will gain more insights into the domain, you will need to refactor and rewrite. Having a single codebase as a single deployable unit of a Monolith at the beginning will be much simpler than having to deal with all the extra complexities of trying to refactor multiple services and their interactions.