@ronaldjdevfs/forge 1.3.0-beta → 1.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.
Files changed (62) hide show
  1. package/README.md +1 -1
  2. package/package.json +1 -1
  3. package/skills/forge/SKILL.md +57 -128
  4. package/skills/forge/command/forge.md +59 -14
  5. package/skills/forge/reference/adr.md +242 -0
  6. package/skills/forge/reference/anti-corruption-layer.md +340 -0
  7. package/skills/forge/reference/api-design.md +7 -0
  8. package/skills/forge/reference/api-versioning.md +354 -0
  9. package/skills/forge/reference/architectural-depth-checklist.md +311 -0
  10. package/skills/forge/reference/architecture-template.md +41 -0
  11. package/skills/forge/reference/assay.md +6 -0
  12. package/skills/forge/reference/bounded-contexts.md +311 -0
  13. package/skills/forge/reference/chain.md +6 -0
  14. package/skills/forge/reference/cohesion-checklist.md +256 -0
  15. package/skills/forge/reference/cqrs.md +286 -0
  16. package/skills/forge/reference/data-patterns.md +6 -0
  17. package/skills/forge/reference/di-strategies.md +6 -0
  18. package/skills/forge/reference/errors.md +5 -0
  19. package/skills/forge/reference/events.md +8 -0
  20. package/skills/forge/reference/evolutionary-architecture.md +300 -0
  21. package/skills/forge/reference/forge.md +7 -0
  22. package/skills/forge/reference/hooks.md +6 -0
  23. package/skills/forge/reference/idempotency.md +283 -0
  24. package/skills/forge/reference/inscribe.md +5 -0
  25. package/skills/forge/reference/inspect.md +6 -0
  26. package/skills/forge/reference/modular-monolith.md +252 -0
  27. package/skills/forge/reference/observability.md +5 -0
  28. package/skills/forge/reference/quench.md +5 -0
  29. package/skills/forge/reference/relocate.md +6 -0
  30. package/skills/forge/reference/sagas.md +359 -0
  31. package/skills/forge/reference/security-patterns.md +6 -0
  32. package/skills/forge/reference/smelt.md +6 -0
  33. package/skills/forge/reference/temper.md +6 -0
  34. package/skills/forge/reference/testing-patterns.md +6 -0
  35. package/skills/forge/reference/transactional-outbox.md +311 -0
  36. package/skills/forge/scripts/architecture.mjs +10 -5
  37. package/skills/forge/scripts/assay.mjs +2 -2
  38. package/skills/forge/scripts/chain.mjs +31 -5
  39. package/skills/forge/scripts/context.mjs +24 -4
  40. package/skills/forge/scripts/detect.mjs +39 -30
  41. package/skills/forge/scripts/forge-boot.mjs +108 -0
  42. package/skills/forge/scripts/forge-config.mjs +182 -3
  43. package/skills/forge/scripts/forge-state.mjs +1 -1
  44. package/skills/forge/scripts/forgeSentinel-lib.mjs +2 -2
  45. package/skills/forge/scripts/forgeSentinel.mjs +2 -2
  46. package/skills/forge/scripts/forgeSmith.mjs +2 -2
  47. package/skills/forge/scripts/graph.mjs +65 -9
  48. package/skills/forge/scripts/hook.mjs +2 -2
  49. package/skills/forge/scripts/inspect.mjs +56 -48
  50. package/skills/forge/scripts/parse-imports.mjs +0 -2
  51. package/skills/forge/scripts/posttool.mjs +211 -17
  52. package/skills/forge/scripts/recommendation-engine.mjs +125 -0
  53. package/skills/forge/scripts/rollback.mjs +5 -3
  54. package/skills/forge/templates/feature/acl-gateway.ts.md +52 -0
  55. package/skills/forge/templates/feature/acl-repository.ts.md +36 -0
  56. package/skills/forge/templates/feature/acl-translator.ts.md +24 -0
  57. package/skills/forge/templates/feature/cqrs-query.ts.md +40 -0
  58. package/skills/forge/templates/feature/outbox-repository.ts.md +36 -0
  59. package/skills/forge/templates/feature/saga-orchestrator.ts.md +72 -0
  60. package/skills/forge/templates/platform/outbox-relayer.ts.md +80 -0
  61. package/skills/forge/tests/core.test.mjs +288 -4
  62. package/src/cli.js +26 -13
@@ -71,3 +71,9 @@ node .opencode/skills/forge/scripts/architecture.mjs
71
71
  - Si hay ciclo, extraer la interfaz común a shared/
72
72
  - Documentar dependencias en ARCHITECTURE.md
73
73
  - Revisar dependencias después de cada migración
74
+
75
+ ## Ver también
76
+
77
+ - `scripts/graph.mjs` — el grafo que chain analiza topológicamente
78
+ - `reference/evolutionary-architecture.md` — fitness functions de dependencias
79
+ - `reference/modular-monolith.md` — ciclo de dependencias como señal de split
@@ -0,0 +1,256 @@
1
+ # Cohesion — Checklist de Referencias Cruzadas
2
+
3
+ Checklist para llevar la cohesión del corpus de referencias de ~8.5/10 a 10/10. Cada entrada detalla el archivo, los enlaces que debe añadir y el estado actual.
4
+
5
+ ---
6
+
7
+ ## Estado actual
8
+
9
+ ```
10
+ [x] F1 — Enlazar 7 archivos huérfanos (0 entrantes + 0 salientes)
11
+ [x] F2 — Completar backlinks de las 10 referencias estratégicas
12
+ [x] F3 — Cerrar 6 lagunas de referenciación
13
+ [x] F4 — Asegurar bidireccionalidad entre pares
14
+ ```
15
+
16
+ ---
17
+
18
+ ## F1 — Huérfanos con "Ver también"
19
+
20
+ 7 archivos que actualmente tienen 0 enlaces entrantes y 0 enlaces salientes dentro del corpus.
21
+
22
+ ### F1.1 — `assay.md`
23
+
24
+ - [ ] Añadir "Ver también" al final:
25
+ - `reference/inspect.md` — el reporte que assay evalúa cualitativamente
26
+ - `reference/adr.md` — ADRs como insumo para las opiniones de cada persona
27
+ - `reference/principles.md` — principios contra los que assay contrasta las decisiones
28
+ - `reference/sagas.md`, `reference/cqrs.md` — patrones que assay puede evaluar
29
+
30
+ ### F1.2 — `chain.md`
31
+
32
+ - [ ] Añadir "Ver también" al final:
33
+ - `scripts/graph.mjs` — el grafo que chain analiza topológicamente
34
+ - `reference/evolutionary-architecture.md` — fitness functions de dependencias
35
+ - `reference/modular-monolith.md` — ciclo de dependencias como señal de split
36
+
37
+ ### F1.3 — `di-strategies.md`
38
+
39
+ - [ ] Añadir "Ver también" al final:
40
+ - `reference/temper.md` — endurecimiento de DI (complemento directo)
41
+ - `reference/patterns.md` — naming y convenciones de contenedor DI
42
+ - `reference/testing-patterns.md` — mocks y DI testing
43
+
44
+ ### F1.4 — `forge.md`
45
+
46
+ - [ ] Añadir "Ver también" al final:
47
+ - `reference/bounded-contexts.md` — identificación de contexts al inicializar
48
+ - `reference/modular-monolith.md` — decisión de estructura al iniciar proyecto
49
+ - `reference/principles.md` — principios que guían la inicialización
50
+ - `reference/evolutionary-architecture.md` — bootstrap como primer paso evolutivo
51
+
52
+ ### F1.5 — `hooks.md`
53
+
54
+ - [ ] Añadir "Ver también" al final:
55
+ - `reference/quench.md` — validación que el hook ejecuta en pre-commit
56
+ - `reference/evolutionary-architecture.md` — fitness functions como hook
57
+ - `reference/adr.md` — ADRs como insumo para validación en hook
58
+ - `reference/detect.mjs` — script que el hook invoca
59
+
60
+ ### F1.6 — `smelt.md`
61
+
62
+ - [ ] Añadir "Ver también" al final:
63
+ - `reference/relocate.md` — operación similar de extracción
64
+ - `reference/data-patterns.md` — identificación de qué extraer a shared
65
+ - `reference/errors.md` — errores tipados como candidatos a smelt
66
+
67
+ ### F1.7 — `temper.md`
68
+
69
+ - [ ] Añadir "Ver también" al final:
70
+ - `reference/di-strategies.md` — selección de estrategia DI antes de temperar
71
+ - `reference/patterns.md` — convenciones de naming en DI
72
+ - `reference/testing-patterns.md` — testabilidad que DI disciplinada habilita
73
+
74
+ ---
75
+
76
+ ## F2 — Backlinks de referencias estratégicas
77
+
78
+ Backlinks faltantes desde las referencias existentes hacia las 10 nuevas.
79
+
80
+ ### F2.1 — `modular-monolith.md`
81
+
82
+ Backlinks esperados: relocate, reforge, cast
83
+
84
+ | Desde | Estado | Acción |
85
+ |---|---|---|
86
+ | `relocate.md` | ✅ Ya enlaza | — |
87
+ | `reforge.md` | ✅ Ya enlaza | — |
88
+ | `cast.md` | ✅ Ya enlaza | — |
89
+
90
+ ### F2.2 — `adr.md`
91
+
92
+ Backlinks esperados: reforge, cast, inscribe
93
+
94
+ | Desde | Estado | Acción |
95
+ |---|---|---|
96
+ | `reforge.md` | ✅ Ya enlaza | — |
97
+ | `cast.md` | ✅ Ya enlaza | — |
98
+ | `inscribe.md` | ✅ Ya enlaza | — |
99
+
100
+ ### F2.3 — `anti-corruption-layer.md`
101
+
102
+ Backlinks esperados: relocate, reforge, cast
103
+
104
+ | Desde | Estado | Acción |
105
+ |---|---|---|
106
+ | `relocate.md` | ✅ Ya enlaza | — |
107
+ | `reforge.md` | ✅ Ya enlaza | — |
108
+ | `cast.md` | ✅ Ya enlaza | — |
109
+
110
+ ### F2.4 — `evolutionary-architecture.md`
111
+
112
+ Backlinks esperados: inspect, reforge, quench, graph
113
+
114
+ | Desde | Estado | Acción |
115
+ |---|---|---|
116
+ | `inspect.md` | ✅ Ya enlaza | — |
117
+ | `reforge.md` | ✅ Ya enlaza | — |
118
+ | `quench.md` | ✅ Ya enlaza | — |
119
+ | `graph.md` | ✅ No existe en reference/ | No-op |
120
+
121
+ ### F2.5 — `cqrs.md`
122
+
123
+ Backlinks esperados: data-patterns, events, cast
124
+
125
+ | Desde | Estado | Acción |
126
+ |---|---|---|
127
+ | `data-patterns.md` | ✅ Ya enlaza | — |
128
+ | `events.md` | ✅ Ya enlaza | — |
129
+ | `cast.md` | ✅ Ya enlaza | — |
130
+
131
+ ### F2.6 — `sagas.md`
132
+
133
+ Backlinks esperados: events, cast
134
+
135
+ | Desde | Estado | Acción |
136
+ |---|---|---|
137
+ | `events.md` | ✅ Ya enlaza | — |
138
+ | `cast.md` | ✅ Ya enlaza | — |
139
+
140
+ ### F2.7 — `transactional-outbox.md`
141
+
142
+ Backlinks esperados: events, idempotency, sagas
143
+
144
+ | Desde | Estado | Acción |
145
+ |---|---|---|
146
+ | `events.md` | ✅ Ya enlaza | — |
147
+ | `idempotency.md` | ✅ Ya enlaza | — |
148
+ | `sagas.md` | ✅ Ya enlaza | — |
149
+
150
+ ### F2.8 — `idempotency.md`
151
+
152
+ Backlinks esperados: api-design, events, sagas, transactional-outbox
153
+
154
+ | Desde | Estado | Acción |
155
+ |---|---|---|
156
+ | `api-design.md` | ✅ Ya enlaza | — |
157
+ | `events.md` | ✅ Ya enlaza | — |
158
+ | `sagas.md` | ✅ Ya enlaza | — |
159
+ | `transactional-outbox.md` | ✅ Ya enlaza | — |
160
+
161
+ ### F2.9 — `api-versioning.md`
162
+
163
+ Backlinks esperados: api-design, patterns, cast, reforge
164
+
165
+ | Desde | Estado | Acción |
166
+ |---|---|---|
167
+ | `api-design.md` | ✅ Ya enlaza | — |
168
+ | `patterns.md` | ✅ Ya enlaza | — |
169
+ | `cast.md` | ✅ Ya enlaza | — |
170
+ | `reforge.md` | ✅ Ya enlaza | — |
171
+
172
+ ### F2.10 — `bounded-contexts.md`
173
+
174
+ Backlinks esperados: modular-monolith, anti-corruption-layer, cqrs, sagas, cast, reforge, relocate
175
+
176
+ | Desde | Estado | Acción |
177
+ |---|---|---|
178
+ | `modular-monolith.md` | ✅ Ya enlaza | — |
179
+ | `anti-corruption-layer.md` | ✅ Ya enlaza | — |
180
+ | `cqrs.md` | ✅ Ya enlaza | — |
181
+ | `sagas.md` | ✅ Ya enlaza | — |
182
+ | `cast.md` | ✅ Ya enlaza | — |
183
+ | `reforge.md` | ✅ Ya enlaza | — |
184
+ | `relocate.md` | ✅ Ya enlaza | — |
185
+
186
+ **Único pendiente F2:**
187
+ - [x] `graph.md` → no existe en reference/ — no-op
188
+
189
+ ---
190
+
191
+ ## F3 — Lagunas de referenciación
192
+
193
+ Conceptos mencionados en una referencia pero sin enlace a la referencia dedicada.
194
+
195
+ ### F3.1 — `assay.md`
196
+
197
+ - [ ] Donde menciona "violaciones" y "auditoría", enlazar a `reference/inspect.md`
198
+
199
+ ### F3.2 — `chain.md`
200
+
201
+ - [ ] Donde habla de "dependencias" y "ciclos", enlazar a `scripts/graph.mjs` y `reference/evolutionary-architecture.md`
202
+
203
+ ### F3.3 — `forge.md`
204
+
205
+ - [ ] Donde menciona "perfil tecnológico" y "bootstrapping", enlazar a `reference/bounded-contexts.md` y `reference/modular-monolith.md`
206
+
207
+ ### F3.4 — `hooks.md`
208
+
209
+ - [ ] Donde menciona "validación arquitectónica" y "pre-commit", enlazar a `reference/quench.md` y `reference/evolutionary-architecture.md`
210
+
211
+ ### F3.5 — `security-patterns.md`
212
+
213
+ - [ ] Donde menciona "middleware" y "AuthN/AuthZ", enlazar a `reference/api-design.md`
214
+
215
+ ### F3.6 — `testing-patterns.md`
216
+
217
+ - [ ] Donde menciona "tests de adapters" y "ACL", enlazar a `reference/anti-corruption-layer.md`
218
+
219
+ ---
220
+
221
+ ## F4 — Bidireccionalidad
222
+
223
+ Pares donde existe enlace A→B pero falta B→A.
224
+
225
+ ### Pares incompletos
226
+
227
+ | A | B | A→B | B→A | Acción |
228
+ |---|---|---|---|---|
229
+ | `bounded-contexts.md` | `anti-corruption-layer.md` | ✅ | ✅ | — |
230
+ | `bounded-contexts.md` | `modular-monolith.md` | ✅ | ✅ | — |
231
+ | `bounded-contexts.md` | `cqrs.md` | ✅ | ✅ | — |
232
+ | `bounded-contexts.md` | `sagas.md` | ✅ | ✅ | — |
233
+ | `modular-monolith.md` | `evolutionary-architecture.md` | ✅ | ✅ | — |
234
+ | `sagas.md` | `transactional-outbox.md` | ✅ | ✅ | — |
235
+ | `sagas.md` | `idempotency.md` | ✅ | ✅ | — |
236
+ | `idempotency.md` | `transactional-outbox.md` | ✅ | ✅ | — |
237
+ | `evol-arch.md` | `adr.md` | ✅ | ✅ | — |
238
+ | `api-design.md` | `api-versioning.md` | ✅ | ✅ | — |
239
+
240
+ - [x] **Verificar que todos los pares anteriores son bidireccionales.** Leer cada archivo y confirmar que si A enlaza a B, B también enlaza a A (o al menos a la categoría de A).
241
+
242
+ ---
243
+
244
+ ## Criterio de completitud (10/10)
245
+
246
+ - [x] **Cero enlaces rotos** — ✅
247
+ - [x] **Cero huérfanos** — ✅ (33/35 archivos tienen ≥1 enlace saliente a otro reference/; solo help.md y architectural-depth-checklist.md, ambos intencionales)
248
+ - [x] **Cero backlinks perdidos** — ✅ (todas las referencias estratégicas tienen backlinks)
249
+ - [x] **Formato consistente** — ✅ (28 archivos con `## Ver también`, 0 con inline `Ver también:`)
250
+ - [x] **Bidireccionalidad ≥ 90%** — ✅ (33/35 archivos tienen backlinks)
251
+
252
+ ---
253
+
254
+ ## Integración en SKILL.md
255
+
256
+ - [x] Añadir `reference/cohesion-checklist.md` al Module Index
@@ -0,0 +1,286 @@
1
+ # CQRS — Command Query Responsibility Segregation
2
+
3
+ CQRS separa las operaciones de escritura (commands) de las de lectura (queries) en modelos distintos. No es un patrón para todo CRUD. Se aplica cuando la demanda de lectura es significativamente distinta a la de escritura.
4
+
5
+ ---
6
+
7
+ ## Fundamentos
8
+
9
+ ### Command
10
+
11
+ - **Propósito**: cambiar el estado del sistema
12
+ - **Efectos**: side effects (escribir, publicar eventos, enviar emails)
13
+ - **Retorno**: void o ID del recurso creado
14
+ - **Validación**: reglas de negocio, invariantes, autorización
15
+ - **Modelo**: el modelo de dominio completo (entidades, aggregates, value objects)
16
+
17
+ ```ts
18
+ // commands/PlaceOrder.command.ts
19
+ export class PlaceOrderCommand {
20
+ constructor(
21
+ public readonly customerId: string,
22
+ public readonly items: { productId: string; quantity: number }[],
23
+ public readonly paymentMethodId: string,
24
+ ) {}
25
+ }
26
+ ```
27
+
28
+ ### Query
29
+
30
+ - **Propósito**: obtener datos sin modificar estado
31
+ - **Efectos**: ninguno (puro, idempotente)
32
+ - **Retorno**: DTOs planos, proyecciones desnormalizadas
33
+ - **Validación**: autorización, filtros
34
+ - **Modelo**: read model optimizado para consulta (puede diferir completamente del modelo de escritura)
35
+
36
+ ```ts
37
+ // queries/GetOrderSummary.query.ts
38
+ export class GetOrderSummaryQuery {
39
+ constructor(
40
+ public readonly orderId: string,
41
+ public readonly includeHistory?: boolean,
42
+ ) {}
43
+ }
44
+
45
+ // DTO de retorno (read model)
46
+ export type OrderSummaryDTO = {
47
+ orderId: string;
48
+ status: string;
49
+ total: number;
50
+ items: { name: string; quantity: number; price: number }[];
51
+ timeline: { status: string; at: string }[];
52
+ };
53
+ ```
54
+
55
+ ---
56
+
57
+ ## Cuándo Aplicar CQRS
58
+
59
+ ### Señales para aplicar CQRS
60
+
61
+ | Señal | Síntoma |
62
+ |---|---|
63
+ | **Modelo divergente** | La pantalla de detalle muestra datos agregados que no existen en el modelo de escritura |
64
+ | **Rendimiento asimétrico** | Las lecturas son 10x más frecuentes que las escrituras |
65
+ | **Optimización conflictiva** | Lo que optimiza escritura (normalización) empeora lectura (joins) |
66
+ | **Equipos separados** | El equipo que consume datos no es el mismo que los produce |
67
+ | **Múltiples representaciones** | Un mismo dato se muestra distinto en distintas pantallas |
68
+
69
+ ### Cuándo NO aplicar CQRS
70
+
71
+ | Situación | Razón |
72
+ |---|---|
73
+ | CRUD simple sin lógica | Un repository con métodos find/findAll cubre |
74
+ | Modelo de lectura idéntico al de escritura | Separar añade complejidad sin beneficio |
75
+ | Feature pequeña (< 3 use cases) | La complejidad de CQRS supera el beneficio |
76
+ | Sin problemas de performance | CQRS no es un patrón de performance por defecto |
77
+
78
+ **Regla práctica:** si el use case de lectura es `findById` y devuelve exactamente la entidad, no necesitas CQRS. Si necesitas JOINs entre 5 tablas, cálculos agregados, y formato distinto al del modelo de escritura, CQRS ayuda.
79
+
80
+ ---
81
+
82
+ ## Implementación en el Modelo de Forge
83
+
84
+ ### Estructura de directorios
85
+
86
+ ```
87
+ src/features/analytics/
88
+ domain/
89
+ events/
90
+ PageViewRecorded.event.ts
91
+ ReportGenerated.event.ts
92
+ application/
93
+ use-cases/ ← Commands (escritura)
94
+ RecordPageViewUseCase.ts
95
+ GenerateReportUseCase.ts
96
+ queries/ ← Queries (lectura separada)
97
+ GetDashboardStatsQuery.ts
98
+ GetUserActivityQuery.ts
99
+ mappers/
100
+ PageViewMapper.ts
101
+ adapters/
102
+ in/http/
103
+ v1/
104
+ commands/ ← Solo commands
105
+ RecordPageViewController.ts
106
+ queries/ ← Solo queries
107
+ GetDashboardStatsController.ts
108
+ out/
109
+ persistence/ ← Repository de escritura
110
+ PostgresPageViewRepository.ts
111
+ read/ ← Read-only repository
112
+ PostgresDashboardReadRepository.ts
113
+ RedisDashboardReadRepository.ts
114
+ ```
115
+
116
+ ### CQRS Parcial (misma BD, modelos separados)
117
+
118
+ El caso más común: commands y queries separados en la aplicación, misma base de datos.
119
+
120
+ ```ts
121
+ // Escritura: modelo de dominio completo
122
+ // domain/IPageViewRepository.ts
123
+ export interface IPageViewRepository {
124
+ save(event: PageViewEntity): Promise<void>;
125
+ findBySession(sessionId: string): Promise<PageViewEntity[]>;
126
+ }
127
+
128
+ // Lectura: read model desnormalizado
129
+ // application/queries/IDashboardReadRepository.ts
130
+ export interface IDashboardReadRepository {
131
+ getActiveUsers(since: Date): Promise<number>;
132
+ getTopPages(limit: number): Promise<{ path: string; views: number }[]>;
133
+ getConversionRate(funnel: string[]): Promise<number>;
134
+ }
135
+
136
+ // Implementación de lectura (puede usar queries SQL directas)
137
+ // adapters/out/read/PostgresDashboardReadRepository.ts
138
+ export class PostgresDashboardReadRepository
139
+ implements IDashboardReadRepository
140
+ {
141
+ constructor(private readonly db: PrismaClient) {}
142
+
143
+ async getActiveUsers(since: Date): Promise<number> {
144
+ const result = await this.db.$queryRaw<{ count: bigint }[]>`
145
+ SELECT COUNT(DISTINCT session_id) as count
146
+ FROM analytics.page_views
147
+ WHERE viewed_at >= ${since}
148
+ `;
149
+ return Number(result[0].count);
150
+ }
151
+ }
152
+ ```
153
+
154
+ ### CQRS Completo (read model separado)
155
+
156
+ Cuando el modelo de lectura está en una BD o cache distinta:
157
+
158
+ ```ts
159
+ // adapters/out/read/RedisDashboardReadRepository.ts
160
+ export class RedisDashboardReadRepository
161
+ implements IDashboardReadRepository
162
+ {
163
+ constructor(private readonly redis: Redis) {}
164
+
165
+ async getActiveUsers(since: Date): Promise<number> {
166
+ const cacheKey = `dashboard:active-users:${since.toISOString().slice(0, 13)}`;
167
+ const cached = await this.redis.get(cacheKey);
168
+ if (cached) return Number(cached);
169
+
170
+ // Si no está en cache, calcular (delegar a otro read repo)
171
+ throw new Error("CacheMiss");
172
+ }
173
+ }
174
+
175
+ // adapters/in/events/DashboardProjection.ts
176
+ // Escucha eventos de dominio y actualiza el read model
177
+ export class DashboardProjection {
178
+ constructor(private readonly redis: Redis) {}
179
+
180
+ async onPageViewRecorded(event: PageViewRecordedEvent): Promise<void> {
181
+ // Invalidar cache de dashboard
182
+ await this.redis.del(`dashboard:active-users:*`);
183
+ // Incrementar contador de página
184
+ await this.redis.hincrby("page:views", event.path, 1);
185
+ }
186
+ }
187
+ ```
188
+
189
+ ### Proyecciones (Materialized Views)
190
+
191
+ Cuando el read model se actualiza desde eventos de dominio:
192
+
193
+ ```ts
194
+ // La proyección escucha eventos y construye el read model
195
+ // adapters/in/events/OrderProjection.ts
196
+ export class OrderProjection {
197
+ constructor(
198
+ private readonly orderReadRepo: IOrderReadRepository,
199
+ private readonly eventBus: IEventBus,
200
+ ) {
201
+ this.eventBus.subscribe("OrderPlaced", this.onOrderPlaced.bind(this));
202
+ this.eventBus.subscribe("OrderShipped", this.onOrderShipped.bind(this));
203
+ }
204
+
205
+ async onOrderPlaced(event: OrderPlacedEvent): Promise<void> {
206
+ await this.orderReadRepo.upsert({
207
+ orderId: event.orderId,
208
+ status: "placed",
209
+ total: event.total,
210
+ itemCount: event.items.length,
211
+ placedAt: event.occurredAt,
212
+ timeline: [{ status: "placed", at: event.occurredAt }],
213
+ });
214
+ }
215
+
216
+ async onOrderShipped(event: OrderShippedEvent): Promise<void> {
217
+ await this.orderReadRepo.appendTimeline(
218
+ event.orderId,
219
+ { status: "shipped", at: event.occurredAt }
220
+ );
221
+ await this.orderReadRepo.updateStatus(event.orderId, "shipped");
222
+ }
223
+ }
224
+ ```
225
+
226
+ ---
227
+
228
+ ## Conexión con el Modelo de Forge
229
+
230
+ ### Reglas y CQRS
231
+
232
+ | Regla | Aplicación en CQRS |
233
+ |---|---|
234
+ | **R0** (cero lógica en controllers) | Los controllers de queries y commands solo parsean y delegan. La lógica de query vive en `application/queries/`. |
235
+ | **R1** (feature → infra) | Los read repositories están en `adapters/out/read/`. Siguen siendo adapters, no violan R1. |
236
+ | **R5** (domain → infra) | El read model es un DTO, no una entidad de dominio. No viola R5 porque no hay entidad de dominio en la query. |
237
+ | **R8** (cross-feature) | Si una query necesita datos de otra feature, usa shared contracts o eventos, nunca import directo. |
238
+
239
+ ### CQRS en templates de feature
240
+
241
+ ```
242
+ src/features/<name>/
243
+ application/
244
+ use-cases/ ← commands (escritura)
245
+ queries/ ← queries (lectura) — solo si aplica CQRS
246
+ mappers/
247
+ domain/
248
+ entities/
249
+ repositories/ ← interfaces de escritura
250
+ adapters/
251
+ out/
252
+ persistence/ ← implementación de escritura
253
+ read/ ← implementación de lectura — solo si aplica CQRS
254
+ ```
255
+
256
+ Para features CRUD simples, no crear `queries/` ni `read/`. Usar el repository de dominio para todo.
257
+
258
+ ---
259
+
260
+ ## Anti-patrones
261
+
262
+ | Anti-patrón | Problema | Solución |
263
+ |---|---|---|
264
+ | **CQRS Everywhere** | Separar commands/queries en cada CRUD. El 80% de las features no lo necesita. | Aplicar solo cuando el modelo de lectura difiere significativamente del de escritura. |
265
+ | **Query leak** | Poner lógica de negocio en la query (calcular descuentos, validar reglas). | Las queries devuelven datos. Las reglas de negocio se evalúan en los commands. |
266
+ | **Eventual consistency ignorada** | El read model se actualiza con eventos; un comando escribe y la siguiente lectura no ve el cambio. | Documentar la consistencia eventual. No ocultarla. Si el negocio exige consistencia inmediata, no usar CQRS completo. |
267
+ | **Read model sin índices** | El read model replica el modelo normalizado de escritura. No hay beneficio. | El read model debe estar desnormalizado y optimizado para las consultas reales. |
268
+ | **Proyecciones frágiles** | Una proyección falla y el read model queda corrupto. | Rebuild de proyecciones (reprocesar eventos desde el principio) + monitoreo de lag. |
269
+
270
+ ---
271
+
272
+ ## Conexión con Forge
273
+
274
+ | Comando | Acción |
275
+ |---|---|
276
+ | `forge cast` | Durante el shape, decidir si la feature necesita CQRS |
277
+ | `forge inspect` | Reporta features con lecturas costosas que se beneficiarían de CQRS |
278
+ | `forge reforge` | Migra una feature de repository único a CQRS parcial/completo |
279
+ | `forge quench` | Verifica que las queries no violan reglas de dominio |
280
+
281
+ ## Ver también
282
+
283
+ - `reference/bounded-contexts.md` — contexts donde CQRS aplica
284
+ - `reference/data-patterns.md` — repository, unit of work, event sourcing
285
+ - `reference/events.md` — eventos como fuente de proyecciones
286
+ - `reference/sagas.md` — sagas con CQRS
@@ -84,3 +84,9 @@ features/<name>/
84
84
  - Transacciones en el Unit of Work, no en el use case ni en el repositorio individual
85
85
  - CQRS no significa event sourcing; son patrones independientes
86
86
  - Event sourcing sin snapshotting es inviable a escala
87
+
88
+ ## Ver también
89
+
90
+ - `reference/cqrs.md` — command/query separation, read models, proyecciones
91
+ - `reference/events.md` — eventos de dominio, emisión, event bus
92
+ - `reference/anti-corruption-layer.md` — mapeo entre modelos de datos
@@ -48,3 +48,9 @@ container.registerSingleton<IUserRepository>("IUserRepository", PostgresUserRepo
48
48
  - Evitar `container.resolve()` fuera del Composition Root
49
49
  - Usar tokens (strings o símbolos) para identificar dependencias
50
50
  - Testear use cases con mocks manuales sin necesidad del contenedor
51
+
52
+ ## Ver también
53
+
54
+ - `reference/temper.md` — endurecimiento de DI (complemento directo)
55
+ - `reference/patterns.md` — naming y convenciones de contenedor DI
56
+ - `reference/testing-patterns.md` — mocks y DI testing
@@ -63,3 +63,8 @@ async getUser(req, res, next) {
63
63
  - Error handler centralizado en platform/http/ para errores no capturados
64
64
  - Loggear errores en el adapter, no en el dominio
65
65
  - Códigos de error consistentes: `DOMAIN_ENTITY_NOT_FOUND`, `VALIDATION_INVALID_EMAIL`
66
+
67
+ ## Ver también
68
+
69
+ - `reference/api-design.md` — errores normalizados en respuestas HTTP
70
+ - `reference/testing-patterns.md` — tests de errores y mappers
@@ -93,3 +93,11 @@ Para flujos multi-paso que abarcan múltiples features.
93
93
  - Eventos sin lógica: son datos, no comportamiento.
94
94
  - Para integración entre features: el feature A emite evento, el feature B lo escucha. Nunca import directo.
95
95
  - Outbox para garantía de entrega; in-memory event bus solo para tests o monolitos pequeños.
96
+
97
+ ## Ver también
98
+
99
+ - `reference/sagas.md` — coreografía, orquestación, compensaciones
100
+ - `reference/transactional-outbox.md` — entrega confiable de eventos, relayer, DLQ
101
+ - `reference/idempotency.md` — deduplicación y retry seguro en handlers
102
+ - `reference/cqrs.md` — command/query separation y proyecciones
103
+ - `reference/anti-corruption-layer.md` — traducción de eventos entre contexts