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

Technology Choices

Highlighting communication protocols and technology stacks

Once you’ve identified the major containers that make up your software system, the next critical step in creating a meaningful Container Diagram is to clearly document the technology choices for each container and — even more importantly — the communication protocols and mechanisms that connect them.

This is what transforms a simple block diagram into a useful architectural artifact: it shows how the pieces fit together technically, revealing integration patterns, potential friction points, performance characteristics, security considerations, and maintainability implications.

Why Technology Choices Belong at Level 2

At the System Context level (Level 1), we deliberately kept everything technology-agnostic to focus on business purpose and boundaries.

At the Container level (Level 2), we introduce just enough technical detail to answer:

  • What technologies power each major part of the system?
  • How do these parts actually talk to each other in production?
  • What architectural style or constraints does the system follow (REST, event-driven, RPC, etc.)?

This information is essential for developers, architects, DevOps teams, security reviewers, and anyone evaluating the system’s health, scalability, or modernization potential.

What to Show for Each Container

For every container in your diagram, include (as text labels inside or beside the box):

  1. Primary Technology / Stack
    • The main runtime or framework used to implement the container.
    • Be specific enough to be meaningful, but not exhaustive.
    • Examples:
      • “Spring Boot + Java 21”
      • “React 18 (TypeScript)”
      • “Node.js 20 + Express”
      • “.NET 8 (ASP.NET Core)”
      • “Go 1.23”
      • “PostgreSQL 16”
      • “Kafka 3.7”
      • “AWS Lambda (Python 3.12)”
  2. Optional Secondary Technologies (if they add clarity)
    • Databases: “PostgreSQL”, “MongoDB Atlas”, “Redis”
    • Frameworks/libraries: “Quarkus”, “FastAPI”, “Next.js”
    • Hosting/runtime: “Kubernetes”, “AWS ECS”, “Vercel”, “Azure Functions”

Keep it concise — aim for 1–3 lines of text per container. Overloading with every dependency defeats the purpose.

Highlighting Communication Protocols and Integration Styles

The real value of the Container Diagram shines in the relationships between containers. Here you explicitly show how containers interact, using:

  1. Directional Arrows with protocol labels
    • Label each arrow with the primary communication mechanism.
    • Common examples:
      • “HTTPS / REST” or “HTTP/REST API”
      • “gRPC”
      • “GraphQL”
      • “AMQP” or “RabbitMQ”
      • “Kafka (events)” or “Kafka topics”
      • “JDBC / SQL” (direct database access — use sparingly and flag as anti-pattern when possible)
      • “WebSocket”
      • “File transfer (S3)”
      • “gRPC over HTTP/2”
      • “AWS SQS” or “Azure Service Bus”
  2. Bidirectional arrows when communication flows both ways (e.g., request-response + callbacks, event pub/sub, two-way sync).
  3. Optional qualifiers for clarity
    • “Synchronous” vs. “Asynchronous”
    • “JSON over HTTPS”
    • “Protobuf + gRPC”
    • “Event-carried state transfer”
    • “Command → Event”

Best Practices for Clean, Informative Relationships

  • Keep labels short but precise — “REST/HTTPS” is better than “Calls RESTful endpoints over HTTPS using JSON payloads”.
  • Use consistent conventions across diagrams (e.g., always put protocol first, then payload format if relevant).
  • Flag important patterns — e.g., direct database coupling between services can be highlighted with a note or different line style.
  • Avoid generic labels like “API” or “Integration” — specify the protocol/style.
  • Show async differently — some teams use dashed arrows for asynchronous/event-driven communication to visually distinguish from synchronous calls.

Example Snippet (Text Representation)

text
[Customer Mobile App (React Native)] --> HTTPS / REST API --> [API Gateway (Kong)]
[API Gateway (Kong)] <--> HTTPS / GraphQL --> [GraphQL Backend (Node.js + Apollo)]
[GraphQL Backend] --> Kafka (events) --> [Order Processor (Spring Boot)]
[Order Processor] --> JDBC --> [PostgreSQL – Orders]
[Order Processor] --> AWS SNS --> [Notification Service (AWS Lambda)]

This single view immediately communicates:

  • Frontend → backend flow via REST/GraphQL
  • Event-driven processing with Kafka
  • Direct DB access (potential concern)
  • Push notifications via SNS

Common Pitfalls to Avoid

  • Hiding critical protocols behind vague labels (“connects to”, “integrates with”)
  • Omitting technology for databases or brokers (they are containers too — label them!)
  • Showing low-level details like ports, IP addresses, or Kubernetes service names (save for Deployment view)
  • Over-fragmenting: if two containers always communicate via the same protocol, one arrow may suffice

By clearly highlighting technology stacks and communication protocols at Level 2, your Container Diagram becomes a living reference for understanding the system’s technical reality — essential for onboarding, architecture reviews, incident response, threat modeling, and planning evolutionary changes.

In the hands-on section coming up, you’ll apply these principles directly: taking a System Context, decomposing into containers, assigning realistic technologies, and labeling interactions with precise protocols — all using C4-PlantUML Studio for rapid, versionable results.

How can we help?