1. Home
  2. Docs
  3. Mastering the C4 Model: F...
  4. Module 3: Level 2 – Divin...
  5. Technical Decomposition

Technical Decomposition

Breaking systems into separately runnable or deployable units (web apps, microservices, databases)

The core purpose of the Container Diagram (Level 2) is to perform a technical decomposition of your software system — breaking it down from a single opaque box (as seen in the System Context) into its major separately runnable or deployable units.

This decomposition is not arbitrary. It reflects real architectural and operational boundaries: units that can be built, tested, deployed, scaled, monitored, secured, and (in many cases) replaced or retired independently. These are the containers in the C4 Model.

What Makes Something a Container?

A container is:

  • A distinct, cohesive piece of software that executes code or hosts data.
  • Separately runnable or deployable — meaning it can be started, stopped, restarted, scaled, or moved without necessarily affecting other containers.
  • Has a clear runtime boundary — it runs in its own process, container image, serverless execution environment, or database instance.

This definition deliberately aligns with how modern systems are actually built and operated — whether monoliths, microservices, serverless, or hybrid architectures.

Common Types of Containers You Will Identify

Here are the most frequent categories you’ll encounter when decomposing almost any real-world system:

  1. Web Applications / Backends
    • Traditional server-side apps (Spring Boot, .NET Core, Django/Flask, Node.js Express)
    • Single-page application (SPA) frontends when hosted separately (React, Angular, Vue served from their own domain/server)
    • API-only services / REST/GraphQL backends
    • Example: “Customer API”, “Admin Web Portal”, “Public Website”
  2. Mobile Applications
    • Native iOS/Android apps
    • Cross-platform apps (Flutter, React Native)
    • Treated as containers because they run independently on user devices and communicate via APIs
    • Example: “iOS Banking App”, “Android Field Service App”
  3. Databases & Data Stores
    • Relational databases (PostgreSQL, MySQL, SQL Server, Oracle)
    • NoSQL/document stores (MongoDB, DynamoDB, Cassandra)
    • Caches (Redis, Memcached)
    • Search engines (Elasticsearch, OpenSearch)
    • File/blob storage (S3, Azure Blob, MinIO)
    • Example: “PostgreSQL – Customer Data”, “Redis – Session Cache”
  4. Message Brokers & Event Streams
    • Queues (RabbitMQ, ActiveMQ, AWS SQS)
    • Event streaming platforms (Kafka, Amazon Kinesis, Azure Event Hubs)
    • Example: “Kafka – Order Events”, “RabbitMQ – Notifications”
  5. Serverless Functions / Batch Jobs
    • Individual functions or groups of related functions (AWS Lambda, Azure Functions, Google Cloud Functions)
    • Scheduled/cron jobs, ETL processes, background workers
    • Example: “Payment Processing Lambda”, “Daily Report Generator”
  6. API Gateways / Reverse Proxies / Ingress
    • Kong, Apigee, AWS API Gateway, NGINX, Traefik
    • Often act as containers when they perform routing, rate limiting, authentication
    • Example: “API Gateway”
  7. Third-Party / External SaaS Services (treated as black-box containers)
    • When they form a distinct integration point with their own identity and deployment boundary
    • Example: “Stripe – Payment Processing”, “SendGrid – Email Delivery”, “Auth0 – Identity Provider”

How to Perform the Decomposition (Step-by-Step)

  1. Start from the System Context Take your single “Software System” box and ask: “What major runnable/deployable pieces live inside this boundary?”
  2. Follow the data and control flow
    • Where does user input arrive? → Likely a web/mobile frontend or API gateway
    • Where is business logic executed? → Backend services
    • Where is data persisted? → Databases
    • Where are events published/consumed? → Message brokers
    • Where are background tasks run? → Workers/functions
  3. Apply the “independence test” Could this piece be:
    • Deployed to production independently?
    • Scaled separately (e.g., more instances of the API than the database)?
    • Developed by a different team?
    • Replaced with a different technology without rewriting everything? If yes → strong candidate for its own container.
  4. Group logically when needed
    • A monolithic backend that can’t be split yet → one container (“Monolith Backend”)
    • A set of tightly coupled microservices that always deploy together → group as one container until boundaries become clear
  5. Name containers clearly and consistently
    • Use noun phrases that describe responsibility + technology when helpful
    • Examples: “Web Application (React SPA)”, “Order Service (Spring Boot)”, “PostgreSQL – Orders”, “Kafka – Event Bus”

Key Outcomes of This Decomposition

By breaking the system into containers, your Container Diagram will reveal:

  • The technical architecture style (monolith, microservices, serverless, hybrid)
  • Communication patterns (synchronous HTTP, asynchronous messaging, direct DB access)
  • Technology diversity and potential complexity
  • Deployment and scaling realities
  • Risk hotspots (e.g., single shared database, heavy reliance on one broker)

This level of decomposition is where most architecture conversations happen in practice — because it shows how the system is really built and runs without drowning in code-level details.

In the hands-on section ahead, you’ll take a real or example System Context and systematically decompose it into containers using C4-PlantUML Studio — identifying boundaries, assigning responsibilities, choosing technologies, and drawing the relationships that make the system function.

Get ready to slice that big box into meaningful, deployable pieces — this is where the architecture starts to feel real and actionable.

How can we help?