Connect Nodes with Relationships
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.2.
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
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!