Skip to content

Commit fae4819

Browse files
committed
docs: document v2 architecture schema and add microservices example
1 parent dd7f863 commit fae4819

5 files changed

Lines changed: 255 additions & 8 deletions

File tree

docs/architecture.md

Lines changed: 153 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,18 @@
22

33
The architecture model defines your system structure in YAML.
44

5-
## File Format
5+
Pacta supports two schema versions:
6+
7+
- **v1** — Flat containers with layers. Simple and sufficient for single-service architectures.
8+
- **v2** — Nested containers with `kind` and `contains`. Designed for microservices and modular monoliths.
9+
10+
The loader auto-detects the version from the `version:` key. If absent, v1 is assumed.
11+
12+
---
13+
14+
## v1 Schema
15+
16+
### File Format
617

718
Create `architecture.yml` in your repository root:
819

@@ -41,16 +52,16 @@ containers:
4152
contexts: {}
4253
```
4354
44-
## Schema
55+
### Schema Reference (v1)
4556
46-
### system
57+
#### system
4758
4859
| Field | Type | Required | Description |
4960
|-------|------|----------|-------------|
5061
| `id` | string | Yes | Unique system identifier |
5162
| `name` | string | Yes | Human-readable name |
5263

53-
### containers
64+
#### containers
5465

5566
Map of container definitions.
5667

@@ -60,15 +71,16 @@ Map of container definitions.
6071
| `description` | string | No | Container description |
6172
| `context` | string | No | Bounded context reference |
6273
| `code` | object | No | Code mapping configuration |
74+
| `tags` | list | No | Tags inherited by nodes in this container |
6375

64-
### code
76+
#### code
6577

6678
| Field | Type | Required | Description |
6779
|-------|------|----------|-------------|
6880
| `roots` | list | Yes | Source root directories |
6981
| `layers` | map | No | Layer definitions |
7082

71-
### layers
83+
#### layers
7284

7385
Map of layer definitions.
7486

@@ -78,7 +90,17 @@ Map of layer definitions.
7890
| `description` | string | No | Layer description |
7991
| `patterns` | list | Yes | Glob patterns matching layer files |
8092

81-
## Example: Clean Architecture
93+
#### relations
94+
95+
```yaml
96+
relations:
97+
- from: container-a
98+
to: container-b
99+
protocol: http
100+
description: A calls B
101+
```
102+
103+
### Example: Clean Architecture (v1)
82104

83105
```yaml
84106
version: 1
@@ -112,3 +134,127 @@ containers:
112134
patterns:
113135
- services/scheduling-api/infra/**
114136
```
137+
138+
---
139+
140+
## v2 Schema
141+
142+
v2 adds **nested containers**, explicit **container kinds**, and the `interactions:` alias for relations. v1 files remain fully valid — no migration is required.
143+
144+
### What's New in v2
145+
146+
| Feature | Description |
147+
|---------|-------------|
148+
| `kind` | Required on every container: `service`, `module`, or `library` |
149+
| `contains` | Nest child containers inside a parent |
150+
| `interactions` | Alias for `relations` (both accepted) |
151+
| Dot-qualified IDs | Nested containers get IDs like `billing-service.invoice-module` |
152+
| Context inheritance | Children inherit parent's `context` unless they override it |
153+
154+
### File Format (v2)
155+
156+
```yaml
157+
version: 2
158+
159+
system:
160+
id: my-platform
161+
name: My Platform
162+
163+
contexts:
164+
billing:
165+
name: Billing Context
166+
167+
containers:
168+
billing-service:
169+
kind: service
170+
name: Billing Service
171+
context: billing
172+
code:
173+
roots: [services/billing]
174+
layers:
175+
api: [services/billing/api/**]
176+
domain: [services/billing/domain/**]
177+
contains:
178+
invoice-module:
179+
kind: module
180+
name: Invoice Module
181+
code:
182+
roots: [services/billing/domain/invoice]
183+
layers:
184+
model: [services/billing/domain/invoice/model/**]
185+
repo: [services/billing/domain/invoice/repo/**]
186+
187+
shared-utils:
188+
kind: library
189+
name: Shared Utilities
190+
code:
191+
roots: [libs/shared]
192+
193+
interactions:
194+
- from: billing-service
195+
to: shared-utils
196+
protocol: import
197+
```
198+
199+
### Schema Reference (v2)
200+
201+
v2 containers extend the v1 schema with these additional fields:
202+
203+
| Field | Type | Required | Description |
204+
|-------|------|----------|-------------|
205+
| `kind` | string | **Yes** | `service`, `module`, or `library` |
206+
| `contains` | map | No | Nested child containers (same schema, recursive) |
207+
208+
All v1 container fields (`name`, `description`, `context`, `code`, `tags`) remain unchanged.
209+
210+
### Container Kinds
211+
212+
| Kind | Meaning |
213+
|------|---------|
214+
| `service` | Deployable service or application |
215+
| `module` | Logical module within a service |
216+
| `library` | Shared library used by other containers |
217+
218+
### Dot-Qualified IDs
219+
220+
Nested containers are addressed using dot-qualified IDs. For the example above:
221+
222+
- `billing-service` — the top-level service
223+
- `billing-service.invoice-module` — the nested module
224+
225+
These IDs are used in `interactions`, rules, and enrichment output.
226+
227+
### Context Inheritance
228+
229+
Children inherit their parent's `context:` unless they explicitly set their own:
230+
231+
```yaml
232+
containers:
233+
billing-service:
234+
kind: service
235+
context: billing # set here
236+
contains:
237+
invoice-module:
238+
kind: module # inherits context: billing
239+
payments-module:
240+
kind: module
241+
context: payments # overrides with own context
242+
```
243+
244+
### Version Detection
245+
246+
| `version:` value | Behavior |
247+
|------------------|----------|
248+
| absent | v1 (default) |
249+
| `1` | v1 |
250+
| `2` | v2 — `kind` required, `contains` and `interactions` supported |
251+
| anything else | Error |
252+
253+
### v1 → v2 Key Mapping
254+
255+
| v1 key | v2 key | Notes |
256+
|--------|--------|-------|
257+
| `context:` | `context:` | Unchanged |
258+
| `relations:` | `interactions:` | Both accepted in v2; `relations:` takes precedence if both present |
259+
| _(n/a)_ | `contains:` | New in v2 |
260+
| _(n/a)_ | `kind:` | Required in v2 |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
--8<-- "examples/microservices-platform/README.md"

docs/rules.md

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,38 @@ rule:
3434
| `message` | string | No | Message shown on violation (auto-generated if omitted) |
3535
| `suggestion` | string | No | Remediation guidance |
3636

37+
## Available Fields
38+
39+
### Node Fields (`target: node`)
40+
41+
| Field | Description |
42+
|-------|-------------|
43+
| `node.symbol_kind` | Symbol type: `file`, `module`, `class`, `function`, etc. |
44+
| `node.kind` | Container kind: `service`, `module`, `library` (v2 only) |
45+
| `node.service` | Top-level container ancestor (v2 only) |
46+
| `node.path` | File path |
47+
| `node.name` | Symbol name |
48+
| `node.layer` | Architectural layer |
49+
| `node.context` | Bounded context |
50+
| `node.container` | Container ID (dot-qualified for nested containers in v2) |
51+
| `node.tags` | Tags inherited from container |
52+
| `node.fqname` | Fully qualified name |
53+
| `node.language` | Source language |
54+
55+
### Dependency Fields (`target: dependency`)
56+
57+
| Field | Description |
58+
|-------|-------------|
59+
| `from.layer` / `to.layer` | Source/target layer |
60+
| `from.context` / `to.context` | Source/target bounded context |
61+
| `from.container` / `to.container` | Source/target container ID |
62+
| `from.service` / `to.service` | Source/target top-level service (v2 only) |
63+
| `from.kind` / `to.kind` | Source/target container kind (v2 only) |
64+
| `from.fqname` / `to.fqname` | Fully qualified names |
65+
| `from.id` / `to.id` | Full canonical ID strings |
66+
| `dep.type` | Dependency type (`import`, `call`, etc.) |
67+
| `loc.file` | Source location file |
68+
3769
## Conditions
3870

3971
### Layer Conditions
@@ -59,7 +91,7 @@ when:
5991
- to.layer == application
6092
```
6193

62-
## Example: Clean Architecture Rules
94+
## Example: Clean Architecture Rules (v1)
6395

6496
```yaml
6597
# Domain cannot depend on Infrastructure
@@ -121,3 +153,35 @@ rule:
121153
message: UI layer should not directly depend on Infrastructure layer
122154
suggestion: Access infrastructure through application services instead
123155
```
156+
157+
## Example: Cross-Service Rules (v2)
158+
159+
These rules use v2-only fields (`from.service`, `to.service`, `from.kind`, `to.kind`):
160+
161+
```yaml
162+
# Forbid cross-service domain dependencies
163+
rule:
164+
id: no-cross-service-domain-deps
165+
name: Domain must not depend on other services
166+
severity: error
167+
target: dependency
168+
action: forbid
169+
when:
170+
all:
171+
- from.service != to.service
172+
- from.layer == domain
173+
message: Domain code must not depend on another service
174+
175+
# Libraries must not depend on services
176+
rule:
177+
id: library-no-service-deps
178+
name: Libraries must be independent of services
179+
severity: error
180+
target: dependency
181+
action: forbid
182+
when:
183+
all:
184+
- from.kind == library
185+
- to.kind == service
186+
message: Library code must not import service code
187+
```

examples/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Pacta includes several example projects demonstrating different architectural pa
99
| [Simple Layered App](simple-layered-app.md) | Classic N-tier architecture | Teams familiar with layered architecture |
1010
| [Hexagonal Architecture](hexagonal-app.md) | Ports and Adapters pattern | Domain-driven design, high testability |
1111
| [Legacy Migration](legacy-migration.md) | Baseline workflow for brownfield | Existing codebases, incremental adoption |
12+
| [Microservices Platform](microservices-platform.md) | v2 schema with nested containers | Multi-service systems, modular monoliths |
1213

1314
## Quick Start
1415

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Microservices Platform Example
2+
3+
This example demonstrates the **v2 schema** with nested containers, container kinds, and cross-service rules.
4+
5+
## Architecture
6+
7+
The platform consists of three containers:
8+
9+
- **billing-service** (`kind: service`) — handles invoicing
10+
- **invoice-module** (`kind: module`) — nested module for invoice domain logic
11+
- **identity-service** (`kind: service`) — handles authentication
12+
- **shared-utils** (`kind: library`) — shared utilities
13+
14+
## Key v2 Features
15+
16+
- **`kind`** — every container declares whether it's a `service`, `module`, or `library`
17+
- **`contains`**`billing-service` nests `invoice-module` inside it
18+
- **`interactions`** — v2 alias for `relations`
19+
- **Dot-qualified IDs** — the nested module is addressed as `billing-service.invoice-module`
20+
- **Context inheritance**`invoice-module` inherits `context: billing` from its parent
21+
22+
## Rules
23+
24+
The rules demonstrate v2-specific fields:
25+
26+
- `from.service != to.service` — detect cross-service dependencies
27+
- `from.kind == library` / `to.kind == service` — enforce library independence
28+
- `from.layer` / `to.layer` — standard layer constraints
29+
30+
## Running
31+
32+
```bash
33+
cd examples/microservices-platform
34+
pacta scan . --model architecture.yml --rules rules.pacta.yml
35+
```

0 commit comments

Comments
 (0)