Today, we will explore the captivating realm of microservices architecture! With substantial experience in designing systems based on microservices, I’m excited to share insights and practical tips with you. Over the years, I’ve gained invaluable expertise in crafting and implementing extensive systems.

A significant credit goes to “Building Microservices” by Sam Newman. This book not only inspired me to explore the intricacies of microservices but has also proven to be an indispensable guide through challenging project endeavors. I wholeheartedly recommend it to anyone intrigued by the topic! Join me in unraveling the fascinating world of microservices.
Here are some situations where choosing microservices might be beneficial:
independent scaling of different services based on their individual needs.concurrently, allowing faster release cycles for individual components. 🚀entire application, microservices can help achieve this goal.At this point you may admit that microservices are the best architecture. But no, I strongly advise against using microservices if you really don’t need them. Microservices architecture (as a distributed architecture) is one of the most difficult to write and maintain. Especially when the team is small and inexperienced.
Microservices architecture is brilliant and most powerful in certain cases. It should be an evolution of the system architecture rather than a starting point.
Microservices architecture has one big problem: it is often written in the wrong way. In the next lines of this article, we will examine the most common mistakes associated with implementing microservices, such as coupling.
In the world of microservices, overall coupling is like figuring out how much each service relies on the others. Keeping it low is the secret sauce for letting each service shine on its own - scaling up, doing its thing, and staying hassle-free.
Remember: There will always be some level of coupling, so don’t be afraid of that! You have to make some trade-offs and decide what is best for your project.
Data coupling, a prevalent aspect in microservices architecture, arises when microservices share data. When one microservice relies on a data structure or content created by another, changes in data format or content can affect multiple services. An example would be when multiple microservices use the same database.
Picture this: you have a user data set in database. It’s used by microservice A and B. In case of adding a new field to database schema it the best case you need to update both schemas in the both microservices or worse, forgot do do that, and not updated microservice can just stop working (especially when you are using strongly typed database)
*You can push a schema to another library and share it between A and B microservices. I highly not recommed such approach. This requires to maintain another library and it’s harder to see a border between these microservices.
Ideally, each microservice should have its own database, as in the diagram below:
But! Sometimes this can be inefficient or you can’t deal with additional databases. In such a case, it can be quite a good approach to create another service (usually called DAS - Data Access Service), which is responsible for communicating with the database and only does it.
Domain coupling comes into play when microservice A needs to connect with microservice B because B relies on the functions offered by A. To work properly microservice A needs to use microservice B. Picture another situation: Microservice A, C and D needs microservice B to work properly.
Typically, domain coupling is a weak coupling, so it’s not so dangerous to our architecture. However, when a microservice finds itself interacting with numerous others, it hints at a scenario where a significant chunk of logic is centralized. This situation could potentially impact the intended benefits of a distributed and independent microservices architecture.
Temporary coupling occurs when microsevice A needs to pass data through microservice B, so that microservice C can read it. We can compare it to prop drilling. Such coupling destroys the independence and hermetization of microservices.
Dividing serves as a fundamental strategy for breaking down monolithic systems into manageable microservices. This pivotal step involves dividing the system’s functionalities to create independently deployable and scalable services. As we embark on this exploration, we’ll dive into various techniques, each offering a unique perspective on how to effectively partition a system and how we can define the boundaries of our microservices.
You don’t want to implement a huge part of the system just when you have changed a piece of specific logic. That’s what dividing by change is all about. You can highlight which parts of the system need a lot of changes and which parts are rather outdated. You can separate them by simply moving the frequently changed part to a new microservice - this approach provides better separation for frequently changed/expanded logic, which is more dangerous and less vulnerable.
When you’re breaking down your system into microservices, think about the parts that could use a speed boost or need to handle more action. Consider the areas in your system that could use a bit of a performance upgrade or need to be able to scale up on their own.
To save some money on scaling, it’s a smart move to shuffle all the heavy-duty tasks into their own microservice. This way, you can tweak the performance of these parts separately, making your whole system more flexible and cost-effective when it comes to handling more workload.
DDD encourages a strategic approach to dismantling complex monolithic systems by viewing them through the lens of the business domain. Bounded Contexts become the blueprint, defining clear boundaries and ensuring each microservice encapsulates a distinct facet of business logic. This decomposition by subdomains allows for the creation of focused, cohesive microservices, each contributing a unique and valuable business capability. The move away from shared databases promotes autonomy, enabling each microservice to manage its data independently. As we embark on this journey inspired by DDD, we reshape not only our technical architecture but also align our systems with the true essence of our business, fostering a more agile, scalable, and purpose-driven microservices landscape.
Simply put, if updating one microservice necessitates updating another, it’s a sign that the service boundaries may need reevaluation. The goal is to have independent, loosely coupled microservices, and dependencies between them may indicate a need for adjustment.
At first: remember to use SemVer. It’s a global standard and you should stick to it.
Let’s talk real talk - versioning APIs introduces complexities, leading to fragmented codebases, increased maintenance overhead, and potential confusion for developers and users alike.
The idea is to prioritize simplicity whenever feasible - embracing microservices architecture from the get-go, where possible, to sidestep potential pitfalls like codebase complexities and unnecessary fragmentation. By fostering a mindset of continuous improvement and maintaining backward compatibility, we create an environment where versioning becomes more of an exception than the rule. There are instances where versioning becomes a lifeline, it’s truly necessary and needed - in such case we should do versioning carefuly and make it as simple as we can.
Picture this: You find yourself in a situation where a breaking change in one API endpoint is inevitable. Despite the challenge, let’s explore how we can handle it with simplicity in mind. There are a couple of strategies we can employ:
In both cases, these versions need to coexist. When introducing the latest endpoint, make necessary interface and logic changes, but strive to reuse as much code as possible from the previous endpoint. Limit modifications to the essential components, ensuring a more straightforward maintenance process for both versions. It’s all about efficiently managing changes while keeping the codebase as clean and maintainable as possible.
*The adapter design pattern can be beneficial in this situation.
Imagine this scenario: You’re in the midst of a major transformation-overhauling architecture, adopting cutting-edge technologies, and introducing a slew of innovative functionalities. As part of this evolution, there’s a critical decision to create a new version of your API, treating it as a standalone project. This strategic move not only positions you to gracefully retire the current version eventually but also ensures the ongoing maintenance and support of the latest iteration.
Enter API Gateway versioning, a powerful ally in this journey. By configuring a special URL, say /api/v2/, through the API gateway, you gain the ability to seamlessly redirect traffic between the old and new APIs. It’s like having a dedicated traffic cop, directing requests intelligently based on specific rules.
Tailoring these rules can even extend to different domains, allowing for a personalized redirection experience-for instance, directing traffic from one domain (client, e.g. A) to v1 while smoothly transitioning another to v2. Client (api consumers) A and B uses the same URI but API Gateway configuration determines that client A uses API v1 but client B uses API v2
This dual-project approach not only accommodates the evolving needs of your current system but also sets the stage for a future-ready API landscape. It’s a dynamic strategy, combining the wisdom of retiring the old with the agility of embracing the new, all orchestrated through the strategic prowess of API gateway versioning.
Let’s sum it all up and see the good and bad points of microserfices architecture.
higher overhead.Let’s start with the fact that you probably don't need a microservices architecture. This approach and the desire to maintain a “pure microservices architecture” can lead to wasting a lot of time maintaining the architecture. In most cases, you probably need something called a three-tier monolith (or modular monolith). And remember that the architecture should grow with the project. Microservices should not be a starting point, but an evolution of the project.
That’s all about microservices. Thanks for reading! If you enjoyed it, I will be very pleased if you follow my GH account: https://github.com/RafalKostecki