Connect Nodes with Relationships
🟢 Difficulty: Beginner | ⏱️ Time: 30-45 minutes
Overview
Expand your architecture by adding multiple nodes and different relationship types to show how they connect, interact, and compose.
Learning Objectives
By the end of this tutorial, you will:
- Understand the different relationship types in CALM
- Create interacts, connects, and composed-of relationships
- Understand the oneOf constraint pattern
- Build a multi-node architecture
Prerequisites
Complete Create Your First Node first.
Step-by-Step Guide
1. Understand Relationship Types
CALM supports four core relationship types (you can only use one per relationship):
| Type | Purpose | Example |
|---|---|---|
| interacts | Actor/human interactions with services | User → Web Application |
| connects | Infrastructure/technical connections | Service → Database |
| deployed-in | Deployment topology | Service → Kubernetes Cluster |
| composed-of | Hierarchical composition | System → Services |
Today we'll work with interacts, connects, and composed-of.
2. Step-by-Step Relationship Building
We'll build up your architecture in stages, adding different relationship types.
Step 2a: Add a Database and Connect It
Prompt:
Update architectures/my-first-architecture.json to add:
1. A database node (node-type: "database") that stores data for my service
2. A "connects" relationship showing that my service connects to this database
The relationship should use the "connects" relationship type with source and destination properties.
Ensure the file still validates against CALM 1.1.
Review the output:
- ✅ New database node added
- ✅
relationshipsarray created (if it didn't exist) - ✅ Relationship uses
connects(not interacts!) - ✅
sourcecontains anodereference to your service - ✅
destinationcontains anodereference to the database
Step 2b: Add an Actor and Show Human Interaction
Now add a human actor who uses your service.
Prompt:
Update architectures/my-first-architecture.json to add:
1. An actor node (node-type: "actor") representing an end user or customer who uses my service
2. An "interacts" relationship showing that this actor interacts with my service
The relationship should use the "interacts" relationship type with actor and nodes properties.
Ensure the file still validates.
Review the output:
- ✅ New actor node added
- ✅ Relationship uses
interacts(for human-to-service interaction) - ✅
actorproperty references the actor node - ✅
nodesarray contains your service node
interacts is for humans/actors → services, connects is for service → infrastructure.
Step 2c: Add a System Node for Composition
Finally, create a system-level view showing your service is part of a larger system.
Prompt:
Update architectures/my-first-architecture.json to add:
1. A system node (node-type: "system") representing the overall system that contains my service and database
2. A "composed-of" relationship showing that this system is composed of both my service and database
The relationship should use the "composed-of" relationship type with container and nodes properties.
Ensure the file still validates.
Review the output:
- ✅ New system node added
- ✅ Relationship uses
composed-of - ✅
containerreferences the system node - ✅
nodesarray contains both your service node and database node
3. Understand Your Architecture
You should now have:
- 4 nodes: System, Service, Database, Actor
- 3 relationships:
- Actor interacts with Service
- Service connects to Database
- System composed-of Service and Database
Ask Copilot to explain what you built:
Explain the three different relationship types I just created and why each one is appropriate for its use case.
4. Experiment with the oneOf Constraint
Try this to understand the constraint:
Prompt:
What would happen if I tried to add both "interacts" and "connects" to the same relationship? Show me an example and explain why it would fail validation.
5. Validate Your Architecture
calm validate -a architectures/my-first-architecture.json
You should see validation succeed with 4 nodes and 3 relationships.
Key Concepts
Relationship Structures
Each relationship type has a different structure:
Interacts (actor → service):
{
"unique-id": "user-to-service",
"relationship-type": {
"interacts": {
"actor": "end-user",
"nodes": ["auth-service"]
}
}
}
Connects (service → infrastructure):
{
"unique-id": "service-to-db",
"relationship-type": {
"connects": {
"source": { "node": "auth-service" },
"destination": { "node": "user-database" }
}
}
}
Composed-of (container → children):
{
"unique-id": "system-composition",
"relationship-type": {
"composed-of": {
"container": "auth-system",
"nodes": ["auth-service", "user-database"]
}
}
}
The oneOf Constraint
Each relationship can have only one type. This is enforced by JSON Schema's oneOf constraint. You cannot have both interacts and connects in the same relationship.
Common Pitfalls
"Should I use 'interacts' or 'connects'?"
- interacts: Human/actor → service
- connects: Service → infrastructure/database/API
- Rule of thumb: If it's a person/external actor, use "interacts"
"My composed-of relationship won't validate"
- Make sure you use
container(singular) for the parent node - Make sure you use
nodes(plural array) for the child nodes - Different structure than interacts/connects!
"Validation says my node reference is invalid"
- Check spelling and case of
unique-idvalues - The reference must match exactly
Resources
Tips
- interacts: Use for actor/human → service interactions
- connects: Use for service → infrastructure (databases, queues, APIs)
- composed-of: Use for showing hierarchical containment
- Relationship
unique-idvalues should be descriptive (e.g., "user-to-auth-service") - The
descriptionproperty on relationships is optional but recommended
Next Steps
In the next tutorial, you'll install the CALM VSCode extension to visualize your architecture!