Files
2025-11-29 18:17:07 +08:00

422 lines
9.4 KiB
Markdown

# C4 Model Relations - Detailed Reference
## Table of Contents
1. [Protocol Documentation Guidelines](#protocol-documentation-guidelines)
2. [Direction vs Coupling](#direction-vs-coupling)
3. [Best Practices per Level](#best-practices-per-level)
4. [Graph Validity Rules](#graph-validity-rules)
5. [Common Patterns](#common-patterns)
---
## Relation Type Definitions
For complete type definitions for all C4 levels, see [types.md](./types.md).
---
## Protocol Documentation Guidelines
### When to Document Protocol
**Always include protocol for:**
- C1 system-to-system communication
- C2 container-to-container communication
- External integrations
- Any network communication
**Optional for:**
- C3 code-level dependencies (unless calling external APIs)
- In-process method calls
### Protocol Format
Use standard protocol names:
**Good:**
- `HTTP/REST`
- `HTTP/2 gRPC`
- `PostgreSQL Wire Protocol`
- `AMQP`
- `OAuth 2.0`
**Bad:**
- `rest` (too vague)
- `http` (specify REST, GraphQL, etc.)
- `database` (specify PostgreSQL, MySQL, etc.)
### Protocol Details to Include
For **HTTP-based protocols:**
- HTTP version (HTTP/1.1, HTTP/2)
- API style (REST, GraphQL, RPC)
- Authentication method
- Content type (JSON, XML, etc.)
For **Database protocols:**
- Database type (PostgreSQL, MySQL, MongoDB)
- Connection method (wire protocol, ORM)
- Authentication method
For **Messaging protocols:**
- Broker type (RabbitMQ, Kafka, SQS)
- Protocol (AMQP, Kafka Protocol)
- Pattern (pub-sub, work queue)
---
## Direction vs Coupling
### Direction (C1 Level)
**Purpose:** Describes the flow of communication between systems
#### outbound
**Definition:** Source system initiates communication
**Example:** Frontend makes HTTP requests to API
**Use when:** Source actively calls, queries, or requests from target
#### inbound
**Definition:** Target system initiates communication
**Example:** External webhook calls your API
**Use when:** Target pushes data, calls back, or triggers source
#### bidirectional
**Definition:** Communication flows both ways
**Example:** WebSocket connection with messages in both directions
**Use when:** True two-way communication (avoid overusing)
**Best Practice:** Prefer outbound/inbound over bidirectional for clarity. Create two separate relations if needed.
### Coupling (C3 Level)
**Purpose:** Describes strength of dependency between components
#### loose
**Definition:** Minimal dependency, easily replaceable
**Characteristics:**
- Components interact through interfaces or abstractions
- Easy to test (can use mocks)
- Easy to swap implementations
- Changes in one component don't force changes in another
**Examples:**
- Dependency injection
- Interface-based dependencies
- Event-driven communication
- Plugin architectures
#### tight
**Definition:** Strong dependency, hard to replace
**Characteristics:**
- Direct class references
- Hard to test without real implementation
- Hard to swap implementations
- Changes cascade between components
**Examples:**
- Direct class instantiation
- Class inheritance
- Static method calls
- Global state dependencies
**Best Practice:** Prefer loose coupling. Document tight coupling as potential refactoring opportunity.
---
## Best Practices per Level
### C1 System Context Best Practices
1. **Focus on protocols**
- Always specify communication protocol
- Include authentication method for external relations
- Document sync vs async nature
2. **Specify direction**
- Use outbound for active calls (API requests, queries)
- Use inbound for passive receives (webhooks, callbacks)
- Avoid bidirectional unless truly necessary
3. **Document external integrations**
- Mark external services clearly
- Include vendor name (Stripe, SendGrid, etc.)
- Note criticality for business operations
4. **Group similar relations**
- One relation for "Frontend → API" covering all endpoints
- Don't create separate relations for each API endpoint
### C2 Container Best Practices
1. **Be specific about API style**
- Use `http-rest` not just `http`
- Use `http-graphql` for GraphQL
- Distinguish between REST and RPC
2. **Differentiate read vs write**
- Use `database-query` for read-only
- Use `database-write` for write-only
- Use `database-read-write` for both
3. **Document cache patterns**
- Specify cache operations (read, write, both)
- Include eviction strategy if relevant
- Note cache hit/miss criticality
4. **Show message patterns**
- Use `message-publish` for producers
- Use `message-subscribe` for consumers
- Document topic/queue names in description
### C3 Component Best Practices
1. **Assess coupling**
- Always include coupling field
- Prefer loose coupling
- Document tight coupling as technical debt
2. **Document design patterns**
- Use `injects` for DI
- Use `observes`/`notifies` for Observer pattern
- Use `implements` for Strategy/Interface patterns
3. **Show dependency direction**
- High-level → low-level (Controller → Service → Repository)
- Abstract → concrete (Interface ← Implementation)
- Stable → volatile
4. **Highlight circular dependencies**
- Flag as warning in observations
- Consider refactoring
- Document workaround if intentional
---
## Graph Validity Rules
### Referential Integrity
**Rule:** All relation targets must reference valid entity IDs
**Validation:**
```python
# Pseudo-code
for relation in entity.relations:
if relation.target not in valid_entity_ids:
raise ValidationError(f"Invalid target: {relation.target}")
```
**Examples:**
✅ Valid:
```json
{
"id": "rel-api-to-db",
"target": "postgres-db", // postgres-db exists in containers list
"type": "database-read-write"
}
```
❌ Invalid:
```json
{
"id": "rel-api-to-db",
"target": "mysql-db", // mysql-db doesn't exist
"type": "database-read-write"
}
```
### No Self-References
**Rule:** Source cannot equal target
✅ Valid:
```json
{ "source": "api-service", "target": "database" }
```
❌ Invalid:
```json
{ "source": "api-service", "target": "api-service" }
```
### Cross-Level References
**Rule:** Relations can reference entities at same or different levels
**C1 → C1:** System to system ✅
**C2 → C2:** Container to container within same system ✅
**C2 → C1:** Container to external system ✅
**C3 → C3:** Component to component within same container ✅
**C3 → C2:** Component to external container ✅
### Bidirectional Relations
**Rule:** If bidirectional, both entities should document the relation
**Best Practice:** Prefer two unidirectional relations over one bidirectional
✅ Preferred:
```json
// In entity A
{ "target": "B", "type": "http-rest", "direction": "outbound" }
// In entity B
{ "target": "A", "type": "websocket", "direction": "inbound" }
```
❌ Less clear:
```json
// In entity A
{ "target": "B", "type": "http-rest", "direction": "bidirectional" }
// No corresponding relation in B
```
### Dangling References
**Rule:** All targets should eventually resolve to a documented entity
**Exceptions:**
- External systems not in your control
- Mark these clearly in description
- Use `external-api` or similar type
---
## Common Patterns
### Frontend-Backend Pattern
**C1 Level:**
```json
{
"source": "web-application",
"target": "backend-api",
"type": "http-rest",
"direction": "outbound",
"protocol": "HTTP/REST"
}
```
**C2 Level:**
```json
{
"source": "react-spa",
"target": "express-api",
"type": "http-rest",
"protocol": "HTTP/REST",
"isAsync": true
}
```
### API-Database Pattern
**C2 Level:**
```json
{
"source": "express-api",
"target": "postgres-db",
"type": "database-read-write",
"protocol": "PostgreSQL Wire Protocol",
"isAsync": false
}
```
### Event-Driven Pattern
**C1 Level:**
```json
{
"source": "order-service",
"target": "message-queue",
"type": "message-queue",
"direction": "outbound",
"protocol": "AMQP",
"isAsync": true
}
```
**C2 Level:**
```json
{
"source": "order-container",
"target": "rabbitmq",
"type": "message-publish",
"protocol": "AMQP"
},
{
"source": "notification-worker",
"target": "rabbitmq",
"type": "message-subscribe",
"protocol": "AMQP"
}
```
### Layered Architecture Pattern (C3)
```json
{
"source": "user-controller",
"target": "user-service",
"type": "uses",
"coupling": "loose"
},
{
"source": "user-service",
"target": "user-repository",
"type": "injects",
"coupling": "loose"
}
```
### Observer Pattern (C3)
```json
{
"source": "order-service",
"target": "order-events",
"type": "notifies",
"coupling": "loose"
},
{
"source": "payment-service",
"target": "order-events",
"type": "observes",
"coupling": "loose"
}
```
---
## Validation Checklist
Before finalizing relations documentation:
- [ ] All relation IDs are unique
- [ ] All targets reference valid entity IDs
- [ ] No self-references (source ≠ target)
- [ ] Direction specified for C1 relations
- [ ] Coupling specified for C3 relations
- [ ] Protocol documented for network communication
- [ ] Descriptions in active voice
- [ ] Types match C4 level (C1 types at C1, etc.)
- [ ] isAsync set appropriately
- [ ] Tags use lowercase kebab-case
- [ ] Critical relations tagged appropriately
---
## References
- **Type Definitions:** [types.md](./types.md)
- **Examples:** [examples.md](./examples.md)
- **Main Skill:** [SKILL.md](./SKILL.md)
- **Validation Scripts:** `${CLAUDE_PLUGIN_ROOT}/validation/scripts/`
- **Schema Documentation:** `/docs/observations-relations-schema.md`
---
**Version:** 1.0.0
**Last Updated:** 2025-11-17