Skip to content

Commit 1ff59ca

Browse files
dmealingclaude
andcommitted
docs(codegen): add codegen scopes (object/package/app) to the concepts guide
Captures the scope axis from the Java codegen scope investigation: object scope (perEntity) and app scope (oncePerRun, e.g. PlantUML whole-model diagram / Kotlin Spring @configuration / a global registry) are first-class; package scope is the gap (Java did it ad-hoc via getPackage() grouping; we make perPackage first-class). The scope IS the walk — what the generator iterates over. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LuZWKnWzYGVnESijL7uuky
1 parent a58815d commit 1ff59ca

1 file changed

Lines changed: 37 additions & 0 deletions

File tree

docs/features/codegen-concepts.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,3 +116,40 @@ C#, Spring for Java/Kotlin, Pydantic for Python) — collapsing them to one gene
116116
emitter is the OpenAPI-Generator trap. Keep language-*neutral* artifacts (migrations,
117117
docs, future OpenAPI/Mermaid) as **one shared engine**. The test: does the output depend
118118
on the implementation language? If yes, it's per-port; if no, it's shared. (ADR-0020.)
119+
120+
## 10. Codegen scopes — object, package, app
121+
122+
MetaObjects codegen has been object-centric (one artifact per MetaObject), but
123+
generation has three natural scopes, and all three are legitimate first-class
124+
patterns. **The scope *is* the walk** — what the generator iterates over:
125+
126+
| Scope | Walks | Emits | Examples |
127+
|---|---|---|---|
128+
| **Object** | each MetaObject | one artifact per object | entity, queries, routes, DTO, repository |
129+
| **Package** | each package (objects grouped by package) | one artifact per package | a service-layer module, a package barrel/index, a package-scoped registry or config |
130+
| **App** | the whole model once | one artifact for the app | a shared component, an app-wide config (e.g. a Spring `@Configuration`), a global object/binding registry, the overall barrel, a whole-model diagram |
131+
132+
**Lineage (the Java predecessor).** App-level generation was first-class there —
133+
`SingleFileDirectGeneratorBase` (one whole-model PlantUML diagram), or overriding
134+
`execute(loader)` to emit a single app config (`KotlinSpringConfigGenerator`), or the
135+
`getFinalWriter()` hook (per-object files *plus* one final app file). Package-level
136+
was *not* first-class even there — it was done ad-hoc, grouping by `getPackage()`
137+
inside a generator's `execute()` (the FR-019 shared-enum aggregation). The newest,
138+
cleanest shape is the cross-port `TemplateGenerator`'s caller-supplied **`walk`
139+
function**: the walk returns the units to render, so per-object / per-package /
140+
whole-model are just different walks.
141+
142+
**The model to expose.** Three scope helpers, so the scope is declared, not buried in
143+
custom iteration:
144+
145+
- **`perEntity(fn)`** — object scope. *(exists)*
146+
- **`perPackage(fn)`** — package scope: group matching objects by package, run `fn`
147+
once per package. *(the gap — Java never had this cleanly either, so this is an
148+
improvement on the predecessor, not just parity.)*
149+
- **`oncePerRun(fn)`** — app scope: run once over all matching objects. *(exists; an
150+
`appLevel` alias can make the intent explicit.)*
151+
152+
A generator picks its scope by which helper it composes — the same way it picks
153+
object vs run today. Reference templates and the guidance call out the scope each
154+
starting point is for, so "I need a per-package service layer" maps to a clear
155+
pattern instead of hand-rolled grouping.

0 commit comments

Comments
 (0)