The evolution of software architecture has often been characterized by a swinging pendulum, oscillating between the consolidation of monolithic structures and the extreme fragmentation of microservices. For a significant period following 2010, the industry was captivated by the success of massive-scale platforms like Netflix, leading to a widespread belief that splitting applications into dozens of microservices was the definitive best practice. This trend often resulted in the creation of "distributed hell," where simple e-commerce applications were fragmented into 47 separate microservices. This fragmentation introduced a level of complexity that was unnecessary for the vast majority of businesses that do not operate at the scale of millions of concurrent users. In response to the operational overhead and the "distributed big ball of mud" created by misapplied microservices, Self Contained Systems (SCS) have emerged as a pragmatic architectural alternative.
Self Contained Systems are designed as independent web applications that operate harmoniously within a larger ecosystem without requiring constant supervision or tight coordination. Rather than focusing on the size of the service—the "micro" aspect—SCS focuses on the boundaries of the system. An SCS is a vertical slice of an application, encompassing everything from the user interface to the data storage. This approach provides the modularity and team autonomy associated with microservices while retaining the operational simplicity and resilience typically found in well-structured monoliths. By prioritizing the integrity of the system boundary over technical purity, SCS allows organizations to deliver value quickly without the burden of distributed systems theory.
The Historical Trajectory of Enterprise Architecture
The current preference for Self Contained Systems is best understood by examining the waves of software development that preceded it. Each era solved specific problems but introduced new challenges that eventually led to the next architectural shift.
The Pre-2000s era is often described as the "Wild West" of software development. In large enterprise environments, this period was dominated by mainframes and COBOL. These systems were centralized and powerful but lacked the flexibility and agility required for the rapid iteration cycles of the modern web.
Between 2000 and 2010, the rise of monoliths became the dominant trend. This was particularly evident in the Java ecosystem with the emergence of J2EE (now known as Jakarta EE) and subsequently the Spring Framework. This era saw many successful greenfield projects where a single codebase managed all business logic, data access, and presentation. While effective, these monoliths frequently devolved into a "Big Ball of Mud"—a system where boundaries blurred, modularity vanished, and the structure became unmaintainable.
The post-2010 era was defined by the microservices explosion. Inspired by the scale of Netflix, developers began decomposing monoliths into tiny, single-purpose services. While this solved the "Big Ball of Mud" problem for hyper-scale companies, it introduced a new set of complexities for everyone else. Many teams found themselves managing a distributed network of services that required sophisticated tooling for distributed tracing, circuit breaking, and eventual consistency, often for applications that had only a handful of users.
Anatomical Components of a Self Contained System
A Self Contained System is not merely a "large microservice"; it is a vertical slice of functionality. To be truly self-contained, a system must incorporate several critical layers within its own boundary.
The first critical component is the User Interface (UI). In a traditional microservices or monolithic approach, there is often a shared frontend monolith. In an SCS architecture, each system owns its own UI. This means there is no shared frontend that must be coordinated across different teams. This allows for a user experience that can feel slightly different across different sections of an application—such as the transition from a cart to shipping to payment in Amazon's checkout process—which is viewed as a feature of autonomy rather than a bug.
The second component is the business logic. Each SCS contains the logic that makes sense as a coherent unit. By grouping related business rules together, the system avoids the need for synchronous API calls to other services to complete a basic task.
The third component is data storage. A fundamental rule of SCS is that each system must own its data completely. Shared databases are identified as the fastest way to couple systems, creating dependencies that negate the benefits of autonomy. By ensuring data is local to the SCS, the system achieves true independence.
Finally, an SCS is characterized by zero runtime dependencies on other systems. This means that the system can perform its core functions regardless of whether other systems in the ecosystem are currently online. This autonomy eliminates the need for complex patterns like circuit breakers, as there are no synchronous calls that could trigger a cascading failure across the network.
Comparative Analysis of Architectural Patterns
The choice between a monolith, Self Contained Systems, and microservices involves trade-offs in team dynamics, infrastructure, and speed of delivery.
| Aspect | Monolith | Self Contained Systems | Microservices |
|---|---|---|---|
| Team Size | 2-15 developers | 3-8 per SCS | 2-5 per service |
| Deployment Complexity | Low | Medium | High |
| Coordination Overhead | High (single codebase) | Low | Very High |
| Infrastructure Cost | Low | Medium | High |
| Time to Market | Fast (initially) | Medium | Slow (initially) |
| Debugging Difficulty | Easy | Medium | Hard |
| Technology Diversity | Limited | High | Very High |
The Fallacy of Distribution and the "Distributed Hell"
Distribution is not a free lunch; it introduces a set of inherent complexities that often outweigh the benefits for non-scale applications. Martin Fowler’s first law of distributed design, "Don’t distribute your objects," serves as a warning against unnecessary fragmentation.
Network failures, latency, retries, and the challenge of eventual consistency are all byproducts of a distributed architecture. When a system is split into too many microservices, these issues become the primary focus of the engineering team, diverting attention away from delivering business value.
Communication overhead is a primary driver of this complexity. Microservices typically rely on several types of communication:
- Synchronous APIs: Using
REST,GraphQL, orgRPCcreates tight coupling between services. If one service fails, the entire request chain fails. - Asynchronous Messaging: Tools like
KafkaorJMSreduce coupling but introduce challenges regarding message delivery and consistency. - Event-driven Systems: While powerful, these only work if the business domain naturally fits an event-driven model.
The risks of these communication patterns are evident in real-world failures. For instance, request-reply messaging can lead to duplicate shipments if the ERP backend is not idempotent. Additionally, the "N+1 problem" becomes a significant performance bottleneck; lazy-loading data over HTTP is described as a disaster compared to lazy-loading over JDBC.
Decomposing the Order Creation Process: Microservices vs. SCS
To illustrate the difference between a fragmented microservice approach and a self-contained approach, consider the FTGO online food delivery application.
In a microservice architecture, a client creates an order via an HTTP POST /orders request. The responsibilities for this action are scattered across multiple services:
- Order Service: The entry point that coordinates the request.
- Restaurant Service: Manages menu data and pricing.
- Consumer Service: Tracks the state of the consumer placing the order.
- Kitchen Service: Creates a
Ticketfor the chef. - Accounting Service: Authorizes the credit card.
If the Order Service uses synchronous request/response (such as REST or gRPC), the availability of the entire process is tied to the availability of every single collaborator. If the Accounting Service is down, the order cannot be created, and the user receives an error.
To solve this within a microservices framework, developers must implement complex patterns such as CQRS (Command Query Responsibility Segregation) and Saga patterns. The Order Service might maintain a replica of the restaurant menu to eliminate the need for synchronous calls. However, this introduces the overhead of managing data synchronization and eventual consistency.
In a Self Contained System approach, the goal is to eliminate these synchronous dependencies entirely. By creating a vertical slice that is substantial enough to keep a small team busy, the system can operate autonomously. Communication happens only when necessary, ideally asynchronously or through the UI itself, ensuring that the failure of one component does not paralyze the entire user journey.
Implementation Guidelines for Self Contained Systems
Transitioning to or implementing an SCS architecture requires a shift in mindset from technical purity to pragmatism. The following guidelines ensure the integrity of the system boundaries.
First, resist the urge to add synchronous API calls between systems. Doing so creates a "slippery slope" that leads directly toward a distributed monolith, where you have the worst of both worlds: the deployment complexity of microservices and the tight coupling of a monolith.
Second, ensure absolute data ownership. There should be no shared databases between SCS. If data from another system is needed, it should be replicated or handled via asynchronous events.
Third, size the systems based on team capacity. An SCS should be large enough to provide meaningful work for a small team (typically 3-8 developers) but not so large that it becomes an unmanageable monolith.
Fourth, accept that the best architecture serves the business. The focus should be on delivering value quickly and maintaining it easily. Architecture decisions should be driven by team size and complexity rather than trends appearing on technical social media.
Analysis of Architectural Resilience and Value
The shift toward Self Contained Systems represents a return to simplicity and a rejection of the "one-size-fits-all" mentality that plagued the microservices era. The core realization is that most projects do not need the hyper-scalability of Netflix; they need modularity.
SCS provides a middle ground that balances the strengths of both monoliths and microservices. From a developer's perspective, it offers the autonomy of microservices—the ability to develop, test, and deploy independently—without the operational nightmare of managing a distributed web of a hundred tiny services. From a business perspective, it improves time to market by reducing the coordination overhead that typically plagues large-scale microservice deployments.
The resilience of an SCS is a direct result of its boundaries. Because there are no runtime dependencies, the "blast radius" of a failure is contained within a single system. This is a significant improvement over distributed architectures where a single failing service can trigger a cascade of errors across the entire platform.
Ultimately, the value of an SCS lies in its pragmatism. It acknowledges that while distribution is a tool for scale, it is a liability for agility. By building systems as vertical slices, organizations can evolve their architecture incrementally. An SCS can be started as a larger module and broken down further only if and when the business complexity justifies the overhead. This approach ensures that the architecture remains a facilitator of business growth rather than a bottleneck.