@sumrco/cli 0.2.1 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ai/modules/kontract/resources/api-generation-standards.rf.md +83 -0
- package/ai/modules/kontract/resources/authoring/configuration.rf.md +220 -0
- package/ai/modules/kontract/resources/authoring/design-profile/api-styles.rf.md +241 -0
- package/ai/modules/kontract/resources/authoring/design-profile/async-styles.rf.md +134 -0
- package/ai/modules/kontract/resources/authoring/design-profile/overview.md +64 -0
- package/ai/modules/kontract/resources/authoring/design-profile/schema-layout-styles.rf.md +356 -0
- package/ai/modules/kontract/resources/authoring/overview.md +56 -0
- package/ai/modules/kontract/resources/authoring/schema-reuse.rf.md +492 -0
- package/ai/modules/kontract/resources/authoring/scope-and-splitting.rf.md +244 -0
- package/ai/modules/kontract/resources/authoring/spec-layout.rf.md +492 -0
- package/ai/modules/kontract/resources/authoring/team-members/spec-author.tm.md +77 -0
- package/ai/modules/kontract/resources/{workflows/contract-change.wf.md → authoring/workflows/spec-change.wf.md} +24 -16
- package/ai/modules/kontract/resources/generated-output.rf.md +185 -17
- package/ai/modules/kontract/resources/openapi-sdk-generator-research.rf.md +57 -0
- package/ai/modules/kontract/resources/overview.md +130 -44
- package/ai/modules/kontract/resources/performance.rf.md +7 -7
- package/ai/modules/kontract/sumr.module.yaml +5 -2
- package/ai/modules/mission/sumr.module.yaml +6 -0
- package/ai/modules/playbook/resources/authoring/content-structure.rf.md +1 -1
- package/ai/modules/playbook/resources/authoring/cross-referencing.rf.md +1 -1
- package/ai/modules/playbook/resources/authoring/descriptions.rf.md +1 -1
- package/ai/modules/playbook/resources/authoring/extraction.rf.md +1 -1
- package/ai/modules/playbook/resources/authoring/flows.rf.md +1 -1
- package/ai/modules/playbook/resources/authoring/folder-structure.rf.md +1 -1
- package/ai/modules/playbook/resources/authoring/frontmatter.rf.md +4 -1
- package/ai/modules/playbook/resources/authoring/markdown.rf.md +1 -1
- package/ai/modules/playbook/resources/authoring/overview.md +1 -1
- package/ai/modules/playbook/resources/team-members/{playbook-technical-writer.tm.md → technical-writer.tm.md} +3 -2
- package/ai/modules/playbook/sumr.module.yaml +7 -2
- package/index.js +486 -284
- package/package.json +1 -1
- package/ai/modules/kontract/resources/configuration.rf.md +0 -123
- package/ai/modules/kontract/resources/openapi-generator-lessons.rf.md +0 -61
- package/ai/modules/kontract/resources/schema-reuse.rf.md +0 -233
- package/ai/modules/kontract/resources/scope-and-splitting.rf.md +0 -147
- package/ai/modules/kontract/resources/spec-layout.rf.md +0 -69
- package/ai/modules/kontract/resources/team-members/contract-author.tm.md +0 -52
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
---
|
|
2
|
+
category: reference
|
|
3
|
+
name: schema-reuse
|
|
4
|
+
title: Schema Reuse and Shared Components
|
|
5
|
+
description: "How to author API and async schemas with reusable shared components, $ref, Kontract sets, and safe composition so Kontract output stays consistent and avoids duplicated contract shapes."
|
|
6
|
+
label: Schema Reuse
|
|
7
|
+
when: Adding repeated fields, response envelopes, pagination, errors, schedules, config objects, or reviewing generated diffs with duplicated DTO/model shapes
|
|
8
|
+
order: 12
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Schema Reuse and Shared Components
|
|
12
|
+
|
|
13
|
+
Kontract works best when the spec is designed like a product API, not like a
|
|
14
|
+
collection of copied YAML snippets. If the same concept appears in multiple
|
|
15
|
+
operations or services, define it once and reference it with `$ref`.
|
|
16
|
+
|
|
17
|
+
This reduces drift in the source specs immediately. It also gives Kontract a
|
|
18
|
+
clear path to emit more shared generated code as the generator evolves.
|
|
19
|
+
|
|
20
|
+
Before extracting or naming a shared component, choose a name from product
|
|
21
|
+
language. Shared names should stay specific enough for generated DTO/model/SDK
|
|
22
|
+
symbols.
|
|
23
|
+
|
|
24
|
+
## Local-first promotion model
|
|
25
|
+
|
|
26
|
+
Keep a schema in the smallest owning place until reuse proves it should move:
|
|
27
|
+
|
|
28
|
+
1. **Surface-local** — define one-off request, response, and model shapes in the
|
|
29
|
+
root file's `components.schemas`.
|
|
30
|
+
2. **Capability-local shared** — move the shape into the capability's
|
|
31
|
+
`shared/` folder when two or more surfaces in that capability reuse the same
|
|
32
|
+
concept.
|
|
33
|
+
3. **Repo-level shared** — move the shape into `schema/shared/` only when two or
|
|
34
|
+
more capabilities share the same semantics, lifecycle, and owner.
|
|
35
|
+
|
|
36
|
+
Do not create shared files only because a shape might become reusable later.
|
|
37
|
+
Shared components are a promotion step, not the default folder for every model.
|
|
38
|
+
|
|
39
|
+
## What to share
|
|
40
|
+
|
|
41
|
+
Prefer shared components for concepts that mean the same thing everywhere:
|
|
42
|
+
|
|
43
|
+
- scalar primitives: UUIDs, ISO dates, slugs, URLs, MIME types, time strings;
|
|
44
|
+
- path/query parameters: `id`, `recipeId`, pagination, date ranges;
|
|
45
|
+
- standard error responses and error body schemas;
|
|
46
|
+
- pagination metadata;
|
|
47
|
+
- common response envelope fields such as `success`, `timestamp`, `path`, and
|
|
48
|
+
optional `message`;
|
|
49
|
+
- weekly schedules and reusable time-slot shapes;
|
|
50
|
+
- repeated configuration objects, such as measurement units, recipe step labels,
|
|
51
|
+
display colors, notification settings, or payment settings when they truly
|
|
52
|
+
share semantics.
|
|
53
|
+
|
|
54
|
+
Example shared scalar and parameter file:
|
|
55
|
+
|
|
56
|
+
```yaml
|
|
57
|
+
# schema/shared/common.yml
|
|
58
|
+
components:
|
|
59
|
+
schemas:
|
|
60
|
+
UuidString:
|
|
61
|
+
type: string
|
|
62
|
+
format: uuid
|
|
63
|
+
ImageMimeType:
|
|
64
|
+
type: string
|
|
65
|
+
pattern: '^image\/(jpeg|png|webp|gif)$'
|
|
66
|
+
PaginationMeta:
|
|
67
|
+
type: object
|
|
68
|
+
required: [page, pageSize, pageCount, total]
|
|
69
|
+
properties:
|
|
70
|
+
page:
|
|
71
|
+
type: integer
|
|
72
|
+
minimum: 1
|
|
73
|
+
pageSize:
|
|
74
|
+
type: integer
|
|
75
|
+
minimum: 1
|
|
76
|
+
pageCount:
|
|
77
|
+
type: integer
|
|
78
|
+
minimum: 0
|
|
79
|
+
total:
|
|
80
|
+
type: integer
|
|
81
|
+
minimum: 0
|
|
82
|
+
parameters:
|
|
83
|
+
id:
|
|
84
|
+
name: id
|
|
85
|
+
in: path
|
|
86
|
+
required: true
|
|
87
|
+
schema:
|
|
88
|
+
$ref: '#/components/schemas/UuidString'
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Use those components from service specs:
|
|
92
|
+
|
|
93
|
+
```yaml
|
|
94
|
+
parameters:
|
|
95
|
+
- $ref: ../shared/common.yml#/components/parameters/id
|
|
96
|
+
content:
|
|
97
|
+
application/json:
|
|
98
|
+
schema:
|
|
99
|
+
$ref: ../shared/common.yml#/components/schemas/ImageMimeType
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Protocol scenarios for reuse
|
|
103
|
+
|
|
104
|
+
Pick the protocol scenario before extracting shared components:
|
|
105
|
+
|
|
106
|
+
1. **API-only** — share HTTP schemas, parameters, responses, envelopes,
|
|
107
|
+
pagination, errors, and scalar constraints through API schema components.
|
|
108
|
+
2. **async-only** — share message payload schemas, message enums, scalar
|
|
109
|
+
constraints, and value objects through async schema components or
|
|
110
|
+
capability-local model fragments.
|
|
111
|
+
3. **API + async** — keep protocol layers separate and share only neutral
|
|
112
|
+
components with identical meaning. The safest cross-protocol sharing is
|
|
113
|
+
usually enums, IDs, slugs, scalar value objects, and simple payload shapes.
|
|
114
|
+
|
|
115
|
+
Do not create cross-protocol shared files only because API schema and async schema
|
|
116
|
+
payloads look similar today. Shared components must have the same product
|
|
117
|
+
meaning and owner.
|
|
118
|
+
|
|
119
|
+
## API-only reuse
|
|
120
|
+
|
|
121
|
+
If the repo only has API schema sources, keep one-off schemas in the owning
|
|
122
|
+
API schema surface. Use a capability-local `shared/` folder only after multiple
|
|
123
|
+
surfaces in that capability reuse the same concept. Use `schema/shared/` only
|
|
124
|
+
after multiple capabilities share the same concept.
|
|
125
|
+
|
|
126
|
+
```text
|
|
127
|
+
schema/
|
|
128
|
+
bookstore/
|
|
129
|
+
main.api.yml
|
|
130
|
+
catalog.api.yml
|
|
131
|
+
reviews.api.yml
|
|
132
|
+
shared/
|
|
133
|
+
enums.yml # reused by catalog and reviews
|
|
134
|
+
components.yml
|
|
135
|
+
shared/
|
|
136
|
+
errors.yml # reused by multiple capabilities
|
|
137
|
+
pagination.yml
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Use API schema `components` for reusable HTTP shapes:
|
|
141
|
+
|
|
142
|
+
```yaml
|
|
143
|
+
# schema/bookstore/shared/enums.yml
|
|
144
|
+
components:
|
|
145
|
+
schemas:
|
|
146
|
+
BookFormat:
|
|
147
|
+
type: string
|
|
148
|
+
enum: [hardcover, paperback, ebook]
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## async-only reuse
|
|
152
|
+
|
|
153
|
+
If the repo only has async schema sources, keep reusable message payload schemas and
|
|
154
|
+
enums close to the messaging roots or under `schema/shared/` when several
|
|
155
|
+
message families share the same meaning.
|
|
156
|
+
|
|
157
|
+
```text
|
|
158
|
+
schema/
|
|
159
|
+
shared/
|
|
160
|
+
message-enums.yml
|
|
161
|
+
bookstore/
|
|
162
|
+
bookstore.async.yml
|
|
163
|
+
messages/
|
|
164
|
+
book-commands.yml
|
|
165
|
+
book-events.yml
|
|
166
|
+
models/
|
|
167
|
+
books.yml
|
|
168
|
+
enums.yml
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
```yaml
|
|
172
|
+
# schema/bookstore/models/enums.yml
|
|
173
|
+
BookFormat:
|
|
174
|
+
type: string
|
|
175
|
+
enum: [hardcover, paperback, ebook]
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## API + async shared components
|
|
179
|
+
|
|
180
|
+
When one product capability has both HTTP and messaging contracts, keep protocol
|
|
181
|
+
entrypoints separate and share only business schemas.
|
|
182
|
+
|
|
183
|
+
```text
|
|
184
|
+
schema/
|
|
185
|
+
bookstore/
|
|
186
|
+
bookstore.api.yml
|
|
187
|
+
bookstore.async.yml
|
|
188
|
+
paths/books.yml
|
|
189
|
+
messages/books.yml
|
|
190
|
+
models/enums.yml
|
|
191
|
+
models/books.yml
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Good shared fragment:
|
|
195
|
+
|
|
196
|
+
```yaml
|
|
197
|
+
# schema/bookstore/models/enums.yml
|
|
198
|
+
BookFormat:
|
|
199
|
+
type: string
|
|
200
|
+
enum: [hardcover, paperback, ebook]
|
|
201
|
+
|
|
202
|
+
# schema/bookstore/models/books.yml
|
|
203
|
+
BookSlug:
|
|
204
|
+
type: string
|
|
205
|
+
minLength: 1
|
|
206
|
+
maxLength: 120
|
|
207
|
+
pattern: "^[a-z0-9]+(?:-[a-z0-9]+)*$"
|
|
208
|
+
|
|
209
|
+
PaginationMeta:
|
|
210
|
+
$ref: ../../shared/common.yml#/components/schemas/PaginationMeta
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
API schema can use it through path and response fragments:
|
|
214
|
+
|
|
215
|
+
```yaml
|
|
216
|
+
# schema/bookstore/paths/books.yml
|
|
217
|
+
parameters:
|
|
218
|
+
- name: bookSlug
|
|
219
|
+
in: path
|
|
220
|
+
required: true
|
|
221
|
+
schema:
|
|
222
|
+
$ref: ../models/books.yml#/BookSlug
|
|
223
|
+
get:
|
|
224
|
+
operationId: getBook
|
|
225
|
+
responses:
|
|
226
|
+
"200":
|
|
227
|
+
description: Book detail.
|
|
228
|
+
content:
|
|
229
|
+
application/json:
|
|
230
|
+
schema:
|
|
231
|
+
$ref: ../models/books.yml#/Book
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
async schema can use the same payload schema from a message fragment:
|
|
235
|
+
|
|
236
|
+
```yaml
|
|
237
|
+
# schema/bookstore/messages/books.yml
|
|
238
|
+
messages:
|
|
239
|
+
GetBookQuery:
|
|
240
|
+
payload:
|
|
241
|
+
$ref: ../models/books.yml#/GetBookQuery
|
|
242
|
+
BookReply:
|
|
243
|
+
payload:
|
|
244
|
+
$ref: ../models/books.yml#/Book
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Keep protocol-specific pieces out of `models/`:
|
|
248
|
+
|
|
249
|
+
- API schema paths, parameters, request bodies, responses → `paths/` or
|
|
250
|
+
`components/`.
|
|
251
|
+
- async schema channels, operations, messages → `messages/`.
|
|
252
|
+
- Payload objects, enums, scalars, and reusable business shapes → `models/`.
|
|
253
|
+
- Share enums through `models/enums.yml` when API schema and async schema use the same
|
|
254
|
+
vocabulary and values.
|
|
255
|
+
|
|
256
|
+
This keeps API contracts, async messages, and generated models aligned without
|
|
257
|
+
making either protocol depend on the other's metadata.
|
|
258
|
+
|
|
259
|
+
## Kontract sets for reusable map fragments
|
|
260
|
+
|
|
261
|
+
Use `kontract.sets` when repeated YAML maps make the source noisy but the
|
|
262
|
+
reusable concept is not a standalone schema. `$sets` can apply reusable maps in
|
|
263
|
+
responses, parameters, schema properties, headers, request bodies, async
|
|
264
|
+
operations, messages, payload schemas, or any other mapping.
|
|
265
|
+
|
|
266
|
+
```yaml
|
|
267
|
+
# schema/bookings/shared/components.yml
|
|
268
|
+
kontract:
|
|
269
|
+
sets:
|
|
270
|
+
StandardErrors:
|
|
271
|
+
"400":
|
|
272
|
+
$ref: '#/components/responses/BadRequest'
|
|
273
|
+
"401":
|
|
274
|
+
$ref: '#/components/responses/Unauthorized'
|
|
275
|
+
"403":
|
|
276
|
+
$ref: '#/components/responses/Forbidden'
|
|
277
|
+
"404":
|
|
278
|
+
$ref: '#/components/responses/NotFound'
|
|
279
|
+
"500":
|
|
280
|
+
$ref: '#/components/responses/InternalServerError'
|
|
281
|
+
AuditedFields:
|
|
282
|
+
createdAt:
|
|
283
|
+
type: string
|
|
284
|
+
format: date-time
|
|
285
|
+
updatedAt:
|
|
286
|
+
type: string
|
|
287
|
+
format: date-time
|
|
288
|
+
components:
|
|
289
|
+
responses:
|
|
290
|
+
BadRequest:
|
|
291
|
+
description: Bad request.
|
|
292
|
+
Unauthorized:
|
|
293
|
+
description: Unauthorized.
|
|
294
|
+
Forbidden:
|
|
295
|
+
description: Forbidden.
|
|
296
|
+
NotFound:
|
|
297
|
+
description: Not found.
|
|
298
|
+
InternalServerError:
|
|
299
|
+
description: Internal server error.
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
```yaml
|
|
303
|
+
# schema/bookings/public.api.yml
|
|
304
|
+
api: 3.0.3
|
|
305
|
+
paths:
|
|
306
|
+
/bookings:
|
|
307
|
+
post:
|
|
308
|
+
responses:
|
|
309
|
+
$sets:
|
|
310
|
+
- $ref: ./shared/components.yml#/kontract/sets/StandardErrors
|
|
311
|
+
"201":
|
|
312
|
+
description: Booking created.
|
|
313
|
+
content:
|
|
314
|
+
application/json:
|
|
315
|
+
schema:
|
|
316
|
+
$ref: '#/components/schemas/CreateBookingResponse'
|
|
317
|
+
components:
|
|
318
|
+
schemas:
|
|
319
|
+
Booking:
|
|
320
|
+
type: object
|
|
321
|
+
properties:
|
|
322
|
+
$sets:
|
|
323
|
+
- $ref: ./shared/components.yml#/kontract/sets/AuditedFields
|
|
324
|
+
id:
|
|
325
|
+
type: string
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
Set rules:
|
|
329
|
+
|
|
330
|
+
- `$sets` merges maps only; it does not concatenate arrays.
|
|
331
|
+
- Local keys override set keys.
|
|
332
|
+
- Multiple sets that define the same key must use identical values.
|
|
333
|
+
- Kontract rebases copied `$ref` values when a set comes from another file.
|
|
334
|
+
- Normalized files passed to validators and generators do not contain
|
|
335
|
+
`kontract` or `$sets`.
|
|
336
|
+
|
|
337
|
+
## Response envelopes
|
|
338
|
+
|
|
339
|
+
If every endpoint repeats the same envelope fields, do not retype them in every
|
|
340
|
+
schema. Put the shared part in a component and compose concrete responses with
|
|
341
|
+
`allOf`.
|
|
342
|
+
|
|
343
|
+
```yaml
|
|
344
|
+
# schema/shared/common.yml
|
|
345
|
+
components:
|
|
346
|
+
schemas:
|
|
347
|
+
SuccessResponseBase:
|
|
348
|
+
type: object
|
|
349
|
+
required: [success, timestamp, path]
|
|
350
|
+
properties:
|
|
351
|
+
success:
|
|
352
|
+
type: boolean
|
|
353
|
+
timestamp:
|
|
354
|
+
type: string
|
|
355
|
+
format: date-time
|
|
356
|
+
path:
|
|
357
|
+
type: string
|
|
358
|
+
message:
|
|
359
|
+
type: string
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
```yaml
|
|
363
|
+
# schema/recipes/catalog.api.yml
|
|
364
|
+
components:
|
|
365
|
+
schemas:
|
|
366
|
+
RecipesFindOneResponse:
|
|
367
|
+
allOf:
|
|
368
|
+
- $ref: ../shared/common.yml#/components/schemas/SuccessResponseBase
|
|
369
|
+
- type: object
|
|
370
|
+
required: [data]
|
|
371
|
+
properties:
|
|
372
|
+
data:
|
|
373
|
+
$ref: '#/components/schemas/RecipeResponse'
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
Use the same pattern for paginated responses:
|
|
377
|
+
|
|
378
|
+
```yaml
|
|
379
|
+
components:
|
|
380
|
+
schemas:
|
|
381
|
+
RecipesFindAllResponse:
|
|
382
|
+
allOf:
|
|
383
|
+
- $ref: ../shared/common.yml#/components/schemas/SuccessResponseBase
|
|
384
|
+
- type: object
|
|
385
|
+
required: [data]
|
|
386
|
+
properties:
|
|
387
|
+
data:
|
|
388
|
+
type: array
|
|
389
|
+
items:
|
|
390
|
+
$ref: '#/components/schemas/RecipeResponse'
|
|
391
|
+
meta:
|
|
392
|
+
$ref: ../shared/common.yml#/components/schemas/PaginationMeta
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
Kontract supports object `allOf` composition by flattening supported object refs
|
|
396
|
+
and inline object parts. Today this mainly reduces YAML duplication and contract
|
|
397
|
+
semantic drift. It may still emit concrete generated DTO/model properties per
|
|
398
|
+
response until the generator grows preserved composition or shared-output
|
|
399
|
+
emission.
|
|
400
|
+
|
|
401
|
+
## Schedules and repeated nested objects
|
|
402
|
+
|
|
403
|
+
If several features define the same day-of-week schedule or time-slot object,
|
|
404
|
+
share the object definitions instead of duplicating each day shape.
|
|
405
|
+
|
|
406
|
+
```yaml
|
|
407
|
+
# schema/shared/scheduling.yml
|
|
408
|
+
components:
|
|
409
|
+
schemas:
|
|
410
|
+
WeeklyTimeSlot:
|
|
411
|
+
type: object
|
|
412
|
+
required: [from, to]
|
|
413
|
+
properties:
|
|
414
|
+
from:
|
|
415
|
+
type: string
|
|
416
|
+
pattern: '^(0[6-9]|1\d|2[0-3]):[0-5]\d$'
|
|
417
|
+
to:
|
|
418
|
+
type: string
|
|
419
|
+
pattern: '^(0[6-9]|1\d|2[0-3]):[0-5]\d$'
|
|
420
|
+
WeeklySchedule:
|
|
421
|
+
type: object
|
|
422
|
+
properties:
|
|
423
|
+
sunday:
|
|
424
|
+
type: array
|
|
425
|
+
items:
|
|
426
|
+
$ref: '#/components/schemas/WeeklyTimeSlot'
|
|
427
|
+
monday:
|
|
428
|
+
type: array
|
|
429
|
+
items:
|
|
430
|
+
$ref: '#/components/schemas/WeeklyTimeSlot'
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
Then service-specific schedules can reference or compose those shared pieces.
|
|
434
|
+
|
|
435
|
+
## Review checklist
|
|
436
|
+
|
|
437
|
+
After writing or updating any `*.api.yml` or `*.async.yml` file, review
|
|
438
|
+
the spec before running generation. Actively look for these smells:
|
|
439
|
+
|
|
440
|
+
- the same `success/timestamp/path/message` fields repeated in many response
|
|
441
|
+
schemas;
|
|
442
|
+
- identical pagination `meta` objects or `PaginationMeta` definitions copied
|
|
443
|
+
across services instead of referencing a shared component;
|
|
444
|
+
- many `id` parameters declared inline instead of referencing a shared
|
|
445
|
+
parameter;
|
|
446
|
+
- inline UUID, date, date-time, URL, slug, or enum-like scalar constraints that
|
|
447
|
+
already exist in a shared component;
|
|
448
|
+
- identical regex patterns copied into multiple schemas;
|
|
449
|
+
- repeated day-of-week schedule structures;
|
|
450
|
+
- error response schemas or problem-details bodies copied instead of referencing
|
|
451
|
+
shared responses;
|
|
452
|
+
- event envelopes or async schema reply wrappers copied with the same product
|
|
453
|
+
meaning;
|
|
454
|
+
- generated diffs adding many near-identical DTO/model files after a small API
|
|
455
|
+
change.
|
|
456
|
+
|
|
457
|
+
When you see a smell, propose a shared component before generating.
|
|
458
|
+
|
|
459
|
+
## When not to share
|
|
460
|
+
|
|
461
|
+
Do not share only because two shapes currently look alike. Keep separate
|
|
462
|
+
components when:
|
|
463
|
+
|
|
464
|
+
- endpoints have different validation rules or lifecycle/backwards-compatibility
|
|
465
|
+
needs;
|
|
466
|
+
- a request DTO and response DTO intentionally differ, even if fields overlap;
|
|
467
|
+
- names would become vague (`CommonThing`, `BaseData`, `GenericConfig`);
|
|
468
|
+
- the reusable name would hide a real product term, such as using `Collection`
|
|
469
|
+
when collections are a product concept;
|
|
470
|
+
- sharing would force unrelated teams or product areas to coordinate every
|
|
471
|
+
future change.
|
|
472
|
+
|
|
473
|
+
A good shared component has a clear product meaning and a stable owner.
|
|
474
|
+
|
|
475
|
+
## Safe workflow
|
|
476
|
+
|
|
477
|
+
1. Keep new schemas local to the root or surface that owns them.
|
|
478
|
+
2. When reuse appears inside one capability, move the repeated shape into that
|
|
479
|
+
capability's `shared/` folder and replace duplicates with `$ref`.
|
|
480
|
+
3. When reuse appears across capabilities, promote the shape to
|
|
481
|
+
`schema/shared/` and update `$ref` links deliberately.
|
|
482
|
+
4. Use supported `allOf` object composition when a concrete response extends a
|
|
483
|
+
shared base.
|
|
484
|
+
5. Run `sumr kontract validate`.
|
|
485
|
+
6. Run a scoped generation first, for example:
|
|
486
|
+
|
|
487
|
+
```bash
|
|
488
|
+
sumr kontract generate --source api --target backend --specific recipes/catalog
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
7. Review both the spec diff and generated diff. If generated output changed in
|
|
492
|
+
unrelated product areas, stop and investigate before committing.
|