Skip to content

Commit fd18ecf

Browse files
committed
docs(architecture): split schema docs into v1/v2 pages and update nav
- Add architecture overview and new v1 schema page - Rename existing architecture doc to v2 and update Getting Started link - Add mkdocs nav entries (including microservices example) - Improve loader error chaining for invalid container kind
1 parent fae4819 commit fd18ecf

7 files changed

Lines changed: 172 additions & 154 deletions

File tree

docs/architecture/index.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Architecture Model
2+
3+
The architecture model defines your system structure in YAML.
4+
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.

docs/architecture/v1.md

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
# v1 Schema
2+
3+
### File Format
4+
5+
Create `architecture.yml` in your repository root:
6+
7+
```yaml
8+
version: 1
9+
10+
system:
11+
id: my-system
12+
name: My System
13+
14+
containers:
15+
my-app:
16+
name: My Application
17+
description: Main application container
18+
code:
19+
roots:
20+
- src
21+
layers:
22+
ui:
23+
name: UI Layer
24+
patterns:
25+
- src/ui/**
26+
application:
27+
name: Application Layer
28+
patterns:
29+
- src/application/**
30+
domain:
31+
name: Domain Layer
32+
patterns:
33+
- src/domain/**
34+
infra:
35+
name: Infrastructure Layer
36+
patterns:
37+
- src/infra/**
38+
39+
contexts: {}
40+
```
41+
42+
### Schema Reference (v1)
43+
44+
#### system
45+
46+
| Field | Type | Required | Description |
47+
|-------|------|----------|-------------|
48+
| `id` | string | Yes | Unique system identifier |
49+
| `name` | string | Yes | Human-readable name |
50+
51+
#### containers
52+
53+
Map of container definitions.
54+
55+
| Field | Type | Required | Description |
56+
|-------|------|----------|-------------|
57+
| `name` | string | Yes | Container name |
58+
| `description` | string | No | Container description |
59+
| `context` | string | No | Bounded context reference |
60+
| `code` | object | No | Code mapping configuration |
61+
| `tags` | list | No | Tags inherited by nodes in this container |
62+
63+
#### code
64+
65+
| Field | Type | Required | Description |
66+
|-------|------|----------|-------------|
67+
| `roots` | list | Yes | Source root directories |
68+
| `layers` | map | No | Layer definitions |
69+
70+
#### layers
71+
72+
Map of layer definitions.
73+
74+
| Field | Type | Required | Description |
75+
|-------|------|----------|-------------|
76+
| `name` | string | No | Layer display name |
77+
| `description` | string | No | Layer description |
78+
| `patterns` | list | Yes | Glob patterns matching layer files |
79+
80+
#### relations
81+
82+
```yaml
83+
relations:
84+
- from: container-a
85+
to: container-b
86+
protocol: http
87+
description: A calls B
88+
```
89+
90+
### Example: Clean Architecture (v1)
91+
92+
```yaml
93+
version: 1
94+
95+
system:
96+
id: healthcare-scheduling
97+
name: Healthcare Scheduling
98+
99+
containers:
100+
scheduling-api:
101+
name: Scheduling API
102+
context: scheduling
103+
code:
104+
roots:
105+
- services/scheduling-api
106+
layers:
107+
ui:
108+
name: Presentation
109+
patterns:
110+
- services/scheduling-api/api/**
111+
application:
112+
name: Use Cases
113+
patterns:
114+
- services/scheduling-api/app/**
115+
domain:
116+
name: Domain
117+
patterns:
118+
- services/scheduling-api/domain/**
119+
infra:
120+
name: Infrastructure
121+
patterns:
122+
- services/scheduling-api/infra/**
123+
```
Lines changed: 1 addition & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -1,143 +1,4 @@
1-
# Architecture Model
2-
3-
The architecture model defines your system structure in YAML.
4-
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
17-
18-
Create `architecture.yml` in your repository root:
19-
20-
```yaml
21-
version: 1
22-
23-
system:
24-
id: my-system
25-
name: My System
26-
27-
containers:
28-
my-app:
29-
name: My Application
30-
description: Main application container
31-
code:
32-
roots:
33-
- src
34-
layers:
35-
ui:
36-
name: UI Layer
37-
patterns:
38-
- src/ui/**
39-
application:
40-
name: Application Layer
41-
patterns:
42-
- src/application/**
43-
domain:
44-
name: Domain Layer
45-
patterns:
46-
- src/domain/**
47-
infra:
48-
name: Infrastructure Layer
49-
patterns:
50-
- src/infra/**
51-
52-
contexts: {}
53-
```
54-
55-
### Schema Reference (v1)
56-
57-
#### system
58-
59-
| Field | Type | Required | Description |
60-
|-------|------|----------|-------------|
61-
| `id` | string | Yes | Unique system identifier |
62-
| `name` | string | Yes | Human-readable name |
63-
64-
#### containers
65-
66-
Map of container definitions.
67-
68-
| Field | Type | Required | Description |
69-
|-------|------|----------|-------------|
70-
| `name` | string | Yes | Container name |
71-
| `description` | string | No | Container description |
72-
| `context` | string | No | Bounded context reference |
73-
| `code` | object | No | Code mapping configuration |
74-
| `tags` | list | No | Tags inherited by nodes in this container |
75-
76-
#### code
77-
78-
| Field | Type | Required | Description |
79-
|-------|------|----------|-------------|
80-
| `roots` | list | Yes | Source root directories |
81-
| `layers` | map | No | Layer definitions |
82-
83-
#### layers
84-
85-
Map of layer definitions.
86-
87-
| Field | Type | Required | Description |
88-
|-------|------|----------|-------------|
89-
| `name` | string | No | Layer display name |
90-
| `description` | string | No | Layer description |
91-
| `patterns` | list | Yes | Glob patterns matching layer files |
92-
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)
104-
105-
```yaml
106-
version: 1
107-
108-
system:
109-
id: healthcare-scheduling
110-
name: Healthcare Scheduling
111-
112-
containers:
113-
scheduling-api:
114-
name: Scheduling API
115-
context: scheduling
116-
code:
117-
roots:
118-
- services/scheduling-api
119-
layers:
120-
ui:
121-
name: Presentation
122-
patterns:
123-
- services/scheduling-api/api/**
124-
application:
125-
name: Use Cases
126-
patterns:
127-
- services/scheduling-api/app/**
128-
domain:
129-
name: Domain
130-
patterns:
131-
- services/scheduling-api/domain/**
132-
infra:
133-
name: Infrastructure
134-
patterns:
135-
- services/scheduling-api/infra/**
136-
```
137-
138-
---
139-
140-
## v2 Schema
1+
# v2 Schema
1412

1423
v2 adds **nested containers**, explicit **container kinds**, and the `interactions:` alias for relations. v1 files remain fully valid — no migration is required.
1434

docs/getting-started.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,6 @@ myproject/
229229
**Next steps:**
230230

231231
- [CLI Reference](cli.md) — Commands and options
232-
- [Architecture Model](architecture.md) — Configuration schema
232+
- [Architecture Model](architecture/index.md) — Configuration schema
233233
- [Rules DSL](rules.md) — Rule conditions
234234
- [CI Integration](ci-integration.md) — Automate in your pipeline

mkdocs.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,15 @@ nav:
2424
- Getting Started: getting-started.md
2525
- User Guide:
2626
- CLI Reference: cli.md
27-
- Architecture Model: architecture.md
27+
- Architecture Model:
28+
- Overview: architecture/index.md
29+
- v1: architecture/v1.md
30+
- v2: architecture/v2.md
2831
- Rules DSL: rules.md
2932
- Examples:
3033
- Overview: examples/index.md
3134
- Simple Layered App: examples/simple-layered-app.md
35+
- Microservices Platform: examples/microservices-platform.md
3236
- Hexagonal Architecture: examples/hexagonal-app.md
3337
- Legacy Migration: examples/legacy-migration.md
3438
- Integration:

pacta/model/loader.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,14 +170,14 @@ def _parse_containers(self, raw: Any, *, version: int = 1) -> dict[str, Containe
170170
)
171171
try:
172172
kind = ContainerKind(str(raw_kind))
173-
except ValueError:
173+
except ValueError as ex:
174174
raise ModelLoadError(
175175
code="invalid_container_kind",
176176
message=(
177177
f"Container '{cid}' has invalid kind '{raw_kind}'. "
178178
f"Must be one of: {', '.join(k.value for k in ContainerKind)}."
179179
),
180-
)
180+
) from ex
181181
raw_contains = spec.get("contains")
182182
if raw_contains is not None:
183183
children = self._parse_containers(raw_contains, version=version)

tests/mapping/test_enricher.py

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -706,8 +706,12 @@ def test_enricher_v2_nested_container_deepest_match():
706706
path="services/billing/domain/invoice/model/entity.py",
707707
)
708708
ir = ArchitectureIR(
709-
schema_version=2, produced_by="test", repo_root="/test",
710-
nodes=(node,), edges=(), metadata={},
709+
schema_version=2,
710+
produced_by="test",
711+
repo_root="/test",
712+
nodes=(node,),
713+
edges=(),
714+
metadata={},
711715
)
712716

713717
enriched = DefaultArchitectureEnricher().enrich(ir, model)
@@ -731,8 +735,12 @@ def test_enricher_v2_parent_container_match():
731735
path="services/billing/api/routes.py",
732736
)
733737
ir = ArchitectureIR(
734-
schema_version=2, produced_by="test", repo_root="/test",
735-
nodes=(node,), edges=(), metadata={},
738+
schema_version=2,
739+
produced_by="test",
740+
repo_root="/test",
741+
nodes=(node,),
742+
edges=(),
743+
metadata={},
736744
)
737745

738746
enriched = DefaultArchitectureEnricher().enrich(ir, model)
@@ -754,8 +762,12 @@ def test_enricher_v2_library_container():
754762
path="libs/shared/util.py",
755763
)
756764
ir = ArchitectureIR(
757-
schema_version=2, produced_by="test", repo_root="/test",
758-
nodes=(node,), edges=(), metadata={},
765+
schema_version=2,
766+
produced_by="test",
767+
repo_root="/test",
768+
nodes=(node,),
769+
edges=(),
770+
metadata={},
759771
)
760772

761773
enriched = DefaultArchitectureEnricher().enrich(ir, model)
@@ -783,8 +795,12 @@ def test_enricher_v2_edge_service_and_kind():
783795
edge = IREdge(src=src_node.id, dst=dst_node.id, dep_type=DepType.IMPORT)
784796

785797
ir = ArchitectureIR(
786-
schema_version=2, produced_by="test", repo_root="/test",
787-
nodes=(src_node, dst_node), edges=(edge,), metadata={},
798+
schema_version=2,
799+
produced_by="test",
800+
repo_root="/test",
801+
nodes=(src_node, dst_node),
802+
edges=(edge,),
803+
metadata={},
788804
)
789805

790806
enriched = DefaultArchitectureEnricher().enrich(ir, model)
@@ -806,8 +822,12 @@ def test_enricher_v2_unmatched_node_has_no_service():
806822
path="other/utils.py",
807823
)
808824
ir = ArchitectureIR(
809-
schema_version=2, produced_by="test", repo_root="/test",
810-
nodes=(node,), edges=(), metadata={},
825+
schema_version=2,
826+
produced_by="test",
827+
repo_root="/test",
828+
nodes=(node,),
829+
edges=(),
830+
metadata={},
811831
)
812832

813833
enriched = DefaultArchitectureEnricher().enrich(ir, model)

0 commit comments

Comments
 (0)