While there exists a spectrum of scenarios where an initial focus on a microservices-centric architecture may be beneficial, such instances are rare pearls amidst a vast ocean of more practical alternatives. Predominantly, systems find themselves better nurtured under the wing of a monolithic entity - a concept recently brought to light by the case study of Prime Video that ignited a wave of interest across the digital realm.
When the scale of your operations nears the enormity of behemoths like Netflix or Amazon, the feasibility of microservices may begin to shimmer. However, remember that even digital titans like GitHub and Shopify operate their primary applications as monoliths. These giants deftly manage millions of lines of code with a robust team of developers collaborating on them. Unless your system calls for an equally extensive codebase or requires such a large legion of programmers, prudence should be your guide when considering the realm of microservices.
For those embarking on fresh projects, such advice may prove valuable. But what of those who have already stepped into the microservices arena, perhaps a tad prematurely? How does one navigate the labyrinth back to the monolith? Here are a few carefully curated insights:
Focus on consolidating crucial, dependent components first. The most detrimental aspect of microservice mania is the fragmentation of a single, coherent workflow across multiple systems. This could be a registration process, a checkout sequence, or accessing specific content. Microservices can make these processes unwieldy and prone to errors by necessitating cross-system coordination, synchronization, and more. Therefore, the road back to monolithic architecture should commence here.
Cease digging deeper. It's impossible to tidy a mess while you're still creating more. Refrain from introducing new microservices and designate one of the existing ones as the new central hub for incoming functionality. This nucleus should gradually draw in the majority of other microservices. Above all, the priority is to prevent the situation from further deteriorating.
Delay dealing with isolated performance hotspots. When executed correctly, microservices tend to target a specific, isolated, and performance-critical segment of the system. Although your entire web application might be running on Ruby on Rails, there could be one segment that experiences load spikes and can't be cached. In such cases, using a faster, albeit more cumbersome, programming language like Rust or Go could be beneficial. This is the essence of microservices done right.
Master the art of dividing large systems using modules, not networks. A considerable part of the motivation for microservices stems from the misconception that network boundaries can resolve issues if a large system's architecture using modules and namespaces seems daunting.
Aim to eliminate the most esoteric implementations. A frequent pitfall of microservices is the inclination to adopt a plethora of programming languages, frameworks, and ecosystems. However, this can lead to a system juggling numerous programming languages and frameworks, complicating comprehension and contributing to the common microservices issue of the system becoming a mystery to everyone.
Simplicity demands that we not invite the complexities of distributed systems into the initial architectural planning. It's plausible that a system might eventually evolve into a complex, distributed architecture that uses microservices judiciously, but this should only occur if it originated from a simple, monolithic design.
A thorough understanding of how to dissect large problem areas into aesthetic domain models can be gleaned from Eric Evan's Domain-Driven Design. However, before aspiring to such strategic, architectural heights, one should first master the basics of tactical programming, as delineated in Kent Beck's Smalltalk Best Practices and Martin Fowler's Patterns of Enterprise Application Architecture.