Towards Modern Architecture of Cloud Applications
Nitin Gaur
AI~Digital Engineering & Solutions Lead | GenAI Consulting, Architecture, Thought Leadership
This article is latest in series of my articles on microservices, starting from 2016 capturing its evolution and changing landscape in last 8+ years. In my post few months ago, Are we developing microservices all wrong? I did touch upon the facts that industry has acknowledged. Microservices architecture has turned out to be another example of what Martin Fowler described as semantic diffusion.
Situation
Customer: I want to modernize/develop my application using microservices and run in Cloud.
Solution consultant: May I know the factors driving the wish/decision to go with microservices?
Sales executive (to Solution consultant): Align your solution with what customer is asking for.
This is a typical situation wherein solution architects find themselves quite often whether it is a formal or informal request for proposal. Proposing a solution without due diligence and agreeing to what the customer is saying often results in not only higher implementation cost but also higher maintenance cost and thus increasing TCO (Total Cost of Ownership).
Industry tried to resolve this by relying on tools: initially DevOps toolchain, then SRE tools or more recently putting everything under Platform engineering. Large organizations have matured these practices and built internal developer platforms including self-service developer portal and instant provisioning of CI/CD pipeline and containers (some flavor of Kubernetes). This may work in the short term but now they need to maintain the platform too because technology keeps changing / upgrading. So, what’s next? Should we take 1-2 steps back and course-correct?
Taking one step back (via development framework)
Let me start by recent quote from Kelsey Hightower, Google’s former engineer and Kubernetes advocate - “If people would just start with organized modular code, we can make the deployment architecture an implementation detail”.
Well, it sounds like a wish-list or something was in making? In fact, yes! A team of Googlers published a paper called Towards Modern Development of Cloud Applications in Jun 2023. It says that microservices based approach conflate logical boundaries (how code is written) with physical boundaries (how code is deployed). And this is where the issues start. So, what is the solution? Should we start using their programming framework Service Weaver that attempts to bring this flexibility and shift-left to development? Answer is No…. I’ll keep it on my radar for assessment then moving to trial before going for adoption (may take few years).
Taking two steps back (via logical design)
Logical View/design consists of several considerations such as architecture style for grouping of business capabilities. For example - what are the drivers to choose one over another? How can I reduce coupling between modules? Which modules need entities and rich domain model?
Fundamentally, designing multiple services does not necessarily mean they need to be developed and deployed independently. In reference to 4+1 architecture by Philippe Kruchten, just because you’ve identified multiple logical views (services), does not mean that each must have their own independent deployment (physical view) or codebase (development view).
A logical boundary defines what the functional capabilities are; then, we often end up creating a source repository (development view) for all the code we write to implement those capabilities. Then we take that source repository and turn that into a deployment artifact.
Even if it is a mono-repo, it’s still very similar where it’s a one-to-one from logical to physical.
Logical boundaries are not same as Physical boundaries. For illustration, see below where four blue boxes represent logical boundaries whereas the red box represents the deployed artifact.
How to approach logical design (view)
Always start by defining logical boundaries (referred to as bounded context in world of domain-driven design). You need to address coupling when designing service boundaries—how you’re coupling, when you’re coupling, the degree of coupling etc. Using a framework to gloss over won’t lead to a better-designed system. Always strive for Low coupling & High cohesion as desired characteristics of your system. Here is quick definition of coupling and cohesion:
Coupling defined as “degree of interdependence between software modules”, could manifest as same process calls between classes or via network calls in modern distributed systems. Cohesion defined as “degree to which the elements inside a module belong together”, could manifest at class level (via design principles) or via grouping similar functional capabilities together.
You may be wondering why I have not mentioned DDD yet (Domain-Driven-Design), the basis of service decomposition in microservices architecture. I would recommend it but not in every case. Unless you have complex domain like e-commerce or online booking system, you can skip starting with DDD and instead focus on cohesion i.e. grouping of desired business operations of the system.
Remember, logical architecture/view of an application has nothing to do with microservices architecture which is a deployment concern and part of physical view.
Let’s look at common logical architecture patterns e.g. Layer, Clean, Vertical slice architecture.
Layered and Clean architecture both focus on separation of technical concerns. Below are two versions, one is traditional on the left while the other is DDD inspired version on the right.
On the other hand, Vertical Slice architecture focuses on functional cohesion i.e. logical boundaries. Below diagrams show clean architecture (left) split into vertical logical boundaries (for example: CRM, Finance, Ordering, Shipping). There can be one of more feature Slices within a logical boundary.
?Benefits of Vertical Slice architecture
The benefit of vertical slice architecture is that it allows each slice / module to have its own set of layers (see below). This flexibility is seen as more agile (pragmatic) approach over maintaining code consistency via layers.
Other benefits are lesser merge conflicts, simpler unit testing and better developer experience.
With vertical slicing, the system may still have coupling between vertical slices but how you choose to handle the coupling is a different topic. You may have workflows across vertical slice boundaries or may connect them using messaging or introduce event-driven architecture to remove temporal coupling.
How does it help in microservice deployment (initially or later)
You may ask that by vertical functional slices architecture and keeping code in a single repo, aren’t we going towards modular monolith deployment? If yes, how easy is to break any of the functional modules into separate processes as microservices?
Below is trivial illustration of a loosely coupled monolithic deployment’s seamless decomposition into separate processes provided they are not making in-process calls.
I’ll discuss the above questions (along with few others) in my next article focused on the Development View which not only plays a significant role in estimating your development cost but also critical in successful realization of the logical view into the physical view.
Thank you for reading this article. If you have any questions or comments, please feel free to post them here. Stay tuned for next one…
Alliances Business Development, Channel Partners Network
4 个月A partner / marketing idea for you, would be great contribution here: https://devopsflow.net/solutions/cloud-native/
AVP & Delivery Head | Digital Transformation Leader | An avid practitioner of technology such as AI | Cloud | SRE | IoT
6 个月Very informative, funny I read two articles today (another one was on Cloud Exit), both feels like suggesting going 10 years back.