eva4j 1.0.14 → 1.0.16

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.
Files changed (139) hide show
  1. package/.agents/skills/skill-creator/LICENSE.txt +202 -0
  2. package/.agents/skills/skill-creator/SKILL.md +485 -0
  3. package/.agents/skills/skill-creator/agents/analyzer.md +274 -0
  4. package/.agents/skills/skill-creator/agents/comparator.md +202 -0
  5. package/.agents/skills/skill-creator/agents/grader.md +223 -0
  6. package/.agents/skills/skill-creator/assets/eval_review.html +146 -0
  7. package/.agents/skills/skill-creator/eval-viewer/generate_review.py +471 -0
  8. package/.agents/skills/skill-creator/eval-viewer/viewer.html +1325 -0
  9. package/.agents/skills/skill-creator/references/schemas.md +430 -0
  10. package/.agents/skills/skill-creator/scripts/__init__.py +0 -0
  11. package/.agents/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  12. package/.agents/skills/skill-creator/scripts/generate_report.py +326 -0
  13. package/.agents/skills/skill-creator/scripts/improve_description.py +247 -0
  14. package/.agents/skills/skill-creator/scripts/package_skill.py +136 -0
  15. package/.agents/skills/skill-creator/scripts/quick_validate.py +103 -0
  16. package/.agents/skills/skill-creator/scripts/run_eval.py +310 -0
  17. package/.agents/skills/skill-creator/scripts/run_loop.py +328 -0
  18. package/.agents/skills/skill-creator/scripts/utils.py +47 -0
  19. package/AGENTS.md +268 -6
  20. package/COMMAND_EVALUATION.md +15 -16
  21. package/DOMAIN_YAML_GUIDE.md +430 -14
  22. package/FUTURE_FEATURES.md +1627 -1168
  23. package/README.md +461 -13
  24. package/bin/eva4j.js +32 -14
  25. package/config/defaults.json +1 -0
  26. package/docs/commands/EVALUATE_SYSTEM.md +746 -261
  27. package/docs/commands/EXPORT_DIAGRAM.md +153 -0
  28. package/docs/commands/GENERATE_ENTITIES.md +599 -6
  29. package/docs/commands/INDEX.md +7 -0
  30. package/examples/domain-events.yaml +166 -20
  31. package/examples/domain-listeners.yaml +212 -0
  32. package/examples/domain-one-to-many.yaml +1 -0
  33. package/examples/domain-one-to-one.yaml +1 -0
  34. package/examples/domain-ports.yaml +414 -0
  35. package/examples/domain-soft-delete.yaml +47 -44
  36. package/examples/system/notification.yaml +147 -0
  37. package/examples/system/product.yaml +185 -0
  38. package/examples/system/system.yaml +112 -0
  39. package/examples/system-report.html +971 -0
  40. package/examples/system.yaml +46 -3
  41. package/package.json +2 -1
  42. package/src/agents/design-reviewer.agent.md +163 -0
  43. package/src/commands/build.js +714 -0
  44. package/src/commands/create.js +1 -0
  45. package/src/commands/detach.js +149 -108
  46. package/src/commands/evaluate-system.js +234 -8
  47. package/src/commands/export-diagram.js +522 -0
  48. package/src/commands/generate-entities.js +685 -66
  49. package/src/commands/generate-http-exchange.js +2 -0
  50. package/src/commands/generate-kafka-event.js +43 -10
  51. package/src/generators/base-generator.js +18 -6
  52. package/src/generators/postman-generator.js +188 -0
  53. package/src/generators/shared-generator.js +12 -2
  54. package/src/skills/build-system-yaml/SKILL.md +323 -0
  55. package/src/skills/build-system-yaml/references/domain-yaml-spec.md +410 -0
  56. package/src/skills/build-system-yaml/references/module-spec.md +367 -0
  57. package/src/skills/build-system-yaml/references/system-yaml-spec.md +178 -0
  58. package/src/utils/config-manager.js +54 -0
  59. package/src/utils/context-builder.js +1 -0
  60. package/src/utils/domain-diagram.js +192 -0
  61. package/src/utils/domain-validator.js +1020 -0
  62. package/src/utils/fake-data.js +376 -0
  63. package/src/utils/system-validator.js +319 -199
  64. package/src/utils/yaml-to-entity.js +272 -7
  65. package/templates/aggregate/AggregateMapper.java.ejs +3 -2
  66. package/templates/aggregate/AggregateRepository.java.ejs +3 -2
  67. package/templates/aggregate/AggregateRepositoryImpl.java.ejs +6 -5
  68. package/templates/aggregate/AggregateRoot.java.ejs +60 -2
  69. package/templates/aggregate/DomainEventHandler.java.ejs +4 -1
  70. package/templates/aggregate/DomainEventRecord.java.ejs +24 -8
  71. package/templates/aggregate/DomainEventSnapshot.java.ejs +46 -0
  72. package/templates/aggregate/JpaAggregateRoot.java.ejs +6 -0
  73. package/templates/base/docker/Dockerfile.ejs +21 -0
  74. package/templates/base/gradle/build.gradle.ejs +3 -2
  75. package/templates/base/root/AGENTS.md.ejs +306 -45
  76. package/templates/crud/ApplicationMapper.java.ejs +4 -0
  77. package/templates/crud/Controller.java.ejs +4 -4
  78. package/templates/crud/CreateCommand.java.ejs +4 -0
  79. package/templates/crud/CreateCommandHandler.java.ejs +3 -6
  80. package/templates/crud/CreateItemDto.java.ejs +4 -0
  81. package/templates/crud/CreateValueObjectDto.java.ejs +4 -0
  82. package/templates/crud/DeleteCommandHandler.java.ejs +12 -6
  83. package/templates/crud/EndpointsController.java.ejs +6 -6
  84. package/templates/crud/FindByQuery.java.ejs +1 -1
  85. package/templates/crud/FindByQueryHandler.java.ejs +3 -9
  86. package/templates/crud/GetQueryHandler.java.ejs +2 -5
  87. package/templates/crud/ListQuery.java.ejs +1 -1
  88. package/templates/crud/ListQueryHandler.java.ejs +8 -14
  89. package/templates/crud/ScaffoldCommandHandler.java.ejs +2 -4
  90. package/templates/crud/ScaffoldQuery.java.ejs +3 -2
  91. package/templates/crud/ScaffoldQueryHandler.java.ejs +5 -6
  92. package/templates/crud/SubEntityAddCommand.java.ejs +4 -0
  93. package/templates/crud/SubEntityAddCommandHandler.java.ejs +2 -6
  94. package/templates/crud/SubEntityRemoveCommandHandler.java.ejs +2 -7
  95. package/templates/crud/TransitionCommandHandler.java.ejs +2 -6
  96. package/templates/crud/UpdateCommand.java.ejs +4 -0
  97. package/templates/crud/UpdateCommandHandler.java.ejs +2 -5
  98. package/templates/evaluate/report.html.ejs +394 -2
  99. package/templates/kafka-event/DomainEventHandlerMethod.ejs +3 -1
  100. package/templates/kafka-event/Event.java.ejs +9 -0
  101. package/templates/kafka-listener/KafkaController.java.ejs +1 -1
  102. package/templates/kafka-listener/KafkaListenerClass.java.ejs +1 -1
  103. package/templates/kafka-listener/ListenerClass.java.ejs +65 -0
  104. package/templates/kafka-listener/ListenerCommand.java.ejs +31 -0
  105. package/templates/kafka-listener/ListenerCommandHandler.java.ejs +25 -0
  106. package/templates/kafka-listener/ListenerIntegrationEvent.java.ejs +37 -0
  107. package/templates/kafka-listener/ListenerMethod.java.ejs +1 -1
  108. package/templates/kafka-listener/ListenerNestedType.java.ejs +28 -0
  109. package/templates/mock/MockEvent.java.ejs +10 -0
  110. package/templates/mock/MockMessageBrokerImpl.java.ejs +35 -0
  111. package/templates/mock/MockMessageBrokerImplMethod.java.ejs +6 -0
  112. package/templates/mock/SpringEventListener.java.ejs +61 -0
  113. package/templates/ports/PortDomainModel.java.ejs +35 -0
  114. package/templates/ports/PortFeignAdapter.java.ejs +67 -0
  115. package/templates/ports/PortFeignClient.java.ejs +45 -0
  116. package/templates/ports/PortFeignConfig.java.ejs +24 -0
  117. package/templates/ports/PortInterface.java.ejs +45 -0
  118. package/templates/ports/PortNestedType.java.ejs +28 -0
  119. package/templates/ports/PortRequestDto.java.ejs +30 -0
  120. package/templates/ports/PortResponseDto.java.ejs +28 -0
  121. package/templates/postman/Collection.json.ejs +1 -1
  122. package/templates/postman/UnifiedCollection.json.ejs +185 -0
  123. package/templates/shared/annotations/LogAfter.java.ejs +2 -0
  124. package/templates/shared/annotations/LogBefore.java.ejs +2 -0
  125. package/templates/shared/annotations/LogExceptions.java.ejs +2 -0
  126. package/templates/shared/annotations/LogLevel.java.ejs +8 -0
  127. package/templates/shared/annotations/LogTimer.java.ejs +1 -0
  128. package/templates/shared/annotations/Loggable.java.ejs +17 -0
  129. package/templates/shared/configurations/eventPublicationConfig/EventPublicationSchemaConfig.java.ejs +109 -0
  130. package/templates/shared/configurations/loggerConfig/HandlerLogs.java.ejs +120 -32
  131. package/templates/shared/errorMessage/ErrorResponse.java.ejs +23 -0
  132. package/templates/shared/handlerException/HandlerExceptions.java.ejs +99 -79
  133. package/src/commands/generate-system.js +0 -243
  134. package/templates/base/root/skill-build-domain-yaml-references-generate-entities.md.ejs +0 -1103
  135. package/templates/base/root/skill-build-domain-yaml.ejs +0 -292
  136. package/templates/base/root/skill-build-system-yaml.ejs +0 -252
  137. package/templates/shared/errorMessage/ErrorMessage.java.ejs +0 -5
  138. package/templates/shared/errorMessage/FullErrorMessage.java.ejs +0 -9
  139. package/templates/shared/errorMessage/ShortErrorMessage.java.ejs +0 -6
@@ -0,0 +1,367 @@
1
+ # Especificaciones de documentación del sistema
2
+
3
+ Referencia para generar `system/system.md` y `system/{module}.md`.
4
+
5
+ ---
6
+
7
+ ## Parte 1 — system.md (especificación narrativa global)
8
+
9
+ ### Ubicación: `system/system.md`
10
+
11
+ Especificación técnica narrativa del sistema completo. Una sección `##` por cada módulo con subsecciones detalladas.
12
+
13
+ ### Estructura obligatoria
14
+
15
+ ```markdown
16
+ # system.md — Technical Specification
17
+
18
+ ## {module-name}
19
+
20
+ ### Module Role
21
+ [3–5 detailed paragraphs]
22
+ - What business problem it solves and what entities/concepts it manages
23
+ - Its exclusive responsibilities (bounded context boundaries)
24
+ - What is NOT this module's responsibility
25
+ - How it collaborates with other modules
26
+ - Business invariants it protects
27
+
28
+ ### Use Cases
29
+ [One `####` section per useCase in exposes: and consumers[].useCase]
30
+
31
+ #### {UseCaseName}
32
+ **What it does:** [detailed business logic description]
33
+ **Preconditions:** [required state, entities that must exist]
34
+ **Postconditions:** [system state after successful execution]
35
+ **Validations and errors:** [exception conditions and error types]
36
+ **Events emitted:** [DomainEvent name and trigger condition, or "none"]
37
+
38
+ ### Exposed Endpoints
39
+ [One `####` per endpoint in exposes:]
40
+
41
+ #### {METHOD} {/path}
42
+ **Purpose:** [endpoint description and usage context]
43
+ **Path params / Query params:** [each parameter described]
44
+ **Request body:** [expected fields, types, validation constraints]
45
+ **Response:** [returned fields and their business meaning]
46
+ **Errors:** [HTTP status codes and when they occur]
47
+
48
+ ### Emitted Events
49
+ [Only if module is producer in integrations.async]
50
+
51
+ #### {EventName}
52
+ **When:** [exact business condition that fires the event]
53
+ **Payload:** [event fields with description]
54
+ **Consumers and their actions:**
55
+ - `{module}` → `{useCase}`: [what the consumer does]
56
+
57
+ ### Ports (outbound sync calls)
58
+ [Only if module is caller in integrations.sync]
59
+
60
+ #### {PortName} → {target-module}
61
+ **When called:** [in which use case and under what condition]
62
+ **Endpoints used:** [METHOD /path list]
63
+ **Data obtained and how it's used:** [detailed description]
64
+ ```
65
+
66
+ ### Reglas del system.md
67
+
68
+ - **Ser SUMAMENTE específico**: estados de entidades, validaciones concretas, campos relevantes. Nunca "gestiona los datos".
69
+ - **Flujos de extremo a extremo**: si `ConfirmReservation` es disparado por `PaymentApprovedEvent`, explicarlo en ambas secciones.
70
+ - **Incluir useCases de consumers** como casos de uso del módulo consumidor.
71
+ - **Referenciar módulos por nombre**.
72
+ - **Máquinas de estado** cuando hay ciclos de vida.
73
+ - Omitir secciones no aplicables.
74
+
75
+ ---
76
+
77
+ ## Parte 2 — {module}.md (especificación técnica por módulo)
78
+
79
+ ### Ubicación: `system/{module-name}.md`
80
+
81
+ Especificación completa e independiente para cada módulo. Un desarrollador puede leerla sin conocer el sistema completo.
82
+
83
+ ### Estructura obligatoria
84
+
85
+ ```markdown
86
+ # {module-name} — Technical Specification
87
+
88
+ ## Module Role
89
+ [3–5 VERY detailed paragraphs]
90
+ - What business problem it solves and what entities/concepts it manages
91
+ - Exclusive responsibilities (bounded context boundaries)
92
+ - What is NOT this module's responsibility
93
+ - How it collaborates with other modules
94
+
95
+ ## Invariants
96
+
97
+ > Invariants are conditions that must be **always true** within this bounded context.
98
+ > Violating an invariant is a **critical business error** that must throw an exception.
99
+
100
+ | ID | Invariant | Violation consequence |
101
+ |----|-----------|----------------------|
102
+ | INV-01 | [condition] | [exception / what it prevents] |
103
+ | INV-02 | ... | ... |
104
+
105
+ > Each use case must verify relevant invariants **before** persisting changes.
106
+
107
+ ## State Machine
108
+ [ONLY if the module manages entity lifecycle — omit otherwise]
109
+
110
+ ```mermaid
111
+ stateDiagram-v2
112
+ [*] --> INITIAL_STATE
113
+ INITIAL_STATE --> NEXT_STATE : event / action
114
+ NEXT_STATE --> FINAL_STATE : event / action
115
+ FINAL_STATE --> [*]
116
+ ```
117
+
118
+ > Transition restrictions: [explain forbidden transitions — these are implicit state machine invariants]
119
+
120
+ ## Interaction Diagram
121
+
122
+ > Shows the complete flow: what arrives at the module (HTTP or async event),
123
+ > which use case executes, and what event is emitted.
124
+
125
+ ```mermaid
126
+ flowchart TD
127
+ subgraph HTTP["REST Endpoints"]
128
+ EP1["METHOD /path"]
129
+ end
130
+
131
+ subgraph ASYNC_IN["Incoming Events"]
132
+ EI1["📥 EventName"]
133
+ end
134
+
135
+ subgraph USE_CASES["Use Cases"]
136
+ UC1["UseCaseName"]
137
+ end
138
+
139
+ subgraph EVENTS_OUT["Emitted Events"]
140
+ EO1["📤 EmittedEventName"]
141
+ end
142
+
143
+ EP1 --> UC1
144
+ EI1 --> UC2["AnotherUseCase"]
145
+ UC1 --> EO1
146
+ UC2 -->|"no event"| FIN([end])
147
+ ```
148
+
149
+ > Node conventions:
150
+ > - HTTP endpoints → rectangles with method and path
151
+ > - Incoming events → `📥` prefix
152
+ > - Use cases → rectangles with handler name
153
+ > - Emitted events → `📤` prefix
154
+ > - No output event → connect to FIN([end])
155
+ > - Omit ASYNC_IN if no events consumed; omit EVENTS_OUT if no events produced
156
+
157
+ ## Sequence Diagram
158
+ [Chronological interactions between actors and components for main flows.
159
+ One diagram per complex flow or flow with significant branching.]
160
+
161
+ ```mermaid
162
+ sequenceDiagram
163
+ actor Client as Client
164
+ participant API as REST API
165
+ participant Handler as CommandHandler
166
+ participant Domain as Domain Entity
167
+ participant Repo as Repository
168
+ participant Broker as Message Broker
169
+ participant ExtSvc as External Service
170
+
171
+ Client->>API: POST /resource {payload}
172
+ API->>Handler: CreateResourceCommand
173
+ Handler->>Repo: findById(id)
174
+ Repo-->>Handler: entity (or 404)
175
+ Handler->>Domain: businessMethod()
176
+ Domain-->>Handler: updated entity
177
+ Handler->>Repo: save(entity)
178
+ Handler->>Broker: publish(ResourceCreatedEvent)
179
+ Broker-->>Handler: ack
180
+ Handler-->>API: resourceId
181
+ API-->>Client: 201 Created {resourceId}
182
+ ```
183
+
184
+ > Actor conventions:
185
+ > - `actor Client` → human users or external systems initiating the flow
186
+ > - `participant API` → module REST controller
187
+ > - `participant Handler` → CommandHandler / QueryHandler
188
+ > - `participant Domain` → domain entity (include when business logic is relevant)
189
+ > - `participant Repo` → repository abstraction
190
+ > - `participant Broker` → message broker — omit if no events
191
+ > - `participant ExtSvc` → external service via ports — use real port name
192
+ > - Sync responses: `-->>` (dotted line with arrow)
193
+ > - Async messages: `--)` (dotted line without immediate return)
194
+ > - One diagram per main flow; skip trivial read-only flows
195
+
196
+ ## Use Cases
197
+ [One `###` per useCase in exposes: and consumers[].useCase]
198
+
199
+ ### {UseCaseName}
200
+ **Type:** `HTTP` | `Incoming Event` (specify which)
201
+ **What it does:** [detailed business logic description]
202
+ **Preconditions:** [valid states, entities that must exist]
203
+ **Postconditions:** [system state after successful execution]
204
+ **Invariants verified:** [ID list — e.g., INV-01, INV-03]
205
+ **Validations and errors:** [exception conditions, error type, HTTP status]
206
+ **Events emitted:** [DomainEvent name and condition, or "none"]
207
+
208
+ **Flow diagram:**
209
+ ```mermaid
210
+ flowchart TD
211
+ IN["🔵 Trigger: METHOD /path · or EventName"] --> UC["⚙️ {UseCaseName}"]
212
+ UC --> INV{"Check invariants"}
213
+ INV -->|"violated"| ERR[/"Exception / Error Response"/]
214
+ INV -->|"passed"| BIZ["Execute business logic"]
215
+ BIZ --> SAVE["Persist changes"]
216
+ SAVE -->|"if applicable"| EV["📤 DomainEvent emitted"]
217
+ SAVE -->|"no event"| FIN([end])
218
+ ```
219
+ > Adapt to real flow: include relevant business nodes and branching.
220
+
221
+ ## Exposed Endpoints
222
+ [Only if module has exposes: entries]
223
+
224
+ ### {METHOD} {/path}
225
+ **Use case:** `{UseCase}`
226
+ **Purpose:** [description]
227
+ **Path params / Query params:** [each parameter]
228
+ **Request body:** [fields, types, validations]
229
+ **Response:** [fields and business meaning]
230
+ **Errors:** [HTTP status codes and conditions]
231
+
232
+ ## Emitted Events
233
+ [Only if module is producer in integrations.async]
234
+
235
+ ### {EventName}
236
+ **When:** [exact business condition]
237
+ **Payload:** [fields with description]
238
+ **Consumers and their actions:**
239
+ - `{module}` → `{useCase}`: [what consumer does]
240
+
241
+ ## Ports (outbound sync calls)
242
+ [Only if module is caller in integrations.sync]
243
+
244
+ ### {PortName} → {target-module}
245
+ **When called:** [in which use case and condition]
246
+ **Endpoints used:** [METHOD /path list]
247
+ **Data obtained and how it's used:** [detailed description]
248
+ ```
249
+
250
+ ---
251
+
252
+ ### Reglas de los archivos de módulo
253
+
254
+ - **Todo en inglés**: títulos, secciones, descripciones, invariantes, use cases.
255
+ - **INVARIANTES obligatorias**: al menos 2–3 por módulo. Analizar unicidad, estados válidos, rangos, precondiciones de transición.
256
+ - **Diagrama de interacciones obligatorio**: todos los endpoints y eventos entrantes. Sin entradas → nodo `[[passive module]]`.
257
+ - **Diagrama de secuencia obligatorio**: al menos un `sequenceDiagram` cubriendo el happy path. Diagramas adicionales para bifurcaciones (error, compensación). Modelar todos los actores reales.
258
+ - **Diagrama de flujo por caso de uso**: `flowchart TD` dentro de cada `### {UseCase}` con trigger, invariantes, lógica y eventos.
259
+ - **Máquina de estados condicional**: solo si hay entidades con ciclo de vida. Restricciones de transición son invariantes implícitas.
260
+ - **Referenciar invariantes** en cada caso de uso (INV-01, INV-02...).
261
+ - **No duplicar** system.md — el `.md` del módulo es la especificación completa.
262
+ - Archivos en `system/{module-name}.md`.
263
+
264
+ ---
265
+
266
+ ### Ejemplo condensado — `system/payments.md`
267
+
268
+ ```markdown
269
+ # payments — Technical Specification
270
+
271
+ ## Module Role
272
+
273
+ The `payments` module is solely responsible for the lifecycle of payments linked
274
+ to reservations. It manages communication with the external payment gateway,
275
+ records each transaction's state, and decides when to emit events that trigger
276
+ changes in other modules. It has no knowledge of reservation logic or notifications.
277
+
278
+ Confirming reservations and sending notifications are NOT this module's responsibility;
279
+ those flows are initiated by events that payments emits.
280
+
281
+ ## Invariants
282
+
283
+ | ID | Invariant | Violation consequence |
284
+ |----|-----------|----------------------|
285
+ | INV-01 | Only one active payment (PENDING or APPROVED) per reservation at any time | Throws `409 Conflict` — prevents double charging |
286
+ | INV-02 | A CANCELLED or FAILED payment cannot transition to APPROVED | Throws `InvalidStateTransitionException` |
287
+ | INV-03 | Payment amount must be > 0 | Throws `400 Bad Request` |
288
+ | INV-04 | A payment can only be refunded if in APPROVED state | Throws `409 Conflict` |
289
+
290
+ ## State Machine
291
+
292
+ ```mermaid
293
+ stateDiagram-v2
294
+ [*] --> PENDING : CreatePayment
295
+ PENDING --> APPROVED : ApprovePayment (gateway confirms)
296
+ PENDING --> FAILED : FailPayment (gateway rejects)
297
+ APPROVED --> REFUNDED : RefundPayment
298
+ FAILED --> [*]
299
+ REFUNDED --> [*]
300
+ ```
301
+
302
+ > Restrictions: CANCELLED and FAILED are terminal states (INV-02). Only APPROVED can transition to REFUNDED (INV-04).
303
+
304
+ ## Interaction Diagram
305
+
306
+ ```mermaid
307
+ flowchart TD
308
+ subgraph HTTP["REST Endpoints"]
309
+ EP1["POST /payments"]
310
+ EP2["GET /payments/{id}"]
311
+ EP3["POST /payments/{id}/refund"]
312
+ end
313
+
314
+ subgraph ASYNC_IN["Incoming Events"]
315
+ EI1["📥 ReservationCreatedEvent"]
316
+ end
317
+
318
+ subgraph USE_CASES["Use Cases"]
319
+ UC1["CreatePayment"]
320
+ UC2["GetPayment"]
321
+ UC3["RefundPayment"]
322
+ UC4["HandleReservationCreated"]
323
+ end
324
+
325
+ subgraph EVENTS_OUT["Emitted Events"]
326
+ EO1["📤 PaymentApprovedEvent"]
327
+ EO2["📤 PaymentFailedEvent"]
328
+ EO3["📤 PaymentRefundedEvent"]
329
+ end
330
+
331
+ EP1 --> UC1
332
+ EP2 --> UC2
333
+ EP3 --> UC3
334
+ EI1 --> UC4
335
+ UC4 --> UC1
336
+ UC1 --> EO1
337
+ UC1 --> EO2
338
+ UC3 --> EO3
339
+ UC2 -->|"read-only"| FIN([end])
340
+ ```
341
+
342
+ ## Use Cases
343
+
344
+ ### HandleReservationCreated
345
+ **Type:** Incoming Event (`ReservationCreatedEvent`)
346
+ **What it does:** Extracts `reservationId` and `totalAmount` from payload and triggers
347
+ `CreatePayment` internally to start charging automatically.
348
+ **Preconditions:** Payload contains valid `reservationId` and `totalAmount > 0`.
349
+ **Postconditions:** Payment created in PENDING state.
350
+ **Invariants verified:** INV-01, INV-03
351
+ **Validations and errors:** If INV-01 violated (active payment exists), discard event
352
+ and log warning. If `totalAmount ≤ 0`, throw `400`.
353
+ **Events emitted:** none directly; approval arrives asynchronously.
354
+
355
+ **Flow diagram:**
356
+ ```mermaid
357
+ flowchart TD
358
+ IN["📥 ReservationCreatedEvent"] --> UC["⚙️ HandleReservationCreated"]
359
+ UC --> INV1{"INV-01: active payment?"}
360
+ INV1 -->|"Yes"| WARN[/"warn — discard event"/]
361
+ INV1 -->|"No"| INV2{"INV-03: amount > 0?"}
362
+ INV2 -->|"No"| ERR[/"400 Bad Request"/]
363
+ INV2 -->|"Yes"| CREATE["Create Payment PENDING"]
364
+ CREATE --> SAVE["Persist Payment"]
365
+ SAVE --> FIN([end — approval arrives async])
366
+ ```
367
+ ```
@@ -0,0 +1,178 @@
1
+ # system.yaml — Especificación completa
2
+
3
+ Referencia técnica para construir y validar el archivo `system/system.yaml`.
4
+
5
+ ---
6
+
7
+ ## Estructura completa
8
+
9
+ ```yaml
10
+ system:
11
+ name: project-name # kebab-case
12
+ groupId: com.example
13
+ javaVersion: 21
14
+ springBootVersion: 3.5.5
15
+ database: postgresql # h2 | postgresql | mysql
16
+
17
+ messaging: # Omitir sección completa si no hay mensajería
18
+ enabled: true
19
+ broker: kafka # kafka | rabbitmq | sns-sqs (solo kafka soportado hoy)
20
+ kafka:
21
+ bootstrapServers: localhost:9092
22
+ defaultGroupId: project-name
23
+ topicPrefix: project-name # opcional — prefixa todos los topics
24
+
25
+ modules:
26
+ - name: orders # plural, kebab-case
27
+ description: "Order lifecycle management"
28
+ exposes:
29
+ - method: GET # GET | POST | PUT | PATCH | DELETE
30
+ path: /orders/{id}
31
+ useCase: GetOrder # PascalCase — alimenta endpoints: en domain.yaml
32
+ description: "Get order by ID"
33
+ - method: GET
34
+ path: /orders
35
+ useCase: FindAllOrders
36
+ description: "List orders with filters and pagination"
37
+ - method: POST
38
+ path: /orders
39
+ useCase: CreateOrder
40
+ description: "Create a new order"
41
+ - method: PUT
42
+ path: /orders/{id}/confirm
43
+ useCase: ConfirmOrder
44
+ description: "Confirm a pending order"
45
+
46
+ - name: notifications
47
+ description: "Notification delivery service"
48
+ # Sin endpoints REST — solo consume eventos
49
+
50
+ integrations:
51
+ async:
52
+ - event: OrderPlacedEvent # PascalCase, tiempo pasado, sufijo Event
53
+ producer: orders
54
+ topic: ORDER_PLACED # SCREAMING_SNAKE_CASE
55
+ consumers:
56
+ - module: payments
57
+ useCase: HandleOrderPlaced # acción que payments ejecuta
58
+ - module: notifications
59
+ useCase: NotifyOrderPlaced # acción que notifications ejecuta
60
+
61
+ sync:
62
+ - caller: orders # módulo que hace la llamada
63
+ calls: customers # módulo destino
64
+ port: OrderCustomerService # PascalCase + Service — prefijado con caller
65
+ using:
66
+ - GET /customers/{id} # debe existir en exposes: de 'customers'
67
+ ```
68
+
69
+ ---
70
+
71
+ ## Convenciones de nombres
72
+
73
+ | Elemento | Convención | Ejemplo válido | Ejemplo inválido |
74
+ |---|---|---|---|
75
+ | Módulos | plural, kebab-case | `orders`, `order-items` | `Order`, `order_items` |
76
+ | Eventos | PascalCase + pasado + sufijo `Event` | `OrderPlacedEvent` | `PlaceOrderEvent` |
77
+ | Topics Kafka | SCREAMING_SNAKE_CASE — **sin `topicPrefix`** | `ORDER_PLACED` | `test-eva.ORDER_PLACED` |
78
+ | Port names | PascalCase + sufijo `Service` — **único por módulo** | `OrderCustomerService` | `CustomerService` (compartido) |
79
+ | useCases | PascalCase, verbo + sustantivo | `CreateOrder`, `FindAllOrders` | `createOrder`, `orders` |
80
+ | `consumers[].useCase` | PascalCase, verbo + sustantivo | `HandleOrderPlaced` | `orderPlaced` |
81
+
82
+ ---
83
+
84
+ ## Restricciones estructurales
85
+
86
+ - ❌ **Sin dependencias circulares síncronas** — si `A` llama a `B`, `B` no puede llamar a `A`
87
+ - ❌ **Sin campos de dominio** — entidades, campos, enums → van en `domain.yaml`
88
+ - ❌ **Sin nombres de `port` genéricos compartidos** — si `orders` y `deliveries` llaman a `customers`, usar `OrderCustomerService` y `DeliveryCustomerService`; **nunca** `CustomerService` en ambos (causa `ConflictingBeanDefinitionException`)
89
+ - ✅ Cada módulo tiene **una sola responsabilidad**
90
+ - ✅ `calls.using:` solo referencia endpoints declarados en `exposes:` del módulo destino
91
+ - ✅ `consumers[].module` debe existir en `modules:`
92
+ - ✅ Módulos pasivos (notificaciones, auditoría) son **consumidores**, nunca `caller`
93
+ - ℹ️ Varios módulos pueden consumir el mismo evento sin riesgo de colisión de beans
94
+
95
+ ---
96
+
97
+ ## useCases — patrones de nombres
98
+
99
+ ### Verbos por tipo de operación
100
+
101
+ | Tipo de operación | Verbos recomendados | Ejemplo |
102
+ |---|---|---|
103
+ | Crear recurso | `Create` | `CreateOrder` |
104
+ | Actualizar | `Update` | `UpdateOrder` |
105
+ | Eliminar | `Delete` | `DeleteOrder` |
106
+ | Obtener por ID | `Get` | `GetOrder` |
107
+ | Listar con paginación | `FindAll` | `FindAllOrders` |
108
+ | Transición de estado | `Confirm`, `Cancel`, `Approve`, `Reject`, `Activate`, `Close`, `Complete`, `Submit`, `Publish` | `ConfirmOrder`, `CancelPayment` |
109
+ | Acción puntual | `Send`, `Process`, `Calculate`, `Generate`, `Assign`, `Transfer`, `Notify` | `SendNotification`, `ProcessPayment` |
110
+ | Búsqueda | `Search`, `Find`, `Lookup` | `FindOrdersByCustomer` |
111
+
112
+ ### CRUD vs negocio — regla de generación
113
+
114
+ eva4j distingue dos categorías:
115
+
116
+ **CRUD estándar** — genera implementación completa del handler:
117
+
118
+ | Patrón | HTTP | Implementación |
119
+ |---|---|---|
120
+ | `Create{Aggregate}` | POST `/resource` | Handler completo |
121
+ | `Update{Aggregate}` | PUT `/resource/{id}` | Handler completo |
122
+ | `Delete{Aggregate}` | DELETE `/resource/{id}` | Handler completo |
123
+ | `Get{Aggregate}` | GET `/resource/{id}` | Handler completo |
124
+ | `FindAll{PluralAggregate}` | GET `/resource` | Handler completo |
125
+
126
+ **Negocio** — genera scaffold con `throw new UnsupportedOperationException(...)`:
127
+
128
+ ```java
129
+ public class ConfirmOrderCommandHandler implements CommandHandler<ConfirmOrderCommand, Void> {
130
+ @Override
131
+ public Void handle(ConfirmOrderCommand command) {
132
+ throw new UnsupportedOperationException("ConfirmOrderCommandHandler not implemented yet");
133
+ }
134
+ }
135
+ ```
136
+
137
+ ### useCase en consumers
138
+
139
+ Cada `consumers[]` **debe** declarar `useCase`: la acción que el consumidor ejecuta al recibir el evento.
140
+
141
+ ```yaml
142
+ consumers:
143
+ - module: payments
144
+ useCase: HandleReservationCreated # payments inicia cobro
145
+ - module: notifications
146
+ useCase: NotifyReservationCreated # envía email de confirmación
147
+ ```
148
+
149
+ **Reglas:**
150
+ - PascalCase, `Verbo + Sustantivo`
151
+ - Describe la acción del consumidor, no repite el evento
152
+ - Se mapea a `listeners[].useCase` del `domain.yaml` del consumidor
153
+ - Verbos típicos: `Handle`, `Process`, `Confirm`, `Cancel`, `Notify`, `Accumulate`, `Release`, `Update`
154
+
155
+ ---
156
+
157
+ ## Mensajería
158
+
159
+ - Solo `kafka` está implementado; `rabbitmq` y `sns-sqs` generan warning
160
+ - Los **campos** de eventos NO van en `system.yaml` → se declaran en `domain.yaml → events[].fields`
161
+ - ❌ `listeners[].topic` NUNCA lleva el `topicPrefix` — usar solo nombre base SCREAMING_SNAKE_CASE
162
+
163
+ ---
164
+
165
+ ## Checklist de validación completa
166
+
167
+ - [ ] Módulos en plural kebab-case
168
+ - [ ] Eventos en tiempo pasado con sufijo `Event`
169
+ - [ ] Topics en SCREAMING_SNAKE_CASE sin topicPrefix
170
+ - [ ] Sin dependencias circulares síncronas
171
+ - [ ] Todos los `consumers[].module` existen en `modules:`
172
+ - [ ] Todos los `consumers[].useCase` presentes y en PascalCase
173
+ - [ ] Todos los `calls.using:` existen en `exposes:` del destino
174
+ - [ ] Módulos pasivos no son `caller`
175
+ - [ ] `useCases` en PascalCase
176
+ - [ ] Port names únicos por módulo (prefijados con bounded context del caller)
177
+ - [ ] Todo el contenido en inglés
178
+ - [ ] Archivo guardado en `system/system.yaml`
@@ -151,6 +151,60 @@ class ConfigManager {
151
151
 
152
152
  return config;
153
153
  }
154
+
155
+ /**
156
+ * Persist mock backup data into .eva4j.json so it survives process restarts.
157
+ * @param {Object} backups - Map of { key: { path, content } }
158
+ */
159
+ async saveMockBackup(backups, opts = {}) {
160
+ const config = await this.loadProjectConfig();
161
+ if (!config) throw new Error('Project configuration not found.');
162
+
163
+ config._mockBackup = {};
164
+ for (const [key, { path: filePath, content }] of Object.entries(backups)) {
165
+ config._mockBackup[key] = { path: filePath, content };
166
+ }
167
+ config._mockActive = true;
168
+ if (opts.onlyBroker) {
169
+ config._mockOnlyBroker = true;
170
+ }
171
+ config.updatedAt = new Date().toISOString();
172
+ await fs.writeJson(this.configFile, config, { spaces: 2 });
173
+ }
174
+
175
+ /**
176
+ * Read and clear mock backup from .eva4j.json.
177
+ * @returns {Object|null} backups map or null if no mock backup stored
178
+ */
179
+ async popMockBackup() {
180
+ const config = await this.loadProjectConfig();
181
+ if (!config || !config._mockBackup) return null;
182
+
183
+ const backups = config._mockBackup;
184
+ delete config._mockBackup;
185
+ delete config._mockActive;
186
+ delete config._mockOnlyBroker;
187
+ config.updatedAt = new Date().toISOString();
188
+ await fs.writeJson(this.configFile, config, { spaces: 2 });
189
+ return backups;
190
+ }
191
+
192
+ /**
193
+ * Returns true if a mock backup is currently stored in .eva4j.json.
194
+ */
195
+ async hasMockBackup() {
196
+ const config = await this.loadProjectConfig();
197
+ return !!(config && config._mockActive);
198
+ }
199
+
200
+ /**
201
+ * Returns true if the active mock was started with --only-broker
202
+ * (i.e. only the broker was swapped, DB config was kept unchanged).
203
+ */
204
+ async hasMockOnlyBroker() {
205
+ const config = await this.loadProjectConfig();
206
+ return !!(config && config._mockOnlyBroker);
207
+ }
154
208
  }
155
209
 
156
210
  module.exports = ConfigManager;
@@ -30,6 +30,7 @@ function buildBaseContext(answers) {
30
30
  dependencyManagementVersion: defaults.dependencyManagementVersion,
31
31
  springModulithVersion: defaults.springModulithVersion,
32
32
  springCloudVersion: defaults.springCloudVersion,
33
+ springdocVersion: defaults.springdocVersion,
33
34
  gradleVersion: defaults.gradleVersion,
34
35
  dependencies: answers.dependencies || [],
35
36
  author: answers.author || 'Generated by eva4j',