|
| 1 | +--- |
| 2 | +id: patterns |
| 3 | +title: Patterns |
| 4 | +sidebar_position: 9 |
| 5 | +--- |
| 6 | + |
| 7 | +# Patterns in CALM |
| 8 | + |
| 9 | +**Patterns** in CALM define **reusable architecture templates** that can be used to **generate** new architecture scaffolds and **validate** that existing architectures conform to a required structure. This “generate + validate” dual use is a key mechanism for reuse and governance across teams. |
| 10 | + |
| 11 | + |
| 12 | +## What is a Pattern? |
| 13 | + |
| 14 | +Patterns describe architecture blueprints. Instead of listing a fixed set of components, a pattern prescribes: |
| 15 | +* Which nodes (e.g., services, systems, databases) must exist. |
| 16 | +* Which relationships connect those nodes. |
| 17 | +* What constraints or choices are required for those nodes and relationships. |
| 18 | +* Which fields are structural (governed by the pattern) versus which are descriptive (left for implementation). |
| 19 | + |
| 20 | +Because they’re expressed in JSON Schema, patterns use familiar constraints such as: |
| 21 | +* const to enforce fixed values that identify required elements, |
| 22 | +* `prefixItems` and `minItems`/`maxItems` to require specific arrays of elements, and |
| 23 | +* `oneOf` / `anyOf` to offer allowable alternatives (e.g., different database options). |
| 24 | + |
| 25 | +This schema-based definition makes patterns self-validating, versionable, and compatible with existing tooling. |
| 26 | + |
| 27 | +## Key Properties of Patterns |
| 28 | + |
| 29 | +Patterns are primarily about **structural intent**—what must exist and how it must connect: |
| 30 | + |
| 31 | +* **Structural identifiers (fixed)**: commonly constrained with `const` (e.g., node `unique-id`, `node-type`, relationship `unique-id`, sometimes `name`). |
| 32 | +* **User-authored fields (open but required)**: fields like `description` are typically left as `"type": "string"` (and may be required), so generated architectures include placeholders that users should replace. |
| 33 | +* **Required arrays of components**: `prefixItems` + `minItems`/`maxItems` are commonly used to require a specific set/count of nodes and relationships. |
| 34 | +* **Choices**: `anyOf`/`oneOf` can model “pick one of these components/topologies” patterns. |
| 35 | + |
| 36 | +## Example pattern template |
| 37 | + |
| 38 | +Below is a small **3-tier web application** pattern template (frontend → API → database) that enforces **exactly 3 nodes** and **exactly 2 relationships**, while leaving `description` user-fillable. |
| 39 | + |
| 40 | +```json |
| 41 | +{ |
| 42 | + "$schema": "https://calm.finos.org/release/1.2/meta/calm.json", |
| 43 | + "$id": "https://example.com/patterns/web-app-pattern.json", |
| 44 | + "title": "Web Application Pattern", |
| 45 | + "description": "A reusable 3-tier web application pattern (frontend, API service, database).", |
| 46 | + "type": "object", |
| 47 | + "properties": { |
| 48 | + "nodes": { |
| 49 | + "type": "array", |
| 50 | + "minItems": 3, |
| 51 | + "maxItems": 3, |
| 52 | + "prefixItems": [ |
| 53 | + { |
| 54 | + "$ref": "https://calm.finos.org/release/1.2/meta/core.json#/defs/node", |
| 55 | + "type": "object", |
| 56 | + "properties": { |
| 57 | + "unique-id": { "const": "web-frontend" }, |
| 58 | + "node-type": { "const": "webclient" }, |
| 59 | + "name": { "const": "Web Frontend" }, |
| 60 | + "description": { "type": "string" } |
| 61 | + }, |
| 62 | + "required": ["description"] |
| 63 | + }, |
| 64 | + { |
| 65 | + "$ref": "https://calm.finos.org/release/1.2/meta/core.json#/defs/node", |
| 66 | + "type": "object", |
| 67 | + "properties": { |
| 68 | + "unique-id": { "const": "api-service" }, |
| 69 | + "node-type": { "const": "service" }, |
| 70 | + "name": { "const": "API Service" }, |
| 71 | + "description": { "type": "string" } |
| 72 | + }, |
| 73 | + "required": ["description"] |
| 74 | + }, |
| 75 | + { |
| 76 | + "$ref": "https://calm.finos.org/release/1.2/meta/core.json#/defs/node", |
| 77 | + "type": "object", |
| 78 | + "properties": { |
| 79 | + "unique-id": { "const": "app-database" }, |
| 80 | + "node-type": { "const": "database" }, |
| 81 | + "name": { "const": "Application Database" }, |
| 82 | + "description": { "type": "string" } |
| 83 | + }, |
| 84 | + "required": ["description"] |
| 85 | + } |
| 86 | + ] |
| 87 | + }, |
| 88 | + "relationships": { |
| 89 | + "type": "array", |
| 90 | + "minItems": 2, |
| 91 | + "maxItems": 2, |
| 92 | + "prefixItems": [ |
| 93 | + { |
| 94 | + "$ref": "https://calm.finos.org/release/1.2/meta/core.json#/defs/relationship", |
| 95 | + "type": "object", |
| 96 | + "properties": { |
| 97 | + "unique-id": { "const": "frontend-to-api" }, |
| 98 | + "description": { "type": "string" }, |
| 99 | + "protocol": { "const": "HTTPS" }, |
| 100 | + "relationship-type": { |
| 101 | + "const": { |
| 102 | + "connects": { |
| 103 | + "source": { "node": "web-frontend" }, |
| 104 | + "destination": { "node": "api-service" } |
| 105 | + } |
| 106 | + } |
| 107 | + } |
| 108 | + }, |
| 109 | + "required": ["description"] |
| 110 | + }, |
| 111 | + { |
| 112 | + "$ref": "https://calm.finos.org/release/1.2/meta/core.json#/defs/relationship", |
| 113 | + "type": "object", |
| 114 | + "properties": { |
| 115 | + "unique-id": { "const": "api-to-database" }, |
| 116 | + "description": { "type": "string" }, |
| 117 | + "protocol": { "const": "JDBC" }, |
| 118 | + "relationship-type": { |
| 119 | + "const": { |
| 120 | + "connects": { |
| 121 | + "source": { "node": "api-service" }, |
| 122 | + "destination": { "node": "app-database" } |
| 123 | + } |
| 124 | + } |
| 125 | + } |
| 126 | + }, |
| 127 | + "required": ["description"] |
| 128 | + } |
| 129 | + ] |
| 130 | + } |
| 131 | + }, |
| 132 | + "required": ["nodes", "relationships"] |
| 133 | +} |
| 134 | +``` |
| 135 | + |
| 136 | +This template follows the same techniques shown in the CALM Patterns guidance: use `const` for structural identity and `prefixItems`/`minItems`/`maxItems` to require exact architecture shape, while keeping human-authored fields open but required. |
| 137 | + |
| 138 | +## Using Patterns effectively |
| 139 | + |
| 140 | +### Generate an architecture scaffold |
| 141 | + |
| 142 | +```bash |
| 143 | +calm generate -p patterns/web-app-pattern.json -o architectures/generated-webapp.json |
| 144 | +``` |
| 145 | + |
| 146 | +This creates a concrete architecture that conforms to the pattern’s constraints. |
| 147 | + |
| 148 | +### Validate an existing architecture against the pattern |
| 149 | + |
| 150 | +```bash |
| 151 | +calm validate -p patterns/web-app-pattern.json -a architectures/existing-webapp.json |
| 152 | +``` |
| 153 | + |
| 154 | +This checks whether the target architecture has the required nodes/relationships and respects the pattern’s constraints. |
| 155 | + |
| 156 | +### Watch for placeholders |
| 157 | + |
| 158 | +User-fillable properties are emitted into the generated architecture using placeholder values based on their declared data type: |
| 159 | + |
| 160 | +* String properties use a bracketed token format, e.g., [[ DESCRIPTION ]] |
| 161 | +* Integer properties use a sentinel numeric value, e.g., -1 |
| 162 | + |
| 163 | +These placeholders act as markers and must be replaced with appropriate architecture-specific values before use. |
0 commit comments