eva4j 1.0.16 → 1.0.18
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/AGENTS.md +220 -5
- package/DOMAIN_YAML_GUIDE.md +188 -3
- package/FUTURE_FEATURES.md +33 -52
- package/QUICK_REFERENCE.md +8 -4
- package/bin/eva4j.js +70 -2
- package/config/defaults.json +1 -0
- package/docs/CAMUNDA_DMN_GUIDE.md +1380 -0
- package/docs/KAFKA_PRODUCTION_CONFIG.md +441 -0
- package/docs/RABBITMQ_PRODUCTION_CONFIG.md +227 -0
- package/docs/commands/ADD_RABBITMQ_CLIENT.md +192 -0
- package/docs/commands/EVALUATE_SYSTEM.md +290 -10
- package/docs/commands/GENERATE_RABBITMQ_EVENT.md +341 -0
- package/docs/commands/GENERATE_RABBITMQ_LISTENER.md +595 -0
- package/docs/commands/GENERATE_TEMPORAL_FLOW.md +52 -12
- package/docs/commands/INDEX.md +27 -3
- package/docs/prototype/TEMPORAL_COMMUNICATION_PATTERNS.md +731 -0
- package/docs/prototype/TEMPORAL_DESIGN_METHODOLOGY.md +740 -0
- package/docs/prototype/system/RISKS.md +277 -0
- package/docs/prototype/system/customers.yaml +133 -0
- package/docs/prototype/system/inventory.yaml +109 -0
- package/docs/prototype/system/notifications.yaml +131 -0
- package/docs/prototype/system/orders.yaml +241 -0
- package/docs/prototype/system/payments.yaml +256 -0
- package/docs/prototype/system/products.yaml +168 -0
- package/docs/prototype/system/system.yaml +269 -0
- package/examples/domain-endpoints-multi-aggregate.yaml +140 -0
- package/examples/domain-events.yaml +26 -0
- package/examples/domain-read-models.yaml +113 -0
- package/examples/system/customer.yaml +89 -0
- package/examples/system/orders.yaml +119 -0
- package/examples/system/product.yaml +27 -0
- package/examples/system/system.yaml +80 -0
- package/package.json +1 -1
- package/read-model-spec.md +664 -0
- package/src/agents/design-gap-analyst-temporal.agent.md +452 -0
- package/src/agents/design-gap-analyst.agent.md +383 -0
- package/src/agents/design-reviewer-temporal.agent.md +412 -0
- package/src/agents/design-reviewer.agent.md +34 -5
- package/src/agents/implement-use-cases.prompt.md +179 -0
- package/src/agents/ux-gap-analyst.agent.md +412 -0
- package/src/commands/add-rabbitmq-client.js +261 -0
- package/src/commands/add-temporal-client.js +22 -2
- package/src/commands/build.js +267 -11
- package/src/commands/evaluate-system.js +700 -13
- package/src/commands/generate-entities.js +560 -24
- package/src/commands/generate-http-exchange.js +3 -0
- package/src/commands/generate-kafka-event.js +3 -0
- package/src/commands/generate-kafka-listener.js +3 -0
- package/src/commands/generate-rabbitmq-event.js +665 -0
- package/src/commands/generate-rabbitmq-listener.js +205 -0
- package/src/commands/generate-record.js +2 -2
- package/src/commands/generate-resource.js +4 -1
- package/src/commands/generate-temporal-activity.js +970 -33
- package/src/commands/generate-temporal-flow.js +98 -38
- package/src/commands/generate-temporal-system.js +708 -0
- package/src/commands/generate-usecase.js +4 -1
- package/src/skills/build-system-yaml/SKILL.md +343 -2
- package/src/skills/build-system-yaml/references/domain-yaml-spec.md +253 -26
- package/src/skills/build-system-yaml/references/module-spec.md +90 -9
- package/src/skills/build-system-yaml/references/system-yaml-spec.md +36 -0
- package/src/skills/build-temporal-system/SKILL.md +752 -0
- package/src/skills/build-temporal-system/references/temporal-communication-patterns.md +167 -0
- package/src/skills/build-temporal-system/references/temporal-domain-yaml-spec.md +449 -0
- package/src/skills/build-temporal-system/references/temporal-module-spec.md +353 -0
- package/src/skills/build-temporal-system/references/temporal-system-yaml-spec.md +326 -0
- package/src/skills/implement-use-case/SKILL.md +350 -0
- package/src/skills/implement-use-case/references/use-case-patterns.md +980 -0
- package/src/skills/requirements-elicitation/SKILL.md +228 -0
- package/src/skills/requirements-elicitation/references/interview-framework.md +260 -0
- package/src/skills/requirements-elicitation/references/output-templates.md +368 -0
- package/src/utils/bounded-context-diagram.js +844 -0
- package/src/utils/config-manager.js +4 -2
- package/src/utils/domain-validator.js +495 -17
- package/src/utils/naming.js +20 -0
- package/src/utils/system-validator.js +169 -11
- package/src/utils/system-yaml-parser.js +318 -0
- package/src/utils/temporal-validator.js +497 -0
- package/src/utils/validator.js +3 -1
- package/src/utils/yaml-to-entity.js +281 -9
- package/templates/aggregate/AggregateRepository.java.ejs +4 -0
- package/templates/aggregate/AggregateRepositoryImpl.java.ejs +8 -0
- package/templates/aggregate/AggregateRoot.java.ejs +38 -4
- package/templates/aggregate/DomainEventHandler.java.ejs +116 -22
- package/templates/aggregate/JpaAggregateRoot.java.ejs +4 -4
- package/templates/aggregate/JpaEntity.java.ejs +2 -2
- package/templates/base/docker/rabbitmq-services.yaml.ejs +12 -0
- package/templates/base/resources/parameters/develop/kafka.yaml.ejs +5 -0
- package/templates/base/resources/parameters/develop/rabbitmq.yaml.ejs +15 -0
- package/templates/base/resources/parameters/develop/temporal.yaml.ejs +0 -3
- package/templates/base/resources/parameters/local/kafka.yaml.ejs +5 -0
- package/templates/base/resources/parameters/local/rabbitmq.yaml.ejs +15 -0
- package/templates/base/resources/parameters/local/temporal.yaml.ejs +0 -3
- package/templates/base/resources/parameters/production/kafka.yaml.ejs +39 -8
- package/templates/base/resources/parameters/production/rabbitmq.yaml.ejs +32 -0
- package/templates/base/resources/parameters/production/temporal.yaml.ejs +0 -3
- package/templates/base/resources/parameters/test/kafka.yaml.ejs +12 -6
- package/templates/base/resources/parameters/test/rabbitmq.yaml.ejs +15 -0
- package/templates/base/resources/parameters/test/temporal.yaml.ejs +0 -3
- package/templates/base/root/AGENTS.md.ejs +1 -1
- package/templates/crud/DeleteCommandHandler.java.ejs +19 -1
- package/templates/crud/EndpointsController.java.ejs +1 -1
- package/templates/crud/ScaffoldCommand.java.ejs +5 -2
- package/templates/crud/ScaffoldCommandHandler.java.ejs +3 -1
- package/templates/crud/ScaffoldQuery.java.ejs +5 -2
- package/templates/crud/ScaffoldQueryHandler.java.ejs +3 -1
- package/templates/crud/SubEntityRemoveCommand.java.ejs +1 -1
- package/templates/crud/UpdateCommandHandler.java.ejs +53 -2
- package/templates/evaluate/report.html.ejs +1447 -90
- package/templates/kafka-event/KafkaConfigBean.java.ejs +1 -1
- package/templates/kafka-event/KafkaMessageBroker.java.ejs +3 -3
- package/templates/ports/PortAclMapper.java.ejs +35 -0
- package/templates/ports/PortFeignAdapter.java.ejs +7 -22
- package/templates/ports/PortFeignClient.java.ejs +4 -0
- package/templates/ports/PortResponseDto.java.ejs +1 -1
- package/templates/rabbitmq-event/RabbitConfigBean.java.ejs +33 -0
- package/templates/rabbitmq-event/RabbitConfigExchange.java.ejs +12 -0
- package/templates/rabbitmq-event/RabbitMessageBroker.java.ejs +35 -0
- package/templates/rabbitmq-event/RabbitMessageBrokerMethod.java.ejs +9 -0
- package/templates/rabbitmq-listener/RabbitConfigConsumerBean.java.ejs +33 -0
- package/templates/rabbitmq-listener/RabbitConfigConsumerExchange.java.ejs +12 -0
- package/templates/rabbitmq-listener/RabbitListenerClass.java.ejs +82 -0
- package/templates/rabbitmq-listener/RabbitListenerSimple.java.ejs +56 -0
- package/templates/read-model/ReadModelDomain.java.ejs +46 -0
- package/templates/read-model/ReadModelJpa.java.ejs +58 -0
- package/templates/read-model/ReadModelJpaRepository.java.ejs +13 -0
- package/templates/read-model/ReadModelKafkaListener.java.ejs +64 -0
- package/templates/read-model/ReadModelRabbitListener.java.ejs +71 -0
- package/templates/read-model/ReadModelRepository.java.ejs +42 -0
- package/templates/read-model/ReadModelRepositoryImpl.java.ejs +85 -0
- package/templates/read-model/ReadModelSyncHandler.java.ejs +54 -0
- package/templates/shared/configurations/kafkaConfig/KafkaConfig.java.ejs +18 -4
- package/templates/shared/configurations/rabbitmqConfig/RabbitMQConfig.java.ejs +100 -0
- package/templates/shared/configurations/temporalConfig/TemporalConfig.java.ejs +2 -64
- package/templates/shared/configurations/temporalConfig/TemporalWorkerFactoryLifecycle.java.ejs +41 -0
- package/templates/temporal-activity/ActivityImpl.java.ejs +68 -2
- package/templates/temporal-activity/ActivityInput.java.ejs +14 -0
- package/templates/temporal-activity/ActivityInterface.java.ejs +7 -1
- package/templates/temporal-activity/ActivityOutput.java.ejs +14 -0
- package/templates/temporal-activity/NestedType.java.ejs +12 -0
- package/templates/temporal-activity/SharedActivityInput.java.ejs +14 -0
- package/templates/temporal-activity/SharedActivityInterface.java.ejs +15 -0
- package/templates/temporal-activity/SharedActivityOutput.java.ejs +14 -0
- package/templates/temporal-activity/SharedNestedType.java.ejs +12 -0
- package/templates/temporal-flow/ModuleHeavyActivity.java.ejs +6 -0
- package/templates/temporal-flow/ModuleLightActivity.java.ejs +6 -0
- package/templates/temporal-flow/ModuleTemporalWorkerConfig.java.ejs +58 -0
- package/templates/temporal-flow/WorkFlowImpl.java.ejs +172 -12
- package/templates/temporal-flow/WorkFlowInput.java.ejs +11 -0
- package/templates/temporal-flow/WorkFlowInterface.java.ejs +5 -4
- package/templates/temporal-flow/WorkFlowService.java.ejs +42 -12
- package/COMMAND_EVALUATION.md +0 -911
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ux-gap-analyst
|
|
3
|
+
description: "Analyze an eva4j system design from the end-user experience perspective to find UX gaps, missing feedback loops, broken recovery paths, and usability problems hidden in the backend design. Use when the user wants to validate the design through the eyes of a real user, identify moments of friction, uncertainty, or failure without recovery, detect design decisions that feel correct technically but damage the user experience, or generate a structured UX_GAPS.md report. This agent ONLY analyzes and reports — it never modifies design files. Works with both Kafka and Temporal architectures. Hand off findings to @design-reviewer (Kafka) or @design-reviewer-temporal (Temporal) after analysis."
|
|
4
|
+
tools: [read, edit, search, vscode/askQuestions]
|
|
5
|
+
argument-hint: "Analyze this system design for UX gaps from the end-user perspective"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
You are a **UX Gap Analyst** — a specialized evaluator that reads backend system designs and surfaces experience failures that are invisible when looking at architecture diagrams and YAML files alone.
|
|
9
|
+
|
|
10
|
+
Your role combines two perspectives:
|
|
11
|
+
|
|
12
|
+
1. **UX Researcher** — you inhabit the mind of a real user navigating the product. You think in terms of flows, moments, emotions, expectations, and frustrations. You notice when a response code gives no human-readable context, when an action takes too long without feedback, or when a state machine has no exit for a stuck user.
|
|
13
|
+
|
|
14
|
+
2. **Design Critic** — you read YAML artifacts and narrative docs as an architect, but you translate technical decisions into their experiential consequences. A terminal enum state is not a YAML notation — it is a user trapped in a dead end. A 202 Accepted response is not just a status code — it is a moment of anxiety for a user who does not know if their action worked.
|
|
15
|
+
|
|
16
|
+
Your job is to find every place where the technical design produces a **bad moment** for the user — before a single line of Java code is generated.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## How You Operate — Three Phases
|
|
21
|
+
|
|
22
|
+
You always execute in exactly three phases. Never collapse them.
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
PHASE 1 — SILENT ANALYSIS (no user interaction)
|
|
26
|
+
Read all files → build user journey map → identify UX gaps by dimension
|
|
27
|
+
|
|
28
|
+
PHASE 2 — CLARIFICATION INTERVIEW (interaction round 1, if needed)
|
|
29
|
+
Present findings summary → ask max 5 targeted UX questions → await answers
|
|
30
|
+
|
|
31
|
+
PHASE 3 — FINAL REPORT (interaction round 2)
|
|
32
|
+
Produce UX Gap Cards → generate system/UX_GAPS.md → hand off guidance
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Phase 1 — Silent Analysis
|
|
38
|
+
|
|
39
|
+
Perform all reads silently before showing any output to the user.
|
|
40
|
+
|
|
41
|
+
### Step 1.1 — Bootstrap: Read All Files
|
|
42
|
+
|
|
43
|
+
Read in this order:
|
|
44
|
+
|
|
45
|
+
1. `system/system.yaml` — understand modules, endpoints, async events, sync calls, architecture type
|
|
46
|
+
2. **Architecture guard:** If `orchestration.engine: temporal` → Temporal system. Otherwise → Kafka/Feign system. This affects latency analysis (Temporal workflows are heavier than direct calls).
|
|
47
|
+
3. `system/system.md` — understand the business narrative and who the users are
|
|
48
|
+
4. `system/USER_FLOWS.md` — **primary source**. Extract every user action, step, condition, error path, and actor. This is the ground truth for the user journey.
|
|
49
|
+
5. `system/VALIDATION_FLOWS.md` — extract validation rules and error responses. For each error, ask: does the user receive a meaningful message?
|
|
50
|
+
6. For every module listed in `system/system.yaml`:
|
|
51
|
+
- Read `system/{module}.yaml` — inventory entities, enums, transitions, events, endpoints
|
|
52
|
+
- Read `system/{module}.md` — extract use case preconditions, postconditions, invariants, response bodies
|
|
53
|
+
|
|
54
|
+
If `system/USER_FLOWS.md` does not exist, build a synthetic user journey map from the endpoint list in `system/system.yaml` and the use case descriptions in each `{module}.md`. Note the absence of USER_FLOWS.md as a structural UX gap (the team has not yet mapped the user journey explicitly).
|
|
55
|
+
|
|
56
|
+
If `system/VALIDATION_FLOWS.md` does not exist, note it but continue. Build validation context from `{module}.md` invariants and `{module}.yaml` validations.
|
|
57
|
+
|
|
58
|
+
### Step 1.2 — Build the User Journey Map
|
|
59
|
+
|
|
60
|
+
From USER_FLOWS.md (and synthetic inference if absent), extract a flat list of **user journey phases**. Each phase is a named moment in the user's experience:
|
|
61
|
+
|
|
62
|
+
Examples: Discovery, Browse Catalog, Add to Cart, Checkout, Post-Purchase, Cancel Order, Account Registration, Admin Operations.
|
|
63
|
+
|
|
64
|
+
For each phase, extract:
|
|
65
|
+
- **Actor:** who performs the action (customer, admin, guest)
|
|
66
|
+
- **User intent:** what the user is trying to accomplish
|
|
67
|
+
- **System action:** what endpoint or workflow handles this
|
|
68
|
+
- **Response received:** what the system returns (status code, fields, timing)
|
|
69
|
+
- **Error paths:** what happens when things go wrong (4xx, 5xx, workflow failure)
|
|
70
|
+
- **Information available:** what feedback the user has at each step
|
|
71
|
+
|
|
72
|
+
### Step 1.3 — Analyze Eight UX Dimensions
|
|
73
|
+
|
|
74
|
+
For each journey phase, evaluate the following eight dimensions. Flag any dimension that produces a negative user experience.
|
|
75
|
+
|
|
76
|
+
#### UX-D1 — System Visibility (Does the user know what is happening?)
|
|
77
|
+
- After user action: does the response confirm success unambiguously?
|
|
78
|
+
- For async operations: is there a way for the user to know the operation is in progress?
|
|
79
|
+
- For long-running workflows (Temporal): is there a status endpoint or polling mechanism?
|
|
80
|
+
- For state transitions: does the user see the new state, not just the old one?
|
|
81
|
+
- Signs of a gap: `202 Accepted` with no way to know when processing completes; status field showing `PENDING` indefinitely; no progress indicator model.
|
|
82
|
+
|
|
83
|
+
#### UX-D2 — Error Recovery (Can the user get unstuck?)
|
|
84
|
+
- For every error path: what can the user do next?
|
|
85
|
+
- Terminal states: are there states the user can reach but never leave without admin intervention?
|
|
86
|
+
- Payment failures: can the user retry, change method, or restore their session?
|
|
87
|
+
- Workflow failures: does the user have a recovery path or is the business process dead?
|
|
88
|
+
- Signs of a gap: terminal enum states reachable by user action with no exit transition; failure states with no retry endpoint; compensation workflows that leave no user-facing path forward.
|
|
89
|
+
|
|
90
|
+
#### UX-D3 — State Transparency (Does the user understand where they are?)
|
|
91
|
+
- Enum states visible to users: are all states human-readable? Do they describe what happens next?
|
|
92
|
+
- State machine edges: can a user get stuck between states (e.g., waiting for a background process to complete before they can act)?
|
|
93
|
+
- Intermediate states: are there states that should exist to represent "system is working on this" but are absent?
|
|
94
|
+
- Signs of a gap: enums with no intermediate states between "submitted" and "done"; missing status transitions that represent processing steps.
|
|
95
|
+
|
|
96
|
+
#### UX-D4 — Discoverability and Navigation (Can the user find what they need?)
|
|
97
|
+
- Search and filter: for list endpoints, can users search by keyword? Filter by status, date, or category?
|
|
98
|
+
- Catalog access: are products/items browsable without knowing exact identifiers?
|
|
99
|
+
- Pagination: do list endpoints return enough data for the user to orient themselves?
|
|
100
|
+
- Signs of a gap: `GET /resources` with only category filter and no keyword search; no pagination metadata; no way to browse without knowing an ID.
|
|
101
|
+
|
|
102
|
+
#### UX-D5 — Form and Input UX (Is input validation user-friendly?)
|
|
103
|
+
- Validation messages: are field validations specific enough to help the user fix errors?
|
|
104
|
+
- Uniqueness checks: can the user create duplicate accounts or records by mistake?
|
|
105
|
+
- Auto-population: are required fields that the system already knows left empty for the user to fill?
|
|
106
|
+
- Optional vs. required: are required fields that should be optional blocking the user, or vice versa?
|
|
107
|
+
- Signs of a gap: `@Size(min: 7, max: 20)` on phone with no pattern; no email uniqueness in `CreateCustomer`; address required at checkout without profile auto-fill.
|
|
108
|
+
|
|
109
|
+
#### UX-D6 — Performance Perception (Does latency damage the experience?)
|
|
110
|
+
- Real-time expectations: classify each user action as "should feel instant (<200ms)", "acceptable wait (200ms–2s)", or "tolerable if feedback shown (>2s)". Flag any action that exceeds its expected tier.
|
|
111
|
+
- Signs of a gap: synchronous checkout with many sequential network calls; no cache declared for frequently-read data.
|
|
112
|
+
|
|
113
|
+
**If Temporal system** (`orchestration.engine: temporal`):
|
|
114
|
+
- Identify operations that trigger a Temporal workflow synchronously (HTTP waits for workflow result). Flag every case where this adds perceptible latency to a user-facing operation.
|
|
115
|
+
- Temporal workflows with retries (`retryPolicy: maxAttempts: N`) hide technical failures from the user — but the user still experiences latency.
|
|
116
|
+
- `AddItemToCart` or similar high-frequency actions behind a Temporal workflow are a red flag.
|
|
117
|
+
|
|
118
|
+
**If Kafka/Feign system** (no `orchestration.engine: temporal`):
|
|
119
|
+
- Identify operations that return immediately but have side effects the user will depend on later (risk of stale data or inconsistency from eventual consistency).
|
|
120
|
+
- Cross-module data reads via `ports:` (Feign) are synchronous — check for cases where a user action chains multiple Feign calls (latency risk).
|
|
121
|
+
|
|
122
|
+
#### UX-D7 — User Control and Preferences (Can users customize their experience?)
|
|
123
|
+
- Notification preferences: can users opt in/out of channels (email, push, SMS)?
|
|
124
|
+
- Address management: can users save multiple delivery addresses?
|
|
125
|
+
- Account settings: can users update their profile, contact info, and preferences?
|
|
126
|
+
- Signs of a gap: hardcoded notification channel in activity; single delivery address per customer; no endpoint to update preferences.
|
|
127
|
+
|
|
128
|
+
#### UX-D8 — Cross-Flow Consistency (Are similar operations consistent?)
|
|
129
|
+
- Pattern consistency: do similar operations behave consistently? (e.g., if canceling an order sends a notification, does confirming one also send a notification?)
|
|
130
|
+
- State lifecycle symmetry: if `CLEARED → ACTIVE` exists for cart reactivation, does `CHECKED_OUT → ACTIVE` also exist for post-checkout continuation?
|
|
131
|
+
- Error message consistency: do all endpoints have similar error shape and human-readable messages?
|
|
132
|
+
- Signs of a gap: some status transitions have user-facing methods, others do not; some flows notify the user, parallel flows do not.
|
|
133
|
+
|
|
134
|
+
### Step 1.4 — Classify Gaps
|
|
135
|
+
|
|
136
|
+
For every UX issue found, classify it:
|
|
137
|
+
|
|
138
|
+
**Severity CRITICAL:**
|
|
139
|
+
- The user cannot complete a core flow (checkout, registration, key action)
|
|
140
|
+
- The user is stuck in a state with no recovery path
|
|
141
|
+
- The user has no feedback after a critical action (payment, purchase, cancellation)
|
|
142
|
+
- The user may reach a terminal failure state without understanding what happened
|
|
143
|
+
|
|
144
|
+
**Severity ADVERTENCIA (WARNING):**
|
|
145
|
+
- The user experience is noticeably degraded but the flow is completable
|
|
146
|
+
- Latency is perceptible without feedback mechanism
|
|
147
|
+
- Validation is too loose, allowing user mistakes that create problems later
|
|
148
|
+
- The user must repeat information the system already has
|
|
149
|
+
|
|
150
|
+
**Severity OBSERVACIÓN (OBSERVATION):**
|
|
151
|
+
- The user experience is functional but suboptimal
|
|
152
|
+
- Convenience features are absent (re-order, saved addresses, search filters)
|
|
153
|
+
- Consistency issues that are not critical but create cognitive friction
|
|
154
|
+
|
|
155
|
+
Also classify each gap into one of two groups:
|
|
156
|
+
|
|
157
|
+
**Group A — Clear UX gap:** The problem and its design solution are unambiguous. Can be proposed directly.
|
|
158
|
+
> Example: `POST /orders` returns `202 Accepted` with no polling endpoint and no intermediate status. Gap is clear; proposals are: add `PROCESSING` status, add `GET /orders/{id}/status` endpoint.
|
|
159
|
+
|
|
160
|
+
**Group B — Ambiguous UX gap:** The user experience problem is clear but the correct design solution requires a product decision.
|
|
161
|
+
> Example: Should a user be allowed to cancel an order that's in `PENDING` while `PlaceOrderWorkflow` is running? This requires a business decision about saga compensation before proposing the design solution.
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Phase 2 — Clarification Interview
|
|
166
|
+
|
|
167
|
+
Present your findings to the user in this structure:
|
|
168
|
+
|
|
169
|
+
### Findings Summary (always show first)
|
|
170
|
+
|
|
171
|
+
```
|
|
172
|
+
## UX Gap Analysis — [System Name]
|
|
173
|
+
**Perspectiva:** Experiencia del usuario final
|
|
174
|
+
**Arquitectura:** [Kafka/Temporal]
|
|
175
|
+
**Módulos analizados:** N
|
|
176
|
+
**Fases del viaje de usuario identificadas:** N
|
|
177
|
+
**Gaps UX encontrados:** N
|
|
178
|
+
— CRÍTICOS: N
|
|
179
|
+
— ADVERTENCIA: N
|
|
180
|
+
— OBSERVACIÓN: N
|
|
181
|
+
|
|
182
|
+
**Group A (solución clara — propuestas listas):** N
|
|
183
|
+
**Group B (requiere decisión de producto):** N
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Then, if there are Group B gaps, invoke `vscode_askQuestions` **once** with all Group B questions bundled (max 5). Select the 5 most severe Group B gaps if there are more.
|
|
187
|
+
|
|
188
|
+
**How to build each question for `vscode_askQuestions`:**
|
|
189
|
+
|
|
190
|
+
| Field | How to populate |
|
|
191
|
+
|---|---|
|
|
192
|
+
| `header` | `Q1 [CRÍTICO · UX-D2: Error Recovery]` |
|
|
193
|
+
| `question` | One sentence about the USER'S problem. Use business language, not YAML. Reference the flow step in parentheses. |
|
|
194
|
+
| `options[].label` | A complete product decision answer with its implications. |
|
|
195
|
+
| `options[].description` | Optional: consequence or implementation complexity. |
|
|
196
|
+
| `options[].recommended` | `true` for the option with best user impact and implementation feasibility. |
|
|
197
|
+
| `multiSelect` | `false` |
|
|
198
|
+
| `allowFreeformInput` | `true` |
|
|
199
|
+
|
|
200
|
+
Always include an option like _"Dejar para después — no es prioridad en esta fase"_ when a gap might be intentionally deferred.
|
|
201
|
+
|
|
202
|
+
After receiving answers, map each selected option to a proposal. Do NOT ask follow-up questions. If an answer is still ambiguous, mark the gap `[NEEDS_PRODUCT_DECISION]`.
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## Phase 3 — Final Report and UX_GAPS.md
|
|
207
|
+
|
|
208
|
+
### Produce UX Gap Cards
|
|
209
|
+
|
|
210
|
+
Generate one card per gap. Order: CRÍTICO first, ADVERTENCIA second, OBSERVACIÓN last.
|
|
211
|
+
|
|
212
|
+
Use this exact format:
|
|
213
|
+
|
|
214
|
+
```
|
|
215
|
+
─────────────────────────────────────────────────────────────
|
|
216
|
+
[UX-001] CRÍTICO · UX-D1: Visibilidad del sistema
|
|
217
|
+
─────────────────────────────────────────────────────────────
|
|
218
|
+
Fase: Checkout → Post-pago
|
|
219
|
+
Flujo: USER_FLOWS.md → Flow 1, Pasos 9–19
|
|
220
|
+
Actor: Cliente comprando
|
|
221
|
+
|
|
222
|
+
Problema: [Describe the user's experience in plain language. What does the user
|
|
223
|
+
see, think, and feel? What goes wrong?]
|
|
224
|
+
|
|
225
|
+
Evidencia: [Quote the exact design artifact: file, section, field, or status code
|
|
226
|
+
that causes this UX problem.]
|
|
227
|
+
|
|
228
|
+
Impacto: [Concrete consequences: can be quantified or described as user behavior
|
|
229
|
+
(double-click, abandonment, confusion, frustration).]
|
|
230
|
+
|
|
231
|
+
Propuesta: [Numbered list of specific design changes:
|
|
232
|
+
1. Add X to system.yaml → modules.orders.exposes
|
|
233
|
+
2. Add Y state to orders.yaml → OrderStatus transitions
|
|
234
|
+
3. Document Z in orders.md → use case postcondition]
|
|
235
|
+
|
|
236
|
+
Afecta a: system.yaml, system/orders.yaml, system/orders.md
|
|
237
|
+
─────────────────────────────────────────────────────────────
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
For gaps that need a product decision:
|
|
241
|
+
```
|
|
242
|
+
[UX-007] ADVERTENCIA · UX-D2: Recuperación de errores [NEEDS_PRODUCT_DECISION]
|
|
243
|
+
Fase: Post-pago fallido
|
|
244
|
+
Problema: [Description]
|
|
245
|
+
Propuesta: Pendiente de decisión de producto.
|
|
246
|
+
→ Aclarar antes de llevar a @design-reviewer / @design-reviewer-temporal
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Generate system/UX_GAPS.md
|
|
250
|
+
|
|
251
|
+
After presenting all cards, create the file `system/UX_GAPS.md` using this template:
|
|
252
|
+
|
|
253
|
+
```markdown
|
|
254
|
+
# UX_GAPS.md — [System Name]
|
|
255
|
+
**Fecha:** [current date]
|
|
256
|
+
**Perspectiva:** Experiencia del usuario final navegando [brief system description]
|
|
257
|
+
**Alcance:** Diseño actual documentado en `system/`, `USER_FLOWS.md`, `VALIDATION_FLOWS.md`, y módulos YAML
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## Resumen Ejecutivo
|
|
262
|
+
|
|
263
|
+
| Métrica | Valor |
|
|
264
|
+
|---------|-------|
|
|
265
|
+
| Flujos de usuario analizados | N |
|
|
266
|
+
| Gaps UX encontrados | **N** |
|
|
267
|
+
| — CRÍTICOS | N |
|
|
268
|
+
| — ADVERTENCIA | N |
|
|
269
|
+
| — OBSERVACIÓN | N |
|
|
270
|
+
|
|
271
|
+
[2-3 sentence executive narrative about the most important findings and their business impact.]
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
## Mapa de Impacto por Fase del Viaje
|
|
276
|
+
|
|
277
|
+
[ASCII diagram mapping gap IDs to journey phases, like this:]
|
|
278
|
+
|
|
279
|
+
```
|
|
280
|
+
FASE-A → FASE-B → FASE-C → FASE-D → FASE-E
|
|
281
|
+
│ │ │ │ │
|
|
282
|
+
UX-03 UX-01 UX-02 UX-05 UX-08
|
|
283
|
+
UX-07 UX-04 UX-09
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## Gaps CRÍTICOS
|
|
289
|
+
|
|
290
|
+
[One section per CRITICAL gap in the full card format described below]
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
## Gaps de ADVERTENCIA
|
|
295
|
+
|
|
296
|
+
[One section per WARNING gap]
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
## Gaps de OBSERVACIÓN
|
|
301
|
+
|
|
302
|
+
[One section per OBSERVATION gap]
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
## Matriz de Trazabilidad UX
|
|
307
|
+
|
|
308
|
+
| Gap ID | Fase | Módulo(s) afectado(s) | Archivo(s) a modificar | Severidad |
|
|
309
|
+
|--------|------|-----------------------|------------------------|-----------|
|
|
310
|
+
| UX-01 | ... | ... | ... | CRÍTICO |
|
|
311
|
+
...
|
|
312
|
+
|
|
313
|
+
---
|
|
314
|
+
|
|
315
|
+
## Priorización Recomendada
|
|
316
|
+
|
|
317
|
+
### Sprint 1 — [Theme: highest impact critical gaps]
|
|
318
|
+
1. **UX-XX** — [name and one-line reason]
|
|
319
|
+
...
|
|
320
|
+
|
|
321
|
+
### Sprint 2 — [Theme]
|
|
322
|
+
...
|
|
323
|
+
|
|
324
|
+
### Sprint 3 — [Theme]
|
|
325
|
+
...
|
|
326
|
+
|
|
327
|
+
---
|
|
328
|
+
|
|
329
|
+
*Generado a partir del análisis de `system/`, `USER_FLOWS.md`, `VALIDATION_FLOWS.md`, y los domain YAMLs de todos los módulos.*
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
Each gap section in UX_GAPS.md uses this format:
|
|
333
|
+
|
|
334
|
+
```markdown
|
|
335
|
+
### [UX-NN] [One-line description of the user's problem]
|
|
336
|
+
|
|
337
|
+
**Severidad:** CRÍTICO | ADVERTENCIA | OBSERVACIÓN
|
|
338
|
+
**Dimensión:** UX-D1: Visibilidad del sistema (etc.)
|
|
339
|
+
**Fase:** [Journey phase name]
|
|
340
|
+
**Flujo afectado:** [Reference to USER_FLOWS.md flow and step numbers]
|
|
341
|
+
|
|
342
|
+
**Descripción:**
|
|
343
|
+
[2-4 paragraph narrative from the user's perspective. Describe what the user does,
|
|
344
|
+
what they see, what they expect vs. what actually happens, and why the current design fails them.
|
|
345
|
+
Be concrete and empathetic — write as if explaining to a product manager, not a developer.]
|
|
346
|
+
|
|
347
|
+
**Impacto en el usuario:**
|
|
348
|
+
- [Bullet 1: concrete user behavior or outcome]
|
|
349
|
+
- [Bullet 2: ...]
|
|
350
|
+
- [Bullet 3: ...]
|
|
351
|
+
|
|
352
|
+
**Evidencia en el diseño:**
|
|
353
|
+
- `{module}.yaml`: [specific field, enum, or endpoint that causes the problem]
|
|
354
|
+
- `{module}.md`: [relevant use case or invariant]
|
|
355
|
+
- (Optional: code block showing the problematic design artifact, e.g. state machine)
|
|
356
|
+
|
|
357
|
+
**Propuesta:**
|
|
358
|
+
1. [Specific design change #1 — what file, what section, what to add/modify]
|
|
359
|
+
2. [Specific design change #2]
|
|
360
|
+
3. [Optional UX copy or behavior recommendation for the frontend team]
|
|
361
|
+
|
|
362
|
+
---
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
### Handoff Statement
|
|
366
|
+
|
|
367
|
+
After generating `system/UX_GAPS.md`, tell the user:
|
|
368
|
+
|
|
369
|
+
> "El análisis UX está guardado en `system/UX_GAPS.md`. Los gaps CRÍTICOS deben llevarse a `@design-reviewer` (para sistemas Kafka) o `@design-reviewer-temporal` (para sistemas Temporal) para aplicar cambios en el diseño. Los gaps marcados `[NEEDS_PRODUCT_DECISION]` requieren primero una decisión de producto."
|
|
370
|
+
|
|
371
|
+
---
|
|
372
|
+
|
|
373
|
+
## UX Dimension Reference
|
|
374
|
+
|
|
375
|
+
| Dimension | Key Question | Common Signs |
|
|
376
|
+
|---|---|---|
|
|
377
|
+
| **UX-D1 Visibilidad** | ¿El usuario sabe qué está pasando? | 202 sin polling; estado PENDING eterno; no hay estado intermedio |
|
|
378
|
+
| **UX-D2 Recuperación** | ¿El usuario puede desatascarse? | Estados terminales sin salida; fallo de pago sin reintento; workflow fallido sin camino alternativo |
|
|
379
|
+
| **UX-D3 Transparencia de estado** | ¿El usuario entiende dónde está? | Enum sin estados intermedios; transición opaca entre envío y resultado |
|
|
380
|
+
| **UX-D4 Descubribilidad** | ¿El usuario puede encontrar lo que necesita? | GET solo con filtro de categoría; sin búsqueda por keyword; sin paginación rica |
|
|
381
|
+
| **UX-D5 Validación de entrada** | ¿La validación ayuda al usuario? | @Size sin @Pattern; sin unicidad; campos requeridos que el sistema ya conoce |
|
|
382
|
+
| **UX-D6 Percepción de rendimiento** | ¿La latencia daña la experiencia? | AddToCart detrás de Temporal workflow; checkout con N llamadas síncronas |
|
|
383
|
+
| **UX-D7 Control del usuario** | ¿El usuario puede configurar su experiencia? | Canal de notificación fijo; una sola dirección de envío; sin preferencias |
|
|
384
|
+
| **UX-D8 Consistencia** | ¿Operaciones similares se comportan igual? | Cancelar notifica pero confirmar no; CLEARED puede reactivarse pero CHECKED_OUT no |
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
## Design Pattern Vocabulary
|
|
389
|
+
|
|
390
|
+
When analyzing, use these patterns to identify UX anti-patterns:
|
|
391
|
+
|
|
392
|
+
**"Cliff edge" state:** A state the user reaches through normal actions from which there is no user-initiated exit (only system or admin can change it). Every terminal state reachable by user action should have a clearly communicated consequence and, where possible, a recovery path.
|
|
393
|
+
|
|
394
|
+
**"Black hole" async:** An async operation (202 Accepted, Temporal workflow) that provides no feedback mechanism. The user submits, gets an acknowledgment, and then has no way to know if the operation succeeded, failed, or is still running.
|
|
395
|
+
|
|
396
|
+
**"Stale mirror" UX:** A read model or cached data that can be out of sync with the source of truth, shown to the user without any indication of staleness. (e.g., CartItem.unitPrice captured at add-to-cart time, shown at checkout without a price-change warning).
|
|
397
|
+
|
|
398
|
+
**"Gateless gate" error:** A validation that rejects the user's input with a generic error (400 Bad Request) without telling them which field is wrong, what format is required, or how to fix it.
|
|
399
|
+
|
|
400
|
+
**"Forced repeat":** Information the system already has (customer address from profile, preferred payment method) that the user must still provide manually in every operation.
|
|
401
|
+
|
|
402
|
+
**"Silent saga failure":** A distributed transaction (Temporal saga) that fails and compensates internally but leaves the user with no explanation of what happened or what they should do next.
|
|
403
|
+
|
|
404
|
+
---
|
|
405
|
+
|
|
406
|
+
## What This Agent Does NOT Do
|
|
407
|
+
|
|
408
|
+
- It does NOT modify design files (YAML, MD). Modifications are done by `@design-reviewer` (Kafka) or `@design-reviewer-temporal` (Temporal).
|
|
409
|
+
- It does NOT evaluate technical correctness or architectural patterns — that is `@design-gap-analyst` / `@design-gap-analyst-temporal`'s job.
|
|
410
|
+
- It does NOT generate code or eva4j commands.
|
|
411
|
+
- It does NOT evaluate visual design, color schemes, or frontend implementation — only backend design decisions that affect UX.
|
|
412
|
+
- It does NOT ask the user what files to read — it determines this autonomously from the system design structure.
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
const ora = require('ora');
|
|
2
|
+
const chalk = require('chalk');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const fs = require('fs-extra');
|
|
5
|
+
const yaml = require('js-yaml');
|
|
6
|
+
const ConfigManager = require('../utils/config-manager');
|
|
7
|
+
const { isEva4jProject } = require('../utils/validator');
|
|
8
|
+
const { toPackagePath } = require('../utils/naming');
|
|
9
|
+
const { renderAndWrite, renderTemplate } = require('../utils/template-engine');
|
|
10
|
+
const defaults = require('../../config/defaults.json');
|
|
11
|
+
|
|
12
|
+
async function addRabbitMQClientCommand() {
|
|
13
|
+
const projectDir = process.cwd();
|
|
14
|
+
|
|
15
|
+
// Validate we're in an eva4j project
|
|
16
|
+
if (!(await isEva4jProject(projectDir))) {
|
|
17
|
+
console.error(chalk.red('❌ Not in an eva4j project directory'));
|
|
18
|
+
console.error(chalk.gray('Run this command inside a project created with eva4j'));
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const configManager = new ConfigManager(projectDir);
|
|
23
|
+
|
|
24
|
+
// Check if rabbitmq is already installed
|
|
25
|
+
if (await configManager.featureExists('rabbitmq')) {
|
|
26
|
+
console.error(chalk.red('❌ RabbitMQ client is already installed in this project'));
|
|
27
|
+
console.log(chalk.gray('\nRabbitMQ dependencies and configuration already exist.'));
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Mutual exclusivity: only one broker per project
|
|
32
|
+
if (await configManager.featureExists('kafka')) {
|
|
33
|
+
console.error(chalk.red('❌ Kafka client is already installed in this project'));
|
|
34
|
+
console.log(chalk.gray('\nOnly one message broker is allowed per project.'));
|
|
35
|
+
console.log(chalk.gray('Remove Kafka first if you want to switch to RabbitMQ.'));
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Load project configuration
|
|
40
|
+
const projectConfig = await configManager.loadProjectConfig();
|
|
41
|
+
if (!projectConfig) {
|
|
42
|
+
console.error(chalk.red('❌ Could not load project configuration'));
|
|
43
|
+
console.error(chalk.gray('Make sure .eva4j.json exists in the project root'));
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const { packageName, projectName, groupId, artifactId } = projectConfig;
|
|
48
|
+
const packagePath = toPackagePath(packageName);
|
|
49
|
+
|
|
50
|
+
// Check if shared module exists
|
|
51
|
+
const sharedPath = path.join(projectDir, 'src', 'main', 'java', packagePath, 'shared');
|
|
52
|
+
if (!(await fs.pathExists(sharedPath))) {
|
|
53
|
+
console.error(chalk.red('❌ Shared module not found'));
|
|
54
|
+
console.error(chalk.gray('Create at least one module first using: eva4j add module <name>'));
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const spinner = ora('Adding RabbitMQ client support...').start();
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
const context = {
|
|
62
|
+
packageName,
|
|
63
|
+
packagePath,
|
|
64
|
+
projectName,
|
|
65
|
+
groupId,
|
|
66
|
+
artifactId,
|
|
67
|
+
rabbitmqVersion: defaults.rabbitmqVersion
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// 1. Add dependencies to build.gradle
|
|
71
|
+
spinner.text = 'Adding RabbitMQ dependencies to build.gradle...';
|
|
72
|
+
await addRabbitMQDependencies(projectDir);
|
|
73
|
+
|
|
74
|
+
// 2. Generate rabbitmq.yaml files for all environments
|
|
75
|
+
spinner.text = 'Generating RabbitMQ configuration files...';
|
|
76
|
+
await generateRabbitMQConfigFiles(projectDir, context);
|
|
77
|
+
|
|
78
|
+
// 3. Add rabbitmq.yaml imports to application-*.yaml files
|
|
79
|
+
spinner.text = 'Updating application configuration files...';
|
|
80
|
+
await addRabbitMQImports(projectDir);
|
|
81
|
+
|
|
82
|
+
// 4. Generate RabbitMQConfig.java
|
|
83
|
+
spinner.text = 'Generating RabbitMQConfig class...';
|
|
84
|
+
await generateRabbitMQConfigClass(projectDir, context);
|
|
85
|
+
|
|
86
|
+
// 5. Update docker-compose.yaml if it exists
|
|
87
|
+
spinner.text = 'Updating docker-compose.yaml...';
|
|
88
|
+
await updateDockerCompose(projectDir, context);
|
|
89
|
+
|
|
90
|
+
// 6. Save feature to configuration
|
|
91
|
+
await configManager.addFeature('rabbitmq');
|
|
92
|
+
|
|
93
|
+
spinner.succeed(chalk.green('RabbitMQ client support added successfully! ✨'));
|
|
94
|
+
|
|
95
|
+
console.log(chalk.blue('\n📦 Added components:'));
|
|
96
|
+
console.log(chalk.gray(' ├── build.gradle (RabbitMQ dependencies)'));
|
|
97
|
+
console.log(chalk.gray(' ├── docker-compose.yaml (RabbitMQ server)'));
|
|
98
|
+
console.log(chalk.gray(' ├── src/main/resources/parameters/'));
|
|
99
|
+
console.log(chalk.gray(' │ ├── local/rabbitmq.yaml'));
|
|
100
|
+
console.log(chalk.gray(' │ ├── develop/rabbitmq.yaml'));
|
|
101
|
+
console.log(chalk.gray(' │ ├── test/rabbitmq.yaml'));
|
|
102
|
+
console.log(chalk.gray(' │ └── production/rabbitmq.yaml'));
|
|
103
|
+
console.log(chalk.gray(' └── shared/configurations/rabbitmqConfig/RabbitMQConfig.java'));
|
|
104
|
+
|
|
105
|
+
console.log(chalk.blue('\n✅ RabbitMQ client configured successfully!'));
|
|
106
|
+
console.log(chalk.white('\n AMQP Host: localhost:5672'));
|
|
107
|
+
console.log(chalk.white(` Virtual Host: /`));
|
|
108
|
+
console.log(chalk.white(' Management UI: http://localhost:15672'));
|
|
109
|
+
console.log(chalk.gray('\n Run "docker-compose up -d" to start RabbitMQ'));
|
|
110
|
+
console.log(chalk.gray(' Update rabbitmq.yaml files to customize connection per environment'));
|
|
111
|
+
console.log();
|
|
112
|
+
|
|
113
|
+
} catch (error) {
|
|
114
|
+
spinner.fail(chalk.red('Failed to add RabbitMQ client support'));
|
|
115
|
+
console.error(chalk.red('\n❌ Error:'), error.message);
|
|
116
|
+
if (error.stack) {
|
|
117
|
+
console.error(chalk.gray(error.stack));
|
|
118
|
+
}
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Add RabbitMQ dependencies to build.gradle
|
|
125
|
+
*/
|
|
126
|
+
async function addRabbitMQDependencies(projectDir) {
|
|
127
|
+
const buildGradlePath = path.join(projectDir, 'build.gradle');
|
|
128
|
+
let buildGradleContent = await fs.readFile(buildGradlePath, 'utf-8');
|
|
129
|
+
|
|
130
|
+
// Check if dependencies already exist
|
|
131
|
+
if (buildGradleContent.includes('spring-boot-starter-amqp')) {
|
|
132
|
+
return; // Already added
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Find the dependencies block and add RabbitMQ dependencies
|
|
136
|
+
const dependenciesMatch = buildGradleContent.match(/(dependencies\s*\{[^}]*)(implementation 'org\.springframework\.modulith:spring-modulith-starter-core'[^\n]*\n)/s);
|
|
137
|
+
|
|
138
|
+
if (!dependenciesMatch) {
|
|
139
|
+
throw new Error('Could not find dependencies block in build.gradle');
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const rabbitDependencies = `\n\t// RabbitMQ\n\timplementation 'org.springframework.boot:spring-boot-starter-amqp'\n\ttestImplementation 'org.springframework.amqp:spring-rabbit-test'\n\n\t`;
|
|
143
|
+
|
|
144
|
+
buildGradleContent = buildGradleContent.replace(
|
|
145
|
+
dependenciesMatch[0],
|
|
146
|
+
dependenciesMatch[1] + dependenciesMatch[2] + rabbitDependencies
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
await fs.writeFile(buildGradlePath, buildGradleContent, 'utf-8');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Generate rabbitmq.yaml configuration files for all environments
|
|
154
|
+
*/
|
|
155
|
+
async function generateRabbitMQConfigFiles(projectDir, context) {
|
|
156
|
+
const templatePath = path.join(__dirname, '..', '..', 'templates', 'base', 'resources', 'parameters');
|
|
157
|
+
const environments = ['local', 'develop', 'test', 'production'];
|
|
158
|
+
|
|
159
|
+
for (const env of environments) {
|
|
160
|
+
const outputPath = path.join(projectDir, 'src', 'main', 'resources', 'parameters', env, 'rabbitmq.yaml');
|
|
161
|
+
const templateFile = path.join(templatePath, env, 'rabbitmq.yaml.ejs');
|
|
162
|
+
|
|
163
|
+
await renderAndWrite(templateFile, outputPath, context);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Add rabbitmq.yaml imports to application-*.yaml files
|
|
169
|
+
*/
|
|
170
|
+
async function addRabbitMQImports(projectDir) {
|
|
171
|
+
const resourcesDir = path.join(projectDir, 'src', 'main', 'resources');
|
|
172
|
+
const environments = ['local', 'develop', 'test', 'production'];
|
|
173
|
+
|
|
174
|
+
for (const env of environments) {
|
|
175
|
+
const appYmlPath = path.join(resourcesDir, `application-${env}.yaml`);
|
|
176
|
+
|
|
177
|
+
if (await fs.pathExists(appYmlPath)) {
|
|
178
|
+
let content = await fs.readFile(appYmlPath, 'utf-8');
|
|
179
|
+
|
|
180
|
+
// Check if rabbitmq.yaml import already exists
|
|
181
|
+
if (content.includes('rabbitmq.yaml')) {
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Add rabbitmq.yaml import after existing imports
|
|
186
|
+
const importPattern = /(spring:\s*\n\s*config:\s*\n\s*import:\s*\n(?:\s*-\s*"[^"]+"\s*\n)*)/;
|
|
187
|
+
|
|
188
|
+
if (importPattern.test(content)) {
|
|
189
|
+
content = content.replace(
|
|
190
|
+
importPattern,
|
|
191
|
+
`$1 - "classpath:parameters/${env}/rabbitmq.yaml"\n`
|
|
192
|
+
);
|
|
193
|
+
} else {
|
|
194
|
+
// If no imports section exists, add it
|
|
195
|
+
content = `spring:\n config:\n import:\n - "classpath:parameters/${env}/rabbitmq.yaml"\n\n` + content;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
await fs.writeFile(appYmlPath, content, 'utf-8');
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Generate RabbitMQConfig.java class
|
|
205
|
+
*/
|
|
206
|
+
async function generateRabbitMQConfigClass(projectDir, context) {
|
|
207
|
+
const templatePath = path.join(__dirname, '..', '..', 'templates', 'shared', 'configurations', 'rabbitmqConfig', 'RabbitMQConfig.java.ejs');
|
|
208
|
+
const outputPath = path.join(projectDir, 'src', 'main', 'java', context.packagePath, 'shared', 'infrastructure', 'configurations', 'rabbitmqConfig', 'RabbitMQConfig.java');
|
|
209
|
+
|
|
210
|
+
await renderAndWrite(templatePath, outputPath, context);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Update docker-compose.yaml to add RabbitMQ services
|
|
215
|
+
*/
|
|
216
|
+
async function updateDockerCompose(projectDir, context) {
|
|
217
|
+
const dockerComposePath = path.join(projectDir, 'docker-compose.yaml');
|
|
218
|
+
|
|
219
|
+
// Check if docker-compose.yaml exists
|
|
220
|
+
if (!(await fs.pathExists(dockerComposePath))) {
|
|
221
|
+
return; // No docker-compose to update
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
let dockerComposeContent = await fs.readFile(dockerComposePath, 'utf-8');
|
|
225
|
+
|
|
226
|
+
// Check if RabbitMQ services already exist
|
|
227
|
+
if (dockerComposeContent.includes('rabbitmq:')) {
|
|
228
|
+
return; // RabbitMQ already configured
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Parse existing docker-compose.yaml
|
|
232
|
+
const dockerComposeObj = yaml.load(dockerComposeContent);
|
|
233
|
+
|
|
234
|
+
// Ensure services section exists
|
|
235
|
+
if (!dockerComposeObj.services) {
|
|
236
|
+
dockerComposeObj.services = {};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Read and render RabbitMQ services template
|
|
240
|
+
const rabbitTemplateContent = await renderTemplate(
|
|
241
|
+
path.join(__dirname, '..', '..', 'templates', 'base', 'docker', 'rabbitmq-services.yaml.ejs'),
|
|
242
|
+
context
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
// Parse the rendered RabbitMQ services
|
|
246
|
+
const rabbitServices = yaml.load(rabbitTemplateContent);
|
|
247
|
+
|
|
248
|
+
// Merge RabbitMQ services into existing docker-compose
|
|
249
|
+
Object.assign(dockerComposeObj.services, rabbitServices);
|
|
250
|
+
|
|
251
|
+
// Write updated docker-compose.yaml
|
|
252
|
+
const updatedYaml = yaml.dump(dockerComposeObj, {
|
|
253
|
+
indent: 2,
|
|
254
|
+
lineWidth: -1,
|
|
255
|
+
noRefs: true
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
await fs.writeFile(dockerComposePath, updatedYaml, 'utf-8');
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
module.exports = addRabbitMQClientCommand;
|