Backend Development 17 min read

From Monolithic to Distributed Architecture: A Case Study of an Inventory System and CQRS Refactoring

This article explains the concept of software architecture, compares monolithic and distributed styles, discusses their advantages and drawbacks, and details a real-world inventory system migration from a monolithic design to a distributed architecture using functional and business decomposition, CQRS, and code refactoring.

IT Architects Alliance
IT Architects Alliance
IT Architects Alliance
From Monolithic to Distributed Architecture: A Case Study of an Inventory System and CQRS Refactoring

Before discussing architecture styles, the article defines software architecture as an abstract structure composed of components and their dependencies, and explains why choosing an architecture is essential for solving functional and non‑functional problems such as cost reduction, faster releases, and system stability.

What is architecture? It is the abstraction of a system’s components and their relationships, guiding technology and middleware selection to meet business requirements.

Purpose of choosing an architecture style is summarized as the “Three‑Better Principle”: better cost efficiency, faster deployment, and improved stability. While any style can meet functional needs, a good style also enhances non‑functional attributes like scalability and reliability.

Monolithic Architecture is introduced with three types:

Big‑Mud monolith – no layering, tightly coupled modules (hard to split).

Layered monolith – typical MVC three‑layer structure.

Modular monolith – evolves from layered monolith by adding business modules.

Advantages of monoliths include simple development, easy large‑scale changes, straightforward testing, clear deployment, and effortless horizontal scaling. However, as business grows, monoliths suffer from code bloat, complexity, slower development, long deployment cycles, poor scalability, stability issues, and reliance on outdated technology stacks.

The article then presents a concrete inventory system case study . Initially, a single service handled product inventory, supporting both API and web management. Rapid early growth exposed stability problems during high‑traffic promotions, leading to memory exhaustion and service crashes.

Transition to Distributed Architecture is driven by the need for high availability, scalability, fault tolerance, and clearer business code. The distributed design follows the core idea of splitting the system by functionality, business domain, or events, while balancing CAP trade‑offs.

Two major steps are described:

Functional decomposition : split services by business-facing functions (e.g., inventory query, stock deduction) without major code changes, deploying each function to separate clusters.

Business decomposition : analyze the domain model, define clear bounded contexts (e.g., inventory initialization, stock maintenance, deduction, alerts), and refactor code accordingly.

During business decomposition, the article emphasizes four principles: understand the business model, minimize cross‑module dependencies, evaluate impact size, and avoid altering business logic unless fixing bugs.

To illustrate the refactoring, the original @Service public class SkuMainServiceImpl implements SkuMainService { ... } is shown, highlighting problems such as same‑layer references, violation of Dependency Inversion, and God‑class behavior.

The refactored version introduces a dedicated business layer:

@Service public class SkuMainBusinessServiceImpl implements SkuMainBusinessService { ... }

Key changes include extracting read/write responsibilities into separate services, applying CQS (Command‑Query Separation) and SRP (Single Responsibility Principle), and delegating caching and MQ operations to specialized components.

After splitting, the system adopts CQRS at the service level: read services query Redis caches, while write services handle commands, update Redis, and emit asynchronous messages for persistence and downstream processing.

The article discusses the trade‑offs of CQRS, recommending to start with read‑side separation because it is simpler and improves stability, while write‑side changes are riskier.

Finally, distributed transaction challenges are addressed. Traditional XA‑based two‑phase commit is heavy and unsupported by many modern data stores (e.g., MongoDB, Kafka). The solution relies on eventual consistency, idempotent operations, and compensating actions rather than strict ACID across services.

In summary, the migration from a monolithic inventory system to a distributed, CQRS‑based architecture demonstrates how functional and business decomposition, code refactoring, and careful handling of consistency can achieve high availability, scalability, and maintainability for backend services.

distributed systemssoftware architecturebackend developmentrefactoringCQRSmonolithic
IT Architects Alliance
Written by

IT Architects Alliance

Discussion and exchange on system, internet, large‑scale distributed, high‑availability, and high‑performance architectures, as well as big data, machine learning, AI, and architecture adjustments with internet technologies. Includes real‑world large‑scale architecture case studies. Open to architects who have ideas and enjoy sharing.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.