Visual Paradigm Desktop VP Online

Architectural Decomposition Guide: Refining C4 Container Diagrams to Component Diagrams

Subject: Internet Banking System – Single-Page Application (SPA) Module


Executive Summary

This guide provides a comprehensive methodology for transitioning from Level 2 (Container Diagrams) to Level 3 (Component Diagrams) within the C4 Model framework. Using the provided Internet Banking System as a case study, we demonstrate how to “zoom in” on the Single-Page App container to reveal its internal client-side architecture, logical groupings, and interaction patterns.

💡 Quick Win: Component diagrams are living documents. Update them as your code evolves, or they become technical debt rather than technical assets.


1. The C4 Model Context

The C4 model visualizes software architecture at four levels of abstraction. This guide focuses on the critical transition between the middle two layers:

  • Level 2: Containers: Represents independently deployable units (e.g., Web Server, Database, Single-Page App). It answers what technology is used.

PlantUML Code:

@startuml
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml
‘ uncomment the following line and comment the first to use locally
‘ !include C4_Container.puml

‘ LAYOUT_TOP_DOWN()
‘ LAYOUT_AS_SKETCH()
LAYOUT_WITH_LEGEND()

title Container diagram for Internet Banking System

Person(customer, Customer, “A customer of the bank, with personal bank accounts”)

System_Boundary(c1, “Internet Banking”) {
Container(web_app, “Web Application”, “Java, Spring MVC”, “Delivers the static content and the Internet banking SPA”)
Container(spa, “Single-Page App”, “JavaScript, Angular”, “Provides all the Internet banking functionality to customers via their web browser”)
Container(mobile_app, “Mobile App”, “C#, Xamarin”, “Provides a limited subset of the Internet banking functionality to customers via their mobile device”)
ContainerDb(database, “Database”, “SQL Database”, “Stores user registration information, hashed auth credentials, access logs, etc.”)
Container(backend_api, “API Application”, “Java, Docker Container”, “Provides Internet banking functionality via API”)
}

System_Ext(email_system, “E-Mail System”, “The internal Microsoft Exchange system”)
System_Ext(banking_system, “Mainframe Banking System”, “Stores all of the core banking information about customers, accounts, transactions, etc.”)

Rel(customer, web_app, “Uses”, “HTTPS”)
Rel(customer, spa, “Uses”, “HTTPS”)
Rel(customer, mobile_app, “Uses”)

Rel_Neighbor(web_app, spa, “Delivers”)
Rel(spa, backend_api, “Uses”, “async, JSON/HTTPS”)
Rel(mobile_app, backend_api, “Uses”, “async, JSON/HTTPS”)
Rel_Back_Neighbor(database, backend_api, “Reads from and writes to”, “sync, JDBC”)

Rel_Back(customer, email_system, “Sends e-mails to”)
Rel_Back(email_system, backend_api, “Sends e-mails using”, “sync, SMTP”)
Rel_Neighbor(backend_api, banking_system, “Uses”, “sync/async, XML/HTTPS”)
@enduml

 

  • Level 3: Components: Represents logical groupings of code within a container. It answers how the container is structured.

PlantUML Code:

@startuml
!include <C4/C4_Component>

LAYOUT_WITH_LEGEND()

title Component Diagram for Single-Page App

Container(web_app, “Web Application”, “Java, Spring MVC”, “Delivers the static content and the Internet banking SPA”)
Container(backend_api, “API Application”, “Java, Docker Container”, “Provides Internet banking functionality via API”)

Container_Boundary(spa_boundary, “Single-Page App”) {
Component(sign_in_controller, “Sign In Controller”, “JavaScript, Angular”, “Allows users to sign in to the Internet Banking System”)
Component(accounts_summary_controller, “Accounts Summary Controller”, “JavaScript, Angular”, “Provides a summary of the customer’s bank accounts”)
Component(security_service, “Security Service”, “JavaScript, Angular”, “Handles authentication and authorization”)
Component(banking_api_facade, “Banking API Facade”, “JavaScript, Angular”, “A facade for the internal Internet Banking API”)
}

Rel(web_app, sign_in_controller, “Delivers”, “HTTPS”)
Rel(web_app, accounts_summary_controller, “Delivers”, “HTTPS”)

Rel(sign_in_controller, security_service, “Uses”)
Rel(accounts_summary_controller, banking_api_facade, “Uses”)
Rel(security_service, banking_api_facade, “Uses”)

Rel(banking_api_facade, backend_api, “Makes API calls to”, “async, JSON/HTTPS”)

@enduml

📋 Key Distinction

Level Audience Detail Purpose
Container Architects, DevOps, Stakeholders Deployment units Technology decisions, infrastructure
Component Developers, Tech Leads Code organization Implementation guidance, code structure

⚠️ Common Pitfall: Don’t confuse “Container” (C4 term) with “Docker Container.” In C4, a container is any independently deployable unit (web app, mobile app, database, file system, etc.).


2. Case Study: The Starting Point (Level 2)

Reference: Top left section (“Container diagram for Internet Banking System”)

 

 Before refinement, we analyze the Single-Page App container to understand its high-level responsibilities.

  • Target Container: Single-Page App
  • Technology: JavaScript, Angular
  • Responsibility: “Provides all the Internet banking functionality to customers via their web browser.”
  • External Dependencies:
    • Receives content from the Web Application (Java, Spring MVC).
    • Communicates with the API Application (Java, Docker Container).
    • Interacts with the Customer (User).

The Goal: We need to understand how the Single-Page App achieves these responsibilities. How is the UI organized? How is security handled on the client side? How does it talk to the backend?

🎯 Pre-Decomposition Checklist

Before you start decomposing, ask:

  • Is this container complex enough to warrant a component diagram?
  • Who is the audience for this diagram? (New developers? Architects? Auditors?)
  • What architectural decisions need to be documented?
  • Are there compliance or security requirements to highlight?

💡 Tip: Not every container needs a component diagram. Focus on containers that are:

  • Complex or critical to the business
  • Worked on by multiple teams
  • Have strict compliance/security requirements
  • New or undergoing major refactoring

3. Methodology: How to Refine a Container

The following 5-step process details how to transform the high-level container box into a detailed component diagram.

Step 1: Establish the System Boundary

Draw a dashed boundary line representing the container you are decomposing.

  • Action: In the case study, a large dashed box labeled “Single-Page App [container]” is created on the right side.
  • Rule: Everything inside this box represents code executing in the user’s browser. Everything outside is a server-side dependency.

✅ Best Practices for Boundaries

  • Use consistent visual styles: Dashed lines for boundaries, solid lines for relationships
  • Label clearly: Include the container name and technology stack
  • Keep it visible: Don’t let the boundary get lost in the diagram

⚠️ Pitfall Alert: Don’t include external libraries or frameworks as components inside your boundary unless they’re custom wrappers you’ve built. React, Angular, Vue, etc. are technologies, not your components.


Step 2: Identify Logical Components (Grouping)

Break the container down into cohesive units of functionality. For a Single-Page App (SPA), this usually follows an MVC (Model-View-Controller) or MVVM pattern.

  • Technique: Look for Controllers (UI logic), Services (Business logic), and Facades (API abstraction).
  • Case Study Application:
    • Controllers: Sign In Controller and Accounts Summary Controller. These handle specific user interactions.
    • Services: Security Service. This handles cross-cutting concerns like authentication.
    • Integration: Banking API Facade. This abstracts the backend communication.

🧩 Component Identification Strategies

Strategy 1: By Responsibility

── Authentication Components
── Business Logic Components
── Data Access Components
── UI Components

Strategy 2: By Feature/Domain

── Account Management
── Transaction Processing
── User Profile
── Reporting

Strategy 3: By Layer (Recommended for SPAs)

── Presentation (Controllers/Views)
── Application Services
── Domain Services
── Infrastructure (API clients, storage)

💡 Pro Tip: Aim for 5-10 components per diagram. Too few means you’re still at container level; too many means you’re too deep (heading toward class diagrams).

⚠️ Warning Signs:

  • God Component: One component that does everything (too broad)
  • Anemic Components: Components with no real responsibility (too narrow)
  • Circular Dependencies: Components that depend on each other (architectural smell)

Step 3: Assign Technologies and Responsibilities

For each component, define its specific job and the technology stack used.

  • Case Study Application:
    • Sign In Controller: Uses JavaScript, Angular to allow users to sign in.
    • Security Service: Uses JavaScript, Angular to handle authentication and authorization logic.
    • Banking API Facade: Uses JavaScript, Angular as a wrapper for the internal Internet Banking API.

📝 Naming Conventions That Work

DO:

  • ✅ SecurityService – Clear, descriptive
  • ✅ BankingApiFacade – Pattern + purpose
  • ✅ SignInController – Action + type
  • ✅ AccountSummaryComponent – Domain + type

DON’T:

  • ❌ Service1Manager2 – Meaningless
  • ❌ UtilsHelpers – Catch-all anti-pattern
  • ❌ AuthenticationSecurityService – Redundant
  • ❌ Ctrl – Unclear abbreviation

🎯 Responsibility Statement Formula

Write responsibilities using this template:

“[Component Name] [verb] [object] [purpose/constraint]”

Examples:

  • Security Service handles authentication and authorization”
  • Banking API Facade abstracts communication with the backend API”
  • Sign In Controller allows users to authenticate to the system”

💡 Tip: Use active verbs: manageshandlescoordinatesvalidatestransformscachesorchestrates


Step 4: Map Internal Relationships

Draw arrows between components to show data flow and dependencies.

  • Case Study Application:
    • Sign In Controller → Security Service: “Uses” (The controller delegates auth logic to the service).
    • Security Service → Banking API Facade: “Uses” (The service likely needs to call the API to validate tokens).
    • Accounts Summary Controller → Banking API Facade: “Uses” (To fetch account data).
    • Sign In Controller → Accounts Summary Controller: “Delivers [HTTPS]” (Likely a navigation flow upon successful login).

🔗 Relationship Labeling Guide

Use these standard relationship types:

Relationship When to Use Example
Uses General dependency Controller uses Service
Reads from Data access Component reads from Cache
Writes to Data modification Service writes to Database
Sends to Messaging/Events Service sends to Queue
Notifies Observer pattern Service notifies UI
Validates Security/Validation Service validates Token
Delegates to Delegation pattern Controller delegates to Service

🎨 Visual Hierarchy Tips

  1. Primary flows: Use solid lines with clear arrows
  2. Optional/async flows: Use dashed lines
  3. Data flow direction: Arrow points to the dependency
  4. Avoid crossing lines: Rearrange components for clarity
  5. Group related components: Use proximity to show relationships

⚠️ Common Mistake: Don’t label every relationship with detailed protocol information unless it’s architecturally significant. “Uses” is often enough. Save “[async, JSON/HTTPS]” for external boundaries or critical performance paths.


Step 5: Re-establish External Context (The “Context Anchor”)

A Component diagram must show what the internal components talk to outside the boundary.

  • Action: Copy the neighboring containers from the Level 2 diagram and place them outside the new boundary.
  • Case Study Application:
    • Top: Web Application (Java, Spring MVC) – “Delivers [HTTPS]” the static content.
    • Bottom: API Application (Java, Docker Container) – The Facade “Makes API calls to” this external system.

🌐 External System Visualization Rules

DO:

  • ✅ Gray out external systems (visual distinction)
  • ✅ Show only systems that your components directly interact with
  • ✅ Include protocol/technology details for external calls
  • ✅ Label the nature of the relationship

DON’T:

  • ❌ Show the entire ecosystem (keep it focused)
  • ❌ Use the same color as internal components
  • ❌ Omit external dependencies (creates false isolation)

💡 Pro Tip: External systems are your “ports and adapters.” They’re where your architecture meets the real world. Make them visible and explicit.


4. Deep Dive Analysis: The Resulting Component Diagram

Reference: Right side section (“Component Diagram for Single-Page App”)

The refinement reveals a structured client-side architecture that was hidden in the Container diagram.

A. Architecture Pattern: MVC/MVVM

The diagram reveals a clear separation of concerns typical of Angular applications:

  • Controllers: The Sign In Controller and Accounts Summary Controller act as the entry points for user actions. They don’t do the heavy lifting; they delegate.
  • Services: The Security Service is a dedicated component for logic that applies across the app (auth), rather than duplicating code in every controller.

🏗️ Pattern Recognition Guide

When reviewing your diagram, look for these patterns:

Layered Architecture:

Presentation → Application → Domain → Infrastructure

Hexagonal/Ports & Adapters:

        [External Systems]
              ↓ ↑
    [Primary Adapters (Controllers)]
              ↓ ↑
        [Domain Logic]
              ↓ ↑
    [Secondary Adapters (API Clients)]
              ↓ ↑
        [External Systems]

Clean Architecture:

        [Frameworks/Drivers]
              ↓ ↑
            [UI]
              ↓ ↑
     [Application Business]
              ↓ ↑
      [Enterprise Business]

💡 Tip: Your component diagram should make the architectural pattern obvious at a glance. If it doesn’t, you may have architectural drift.


B. The Facade Pattern

A key architectural decision revealed here is the Banking API Facade.

  • Instead of every controller calling the backend API directly, they all talk to the Facade.
  • Benefit: This centralizes API communication. If the API endpoint changes, or if error handling logic needs to be updated, it only needs to change in one place (the Facade), not in every controller.

🎭 When to Use a Facade

Use a Facade when:

  • ✅ Multiple components need the same external service
  • ✅ You need to simplify a complex subsystem
  • ✅ You want to add a layer of abstraction for testing
  • ✅ You need to transform data between systems
  • ✅ You want to implement caching or retry logic centrally

Don’t use a Facade when:

  • ❌ Only one component uses the service (over-engineering)
  • ❌ The underlying API is already simple and stable
  • ❌ Performance is critical and the extra layer adds latency

⚠️ Warning: Don’t create “leaky facades” that expose all underlying complexity. A facade should simplify, not just pass through.


C. Security Separation

The diagram explicitly separates security logic into a Security Service.

  • This suggests that authentication isn’t just a “login screen” feature; it’s a service that likely manages tokens, session state, and authorization checks, which are then used by other parts of the app (like the API Facade).

🔐 Security Component Checklist

If your system has security requirements, ensure you have:

  • Authentication Service/Component: Handles login, logout, token management
  • Authorization Component: Enforces access control rules
  • Secure Storage: Handles encrypted local storage
  • Session Manager: Tracks user session state
  • Audit Logger: Records security-relevant events
  • Input Validator: Sanitizes user input
  • Token Handler: Manages JWT/OAuth tokens

💡 Best Practice: Security components should be highly visible in your diagram. They’re critical to review during architecture reviews and security audits.


5. Advanced Tips & Tricks

🎯 Component Diagram Quality Metrics

Good Component Diagram:

  • ✅ 5-10 components (not too few, not too many)
  • ✅ Clear separation of concerns
  • ✅ Minimal cross-layer dependencies
  • ✅ Obvious architectural pattern
  • ✅ External dependencies clearly marked
  • ✅ Technology stack documented
  • ✅ Responsibilities are clear and non-overlapping

Red Flags:

  • ❌ Components with >5 dependencies (God components)
  • ❌ Circular dependencies between components
  • ❌ Components that span multiple responsibilities
  • ❌ Missing external context
  • ❌ No technology information
  • ❌ Vague or missing responsibilities

🛠️ Tool Recommendation

Visual Paradigm’s AI-Powered C4 PlantUML Studio is a cloud-based design tool that automates software architecture documentation. It combines the hierarchical clarity of the C4 model (Context, Container, Component, and Code) with the efficiency of PlantUML script-based rendering.

Core Capabilities

  • AI-Driven Generation: Users can describe a system in plain English (e.g., “an online banking app with a mobile frontend and microservices”), and the AI instantly generates the corresponding PlantUML code and diagrams.
  • Comprehensive C4 Support: It supports all levels of the C4 hierarchy, including specialized views like System Landscape, Dynamic, and Deployment diagrams.
  • Dual Editor Interface: Features a side-by-side view where you can edit PlantUML script on the left and see the rendered diagram update in real-time on the right.

Standard Workflow

  1. Define: Enter a project name and a high-level text description of the system.
  2. Generate: The AI creates a “Problem Statement” and an initial set of diagrams across different C4 levels.
  3. Refine: Manually edit the PlantUML code.

Why It’s Used

It addresses the common issue of architecture documentation becoming outdated. By using text-based PlantUML, diagrams are easier to maintain, version-control in Git, and align across different technical and business stakeholders.


📚 Documentation Integration

Your component diagram should live in:

  1. Architecture Decision Records (ADRs): Link diagrams to decisions
  2. README.md: High-level overview for new developers
  3. Onboarding Docs: Help new team members understand the system
  4. API Documentation: Show how components interact with APIs
  5. Compliance Docs: Demonstrate security and data flow

⚠️ Warning: A diagram that’s not maintained is worse than no diagram at all. It creates false confidence. Assign ownership and review diagrams quarterly.


🔄 Keeping Diagrams Current

Maintenance Strategies:

1. Architecture Reviews:

  • Review diagrams during sprint planning
  • Update before major refactoring
  • Validate after major releases

2. Automated Validation:

  • Use tools like ArchUnit to enforce architecture rules
  • Generate diagrams from code where possible
  • Set up CI/CD checks for architecture violations

3. Documentation Sprints:

  • Dedicate time quarterly to update diagrams
  • Assign diagram ownership to team members
  • Make it part of the Definition of Done

4. Change Triggers:
Update your diagram when:

  • Adding a new component
  • Removing a component
  • Changing component responsibilities
  • Modifying external dependencies
  • Refactoring architecture patterns

6. Common Pitfalls & How to Avoid Them

Pitfall Symptom Solution
Too Much Detail Diagram looks like a class diagram Step back; group related classes into components
Too Little Detail Just boxes with names Add responsibilities, technologies, relationships
Inconsistent Abstraction Some components are modules, others are classes Choose one level and stick to it
Missing Context Can’t see external systems Always show what’s outside the boundary
Outdated Diagram Doesn’t match the code Implement review process; automate where possible
Unclear Purpose Diagram doesn’t aid understanding Define audience and goal before creating
Over-Engineering 20+ components in one diagram Split into multiple focused diagrams

7. Best Practices Checklist

Before You Start

  • Define the audience (developers, architects, stakeholders?)
  • Identify the container to decompose
  • Gather existing documentation
  • Identify key architectural decisions to highlight

While Creating

  • Use consistent naming conventions
  • Limit to 5-10 components
  • Show external dependencies
  • Label relationships clearly
  • Include technology stack
  • Write clear responsibility statements
  • Use standard C4 notation

After Completion

  • Review with team members
  • Validate against actual code
  • Store in version control
  • Link to related documentation
  • Schedule regular reviews
  • Assign ownership

8. Conclusion

Refining a Container diagram into a Component diagram is an exercise in architectural transparency. By following the decomposition workflow outlined above, architects can move from high-level deployment views to detailed implementation views.

In this specific case, we uncovered how a JavaScript/Angular SPA is organized into Controllers, Services, and Facades to ensure maintainability and security.

🎓 Key Takeaways

  1. Component diagrams bridge the gap between architecture and implementation
  2. Focus on responsibilities, not just structure
  3. Keep it current or don’t create it at all
  4. Make it collaborative – involve the development team
  5. Use the right level of detail for your audience

🚀 Next Steps

  • Create your first component diagram using this guide
  • Review existing diagrams against the quality metrics
  • Implement a diagram maintenance process
  • Train your team on C4 modeling
  • Integrate diagrams into your development workflow

💡 Final Thought: A good component diagram is a communication tool, not an artifact. Its value is measured by how well it helps your team understand, build, and maintain the system.


📖 Further Reading:

  • “Software Architecture for Developers” by Simon Brown (C4 Model creator)
  • “Building Microservices” by Sam Newman
  • “Clean Architecture” by Robert C. Martin
  • C4 Model official website: c4model.com

🔗 Resources:

Turn every software project into a successful one.

We use cookies to offer you a better experience. By visiting our website, you agree to the use of cookies as described in our Cookie Policy.

OK