Skip to content

Commit dcc2d53

Browse files
Merge pull request #1941 from rocketstack-matt/feat/advent-day-17-19
feat(advent): Add days 17 - 19
2 parents bba6aa9 + 232e7cf commit dcc2d53

File tree

4 files changed

+874
-3
lines changed

4 files changed

+874
-3
lines changed

advent-of-calm/day-17.md

Lines changed: 351 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,351 @@
1+
# Day 17: Introduction to CALM Patterns
2+
3+
## Overview
4+
Learn how CALM Patterns enable you to define reusable architecture templates that can both generate new architectures and validate existing ones.
5+
6+
## Objective and Rationale
7+
- **Objective:** Understand what Patterns are and create your first Pattern for a simple web application
8+
- **Rationale:** Patterns are CALM's superpower for reuse and governance. A single Pattern can generate architecture scaffolds instantly AND validate that existing architectures conform to a required structure. This dual capability makes Patterns essential for scaling architecture practices across teams.
9+
10+
## Requirements
11+
12+
### 1. Understand What Patterns Are
13+
14+
**The Problem Patterns Solve:**
15+
- Teams keep building similar architectures from scratch
16+
- No easy way to enforce "all web apps must have these components"
17+
- Inconsistent structures across projects
18+
19+
**The Solution:**
20+
CALM Patterns pre-define the required structure of an architecture. They specify which nodes must exist, what relationships must connect them, and what properties they must have.
21+
22+
### 2. Understand the Dual Superpower
23+
24+
**One Pattern = Two Powers:**
25+
26+
**Power 1 - Generation:**
27+
```bash
28+
calm generate -p my-pattern.json -o new-architecture.json
29+
```
30+
Creates an architecture scaffold with all required nodes and relationships.
31+
32+
**Power 2 - Validation:**
33+
```bash
34+
calm validate -p my-pattern.json -a existing-architecture.json
35+
```
36+
Checks that an architecture has the required structure.
37+
38+
### 3. Understand How Patterns Work
39+
40+
Patterns use JSON Schema keywords to define requirements:
41+
42+
- **`const`** - Requires an exact value (e.g., `"unique-id": { "const": "api-gateway" }`)
43+
- **`prefixItems`** - Defines the first exact items in an array (you can add others)
44+
- **`minItems`/`maxItems`** - Enforces array length
45+
- **`$ref`** - References other schemas
46+
47+
**Example: Requiring a specific node**
48+
```json
49+
{
50+
"nodes": {
51+
"type": "array",
52+
"prefixItems": [
53+
{
54+
"properties": {
55+
"unique-id": { "const": "api-gateway" },
56+
"node-type": { "const": "service" },
57+
"name": { "const": "API Gateway" }
58+
}
59+
}
60+
],
61+
"minItems": 1
62+
}
63+
}
64+
```
65+
66+
This pattern requires an architecture to have a node with `unique-id: "api-gateway"`.
67+
68+
### 4. Create Your First Pattern
69+
70+
Create a simple pattern for a 3-tier web application.
71+
72+
**Prompt:**
73+
```text
74+
Create a CALM pattern at patterns/web-app-pattern.json for a 3-tier web application.
75+
76+
The pattern should:
77+
1. Have a unique $id (https://example.com/patterns/web-app-pattern.json)
78+
2. Have title "Web Application Pattern" and a description
79+
3. Require exactly 3 nodes using prefixItems:
80+
- "web-frontend" (node-type: webclient, name: "Web Frontend")
81+
- "api-service" (node-type: service, name: "API Service")
82+
- "app-database" (node-type: database, name: "Application Database")
83+
4. Require exactly 2 relationships:
84+
- "frontend-to-api": connects web-frontend to api-service
85+
- "api-to-database": connects api-service to app-database
86+
87+
Use const for unique-id, node-type, and name properties.
88+
Set minItems and maxItems to enforce exact counts.
89+
```
90+
91+
### 5. Test Generation
92+
93+
Generate an architecture from your pattern:
94+
95+
```bash
96+
calm generate -p patterns/web-app-pattern.json -o architectures/generated-webapp.json
97+
```
98+
99+
Open `architectures/generated-webapp.json` and verify:
100+
- ✅ Has exactly 3 nodes with the correct IDs, types, and names
101+
- ✅ Has exactly 2 relationships connecting them
102+
- ✅ Structure matches what the pattern defined
103+
104+
### 6. Visualize the Generated Architecture
105+
106+
**Steps:**
107+
1. Open `architectures/generated-webapp.json` in VSCode
108+
2. Open preview (Ctrl+Shift+C / Cmd+Shift+C)
109+
3. See the 3-tier architecture visualized
110+
4. **Take a screenshot**
111+
112+
### 7. Test Validation - Passing Case with Warnings
113+
114+
Your generated architecture should validate against the pattern:
115+
116+
```bash
117+
calm validate -p patterns/web-app-pattern.json -a architectures/generated-webapp.json
118+
```
119+
120+
Should pass! ✅ but show some warnings:
121+
122+
```json
123+
{
124+
"jsonSchemaValidationOutputs": [],
125+
"spectralSchemaValidationOutputs": [
126+
{
127+
"code": "architecture-has-no-placeholder-properties-string",
128+
"severity": "warning",
129+
"message": "String placeholder detected in architecture.",
130+
"path": "/nodes/0/description",
131+
"schemaPath": "",
132+
"line_start": 0,
133+
"line_end": 0,
134+
"character_start": 98,
135+
"character_end": 117
136+
},
137+
{
138+
"code": "architecture-has-no-placeholder-properties-string",
139+
"severity": "warning",
140+
"message": "String placeholder detected in architecture.",
141+
"path": "/nodes/1/description",
142+
"schemaPath": "",
143+
"line_start": 0,
144+
"line_end": 0,
145+
"character_start": 203,
146+
"character_end": 222
147+
},
148+
{
149+
"code": "architecture-has-no-placeholder-properties-string",
150+
"severity": "warning",
151+
"message": "String placeholder detected in architecture.",
152+
"path": "/nodes/2/description",
153+
"schemaPath": "",
154+
"line_start": 0,
155+
"line_end": 0,
156+
"character_start": 319,
157+
"character_end": 338
158+
}
159+
],
160+
"hasErrors": false,
161+
"hasWarnings": true
162+
}%
163+
```
164+
165+
This is because `description` is a required field of nodes in the calm schema, but because we didn't provide a default description in our pattern, the generate command has put in placeholders which it expects us to fill in after generation.
166+
167+
String placeholders can be identified by two square brackets: `"description": "[[ DESCRIPTION ]]"`. Numeric placeholders will have the value `-1`.
168+
169+
As you will have seen when you visualized the architecture, placeholders don't make the architecture invalid, hence why only warnings are reported, but it allows you to build integration in your own tooling to spot where an architect or engineer is expected to replace those placeholders.
170+
171+
### 8. Test Validation - Failing Case
172+
173+
Create a broken architecture to see validation fail:
174+
175+
1. Create architectures/broken-webapp.json by copying generated-webapp.json
176+
2. Change the unique-id of "api-service" to "backend-api"
177+
178+
Validate:
179+
```bash
180+
calm validate -p patterns/web-app-pattern.json -a architectures/broken-webapp.json
181+
```
182+
183+
Should fail! ❌ The pattern catches that "api-service" is missing.
184+
185+
```json
186+
{
187+
"jsonSchemaValidationOutputs": [
188+
{
189+
"code": "json-schema",
190+
"severity": "error",
191+
"message": "must be equal to constant",
192+
"path": "/nodes/1/unique-id",
193+
"schemaPath": "#/properties/nodes/prefixItems/1/properties/unique-id/const"
194+
}
195+
],
196+
"spectralSchemaValidationOutputs": [
197+
{
198+
"code": "connects-relationship-references-existing-nodes-in-architecture",
199+
"severity": "error",
200+
"message": "'api-service' does not refer to the unique-id of an existing node.",
201+
"path": "/relationships/0/relationship-type/connects/destination/node",
202+
"schemaPath": "",
203+
"line_start": 0,
204+
"line_end": 0,
205+
"character_start": 440,
206+
"character_end": 453
207+
},
208+
{
209+
"code": "connects-relationship-references-existing-nodes-in-architecture",
210+
"severity": "error",
211+
"message": "'api-service' does not refer to the unique-id of an existing node.",
212+
"path": "/relationships/1/relationship-type/connects/source/node",
213+
"schemaPath": "",
214+
"line_start": 0,
215+
"line_end": 0,
216+
"character_start": 539,
217+
"character_end": 552
218+
},
219+
{
220+
"code": "architecture-nodes-must-be-referenced",
221+
"severity": "warning",
222+
"message": "Node with ID 'api-service-1' is not referenced by any relationships.",
223+
"path": "/nodes/1/unique-id",
224+
"schemaPath": "",
225+
"line_start": 0,
226+
"line_end": 0,
227+
"character_start": 119,
228+
"character_end": 134
229+
}
230+
],
231+
"hasErrors": true,
232+
"hasWarnings": true
233+
}%
234+
```
235+
236+
As you can see, not only does the `validate` command identify that it is missing an expected node, but the relationship which was referencing it is also now in error and there is also a warning because we now have a node which is not referenced in any relationships.
237+
238+
### 9. Enhance the Generated Architecture
239+
240+
The generated architecture has the required structure. Now add details:
241+
242+
**Prompt:**
243+
```text
244+
Update architectures/generated-webapp.json to add:
245+
1. Descriptions for each node explaining their purpose
246+
2. A description for each relationship
247+
3. Interfaces on api-service (host, port for HTTPS)
248+
4. Interfaces on app-database (host, port for PostgreSQL)
249+
250+
Keep the unique-ids, node-types, and names exactly as they are.
251+
```
252+
253+
### 10. Validate the Enhanced Architecture
254+
255+
```bash
256+
calm validate -p patterns/web-app-pattern.json -a architectures/generated-webapp.json
257+
```
258+
259+
```json
260+
{
261+
"jsonSchemaValidationOutputs": [],
262+
"spectralSchemaValidationOutputs": [],
263+
"hasErrors": false,
264+
"hasWarnings": false
265+
}%
266+
```
267+
268+
It now passes without warnings ✅ and adding extra properties doesn't break pattern compliance. This is how patterns act as the base foundation for new applications that share the same architecture, but aren't the same application.
269+
270+
Think about how this can help you automate typically time consuming processes such as security review, which rely on certain things following strict convention, but other things being the responsibility of the engineers.
271+
272+
### 11. Document the Pattern
273+
274+
**Prompt:**
275+
```text
276+
Create patterns/README.md that documents:
277+
278+
1. What Patterns are and their dual superpower (generation + validation)
279+
2. How to use web-app-pattern.json:
280+
- Generation: calm generate -p patterns/web-app-pattern.json -o my-app.json
281+
- Validation: calm validate -p patterns/web-app-pattern.json -a my-app.json
282+
3. What the pattern enforces (3 specific nodes, 2 specific relationships)
283+
4. What's flexible (descriptions, interfaces, metadata)
284+
```
285+
286+
### 12. Commit Your Work
287+
288+
```bash
289+
git add patterns/ architectures/generated-webapp.json README.md
290+
git commit -m "Day 17: Create first CALM pattern for web applications"
291+
git tag day-17
292+
```
293+
294+
## Deliverables / Validation Criteria
295+
296+
Your Day 17 submission should include a commit tagged `day-17` containing:
297+
298+
**Required Files:**
299+
- `patterns/web-app-pattern.json` - Pattern for 3-tier web app
300+
- `patterns/README.md` - Pattern documentation
301+
- `architectures/generated-webapp.json` - Generated and enhanced architecture
302+
- Updated `README.md` - Day 17 marked as complete
303+
304+
**Validation:**
305+
```bash
306+
# Pattern exists
307+
test -f patterns/web-app-pattern.json
308+
309+
# Generation works
310+
calm generate -p patterns/web-app-pattern.json -o /tmp/test-webapp.json
311+
312+
# Validation works
313+
calm validate -p patterns/web-app-pattern.json -a /tmp/generated-webapp.json
314+
315+
# Check tag
316+
git tag | grep -q "day-17"
317+
```
318+
319+
## Resources
320+
321+
- [JSON Schema prefixItems](https://json-schema.org/understanding-json-schema/reference/array#tupleValidation)
322+
- [JSON Schema const](https://json-schema.org/understanding-json-schema/reference/const)
323+
324+
## Tips
325+
326+
- Use `const` for values that MUST be exactly as specified
327+
- `prefixItems` defines the exact items required in order
328+
- `minItems`/`maxItems` together enforce exact array length
329+
- Patterns only constrain what you specify - extra properties are allowed
330+
- Test both generation AND validation to ensure the pattern works
331+
332+
## Trouble Shooting
333+
- Remember AI can make mistakes, if for some reason your pattern won't generate a valid architecture ask CALM Chatmode to figure out why not, this prompt may be helpful.
334+
335+
```text
336+
My pattern doesn't generate a valid architecture when I run the generate command, look at this valid pattern - https://raw.githubusercontent.com/finos/architecture-as-code/refs/heads/main/conferences/osff-ln-2025/workshop/conference-signup.pattern.json - identify the problem.
337+
```
338+
339+
## Key Concepts
340+
341+
| Concept | Purpose | Example |
342+
|---------|---------|---------|
343+
| `const` | Require exact value | `"unique-id": { "const": "api-gateway" }` |
344+
| `prefixItems` | Define required array items | First node must be X, second must be Y |
345+
| `minItems` | Minimum array length | At least 3 nodes |
346+
| `maxItems` | Maximum array length | At most 3 nodes |
347+
| `$ref` | Reference other schemas | Point to node definition |
348+
349+
## Next Steps
350+
351+
Tomorrow (Day 18) you'll learn how to create organizational Standards - JSON Schema extensions that define custom properties like cost centers, owners, and compliance tags that your Patterns can enforce!

0 commit comments

Comments
 (0)