Files
gh-cubical6-melly/skills/c4model-c4/relation-types-c4.md
2025-11-29 18:17:07 +08:00

8.6 KiB

Relation Types for C4 Code Elements

This guide provides comprehensive documentation of relationship types between code elements at the C4 Code level.


Overview

Relations describe how code elements interact with each other. Each relation has:

  • target: The code element being related to
  • type: The nature of the relationship
  • description: Active voice description of the relationship

Code Flow Relations

calls

Definition: Invokes another function or method

When to use: When a function/method directly calls another

Examples:

function processOrder(order: Order) {
  validateOrder(order);        // calls validateOrder
  calculateTotal(order);       // calls calculateTotal
}

JSON:

{
  "target": "validate-order",
  "type": "calls",
  "description": "Calls validateOrder to validate order structure"
}

awaits

Definition: Awaits an async function

When to use: When async function awaits another async function

Examples:

async function getUser(id: string) {
  const user = await userRepository.findById(id);  // awaits findById
  return user;
}

JSON:

{
  "target": "user-repository-find-by-id",
  "type": "awaits",
  "description": "Awaits UserRepository.findById for user lookup"
}

returns

Definition: Returns a specific type

When to use: When documenting what type a function returns

Examples:

function createUser(data: UserInput): User {
  return new User(data);  // returns User
}

JSON:

{
  "target": "user-class",
  "type": "returns",
  "description": "Returns User instance with provided data"
}

throws

Definition: Throws an exception type

When to use: When function explicitly throws an exception

Examples:

function validateAge(age: number) {
  if (age < 0) {
    throw new ValidationError('Age cannot be negative');  // throws ValidationError
  }
}

JSON:

{
  "target": "validation-error-class",
  "type": "throws",
  "description": "Throws ValidationError when age is negative"
}

Structural Relations

inherits

Definition: Extends another class

When to use: When a class extends a parent class

Examples:

class AdminUser extends User {  // inherits from User
  permissions: string[];
}

JSON:

{
  "target": "user-class",
  "type": "inherits",
  "description": "Inherits from User base class"
}

implements

Definition: Implements an interface

When to use: When a class implements an interface contract

Examples:

class UserService implements IUserService {  // implements IUserService
  getUser(id: string): User { ... }
}

JSON:

{
  "target": "i-user-service-interface",
  "type": "implements",
  "description": "Implements IUserService interface contract"
}

declares

Definition: Declares a type or interface

When to use: When defining the shape of data used elsewhere

Examples:

interface UserInput {  // declares shape for createUser
  name: string;
  email: string;
}

function createUser(input: UserInput) { ... }

JSON:

{
  "target": "create-user-function",
  "type": "declares",
  "description": "Declares the input shape for createUser function"
}

uses-type

Definition: Uses a type in signature or body

When to use: When function uses a type for parameters, return, or internally

Examples:

function processPayment(payment: Payment): PaymentResult {
  // Uses Payment type for input
  // Uses PaymentResult type for output
}

JSON:

{
  "target": "payment-type",
  "type": "uses-type",
  "description": "Uses Payment type for input parameter"
}

Data Relations

creates

Definition: Instantiates a class

When to use: When function creates new instance of a class

Examples:

function createOrder(items: Item[]): Order {
  return new Order(items);  // creates Order instance
}

JSON:

{
  "target": "order-class",
  "type": "creates",
  "description": "Creates new Order instance with provided items"
}

mutates

Definition: Modifies state or data

When to use: When function modifies object state

Examples:

function updateUser(user: User, data: Partial<User>) {
  Object.assign(user, data);  // mutates user object
}

JSON:

{
  "target": "user-class",
  "type": "mutates",
  "description": "Mutates User instance with provided data"
}

reads

Definition: Reads from data source

When to use: When function reads data without modifying

Examples:

function getUserName(user: User): string {
  return user.name;  // reads from User
}

JSON:

{
  "target": "user-class",
  "type": "reads",
  "description": "Reads name property from User instance"
}

Dependency Relations

imports

Definition: Imports from another module

When to use: When code imports from another file/module

Examples:

import { UserService } from './services/user';  // imports UserService

JSON:

{
  "target": "user-service-class",
  "type": "imports",
  "description": "Imports UserService from services module"
}

depends-on

Definition: General dependency relationship

When to use: When there's a dependency that doesn't fit other categories

Examples:

class OrderService {
  constructor(
    private userService: UserService,  // depends-on UserService
    private paymentService: PaymentService  // depends-on PaymentService
  ) {}
}

JSON:

{
  "target": "user-service-class",
  "type": "depends-on",
  "description": "Depends on UserService for user operations"
}

overrides

Definition: Overrides parent class method

When to use: When method overrides a parent method

Examples:

class AdminUser extends User {
  override toString(): string {  // overrides User.toString
    return `Admin: ${this.name}`;
  }
}

JSON:

{
  "target": "user-class-to-string",
  "type": "overrides",
  "description": "Overrides User.toString to include admin prefix"
}

Relation Type Summary

Type Category Description
calls Code Flow Invokes function/method
awaits Code Flow Awaits async function
returns Code Flow Returns specific type
throws Code Flow Throws exception
inherits Structural Extends class
implements Structural Implements interface
declares Structural Declares type/interface
uses-type Structural Uses type in signature
creates Data Instantiates class
mutates Data Modifies state
reads Data Reads data
imports Dependency Imports from module
depends-on Dependency General dependency
overrides Dependency Overrides parent method

Complete Example

{
  "relations": [
    {
      "target": "base-service-class",
      "type": "inherits",
      "description": "Inherits common service functionality from BaseService"
    },
    {
      "target": "i-user-service-interface",
      "type": "implements",
      "description": "Implements IUserService interface contract"
    },
    {
      "target": "user-repository",
      "type": "depends-on",
      "description": "Depends on UserRepository for data persistence"
    },
    {
      "target": "user-repository-find-by-id",
      "type": "awaits",
      "description": "Awaits UserRepository.findById for user lookup"
    },
    {
      "target": "user-class",
      "type": "returns",
      "description": "Returns User instance to caller"
    },
    {
      "target": "user-not-found-error",
      "type": "throws",
      "description": "Throws UserNotFoundError when user doesn't exist"
    }
  ]
}

Best Practices

DO:

  • Use active voice in descriptions
  • Be specific about what is being called/used
  • Document all significant relationships
  • Use the most specific relation type

DON'T:

  • Use passive voice ("is called by")
  • Skip obvious but important relations
  • Use generic descriptions
  • Mix relation types inappropriately

Relation Direction

All relations are outbound from the source element:

  • Source element → calls → Target element
  • Source element → inherits → Target element
  • Source element → depends-on → Target element

Do NOT document reverse relations (e.g., "is called by"). The target element's incoming relations can be derived from the source elements' outbound relations.