@ronaldjdevfs/forge 1.2.0 → 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 (74) hide show
  1. package/README.md +36 -21
  2. package/package.json +7 -2
  3. package/skills/forge/SKILL.md +56 -122
  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 +86 -0
  45. package/skills/forge/scripts/forgeSentinel.mjs +184 -0
  46. package/skills/forge/scripts/forgeSmith-admin.mjs +104 -0
  47. package/skills/forge/scripts/forgeSmith.mjs +164 -0
  48. package/skills/forge/scripts/graph.mjs +65 -9
  49. package/skills/forge/scripts/hook.mjs +2 -2
  50. package/skills/forge/scripts/inspect.mjs +56 -48
  51. package/skills/forge/scripts/parse-imports.mjs +0 -2
  52. package/skills/forge/scripts/pin.mjs +10 -3
  53. package/skills/forge/scripts/posttool.mjs +2 -2
  54. package/skills/forge/scripts/recommendation-engine.mjs +125 -0
  55. package/skills/forge/scripts/rollback.mjs +5 -3
  56. package/skills/forge/templates/agents/SKILL.md.template +283 -0
  57. package/skills/forge/templates/agents/agents/hooks.json +18 -0
  58. package/skills/forge/templates/agents/claude/CLAUDE.md +17 -16
  59. package/skills/forge/templates/agents/claude/settings.local.json +18 -0
  60. package/skills/forge/templates/agents/codex/hooks.json +18 -0
  61. package/skills/forge/templates/agents/cursor/.cursorrules +22 -5
  62. package/skills/forge/templates/agents/cursor/hooks.json +11 -0
  63. package/skills/forge/templates/agents/gemini/SKILL.md +13 -0
  64. package/skills/forge/templates/feature/acl-gateway.ts.md +52 -0
  65. package/skills/forge/templates/feature/acl-repository.ts.md +36 -0
  66. package/skills/forge/templates/feature/acl-translator.ts.md +24 -0
  67. package/skills/forge/templates/feature/cqrs-query.ts.md +40 -0
  68. package/skills/forge/templates/feature/outbox-repository.ts.md +36 -0
  69. package/skills/forge/templates/feature/saga-orchestrator.ts.md +72 -0
  70. package/skills/forge/templates/platform/outbox-relayer.ts.md +80 -0
  71. package/skills/forge/tests/core.test.mjs +284 -0
  72. package/src/agents.mjs +35 -2
  73. package/src/cli.js +112 -39
  74. package/src/wizard.mjs +142 -90
@@ -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
@@ -0,0 +1,300 @@
1
+ # Evolutionary Architecture
2
+
3
+ La arquitectura no es un diseño inicial que se congela. Es una estructura que evoluciona con el conocimiento del dominio, el tamaño del equipo y las restricciones del negocio. Forge está diseñado para guiar esa evolución sin reescrituras.
4
+
5
+ ---
6
+
7
+ ## Definición
8
+
9
+ Una **arquitectura evolutiva** es aquella en la que los cambios significativos pueden realizarse de forma incremental, guiados por **fitness functions** que verifican que las propiedades arquitectónicas se mantienen.
10
+
11
+ **Propiedades de una arquitectura evolutiva:**
12
+ - **Incremental**: los cambios grandes se dividen en pasos pequeños y reversibles
13
+ - **Guiada por métricas**: se sabe si un cambio mejora o degrada la arquitectura
14
+ - **Fitness functions automatizadas**: las propiedades se verifican en CI
15
+ - **Sin big-bang rewrites**: nunca se reescribe desde cero
16
+
17
+ ---
18
+
19
+ ## Fitness Functions
20
+
21
+ Una **fitness function** es un test automatizado que valida una característica arquitectónica. Forge ya implementa varias:
22
+
23
+ ### Built-in (R1-R9)
24
+
25
+ Las 9 reglas de Forge son fitness functions:
26
+
27
+ ```ts
28
+ // scripts/registry/rules.mjs (conceptual)
29
+ const rules = {
30
+ R1: {
31
+ name: "feature → infra prohibited",
32
+ severity: "CRITICAL",
33
+ check: (edge) =>
34
+ edge.from.type === "feature" && edge.to.type === "infra",
35
+ },
36
+ R8: {
37
+ name: "cross-feature direct import prohibited",
38
+ severity: "ERROR",
39
+ check: (edge) =>
40
+ edge.from.type === "feature" && edge.to.type === "feature",
41
+ },
42
+ // R2-R7, R9 con estructura similar
43
+ };
44
+ ```
45
+
46
+ Estas fitness functions se ejecutan en:
47
+ - `node scripts/detect.mjs` — detección local
48
+ - `forge quench` — validación completa
49
+ - PostToolUse hook — después de cada escritura del agente
50
+
51
+ ### Custom Fitness Functions
52
+
53
+ Los usuarios pueden registrar sus propias funciones:
54
+
55
+ ```ts
56
+ // scripts/registry/rules.mjs
57
+ import { registerRule } from "./registry/rules.mjs";
58
+
59
+ registerRule({
60
+ id: "CUSTOM_01",
61
+ name: "no console.log in use-cases",
62
+ severity: "WARNING",
63
+ check: ({ filePath, content }) =>
64
+ filePath.includes("application/use-cases") && content.includes("console.log"),
65
+ description: "Los casos de uso no deben tener console.log. Usar logger inyectado.",
66
+ });
67
+ ```
68
+
69
+ ### Tipos de Fitness Functions
70
+
71
+ | Tipo | Ejecución | Ejemplo |
72
+ |---|---|---|
73
+ | **Estática** | Lint/build | `detect.mjs` analiza imports |
74
+ | **Dinámica** | Runtime | Verificar que event bus no pierde eventos |
75
+ | **Benchmark** | CI periódico | Tiempo de respuesta de queries < 200ms |
76
+ | **Trigger-based** | Evento (PR, deploy) | No hay imports directos entre features |
77
+ | **Contrato** | CI multi-servicio | Las APIs son compatibles con versiones anteriores |
78
+
79
+ ### Integración en CI
80
+
81
+ ```yaml
82
+ # .github/workflows/forge-fitness.yml
83
+ jobs:
84
+ forge-quench:
85
+ runs-on: ubuntu-latest
86
+ steps:
87
+ - uses: actions/checkout@v4
88
+ - run: node .opencode/skills/forge/scripts/detect.mjs
89
+ env:
90
+ FORGE_STRICT: "true"
91
+ - run: node .opencode/skills/forge/scripts/chain.mjs --json
92
+ ```
93
+
94
+ ---
95
+
96
+ ## Guided Change
97
+
98
+ El flujo de cambio guiado de Forge asegura que cada modificación arquitectónica es segura:
99
+
100
+ ### 1. Estado actual (before)
101
+
102
+ ```bash
103
+ node scripts/inspect.mjs --json
104
+ # Score: 85
105
+ # Violaciones: 2 WARNING (R9 borderline, naming)
106
+ ```
107
+
108
+ ### 2. Proponer cambio
109
+
110
+ ```bash
111
+ forge reforge --move features/old-payments to features/payments/v2
112
+ # Generate plan: split feature, add ACL, migrate use-cases
113
+ ```
114
+
115
+ ### 3. Verificar antes de aplicar
116
+
117
+ ```bash
118
+ forge quench --diff
119
+ # Simula el cambio y reporta impacto en score
120
+ ```
121
+
122
+ ### 4. Ejecutar con rollback
123
+
124
+ ```bash
125
+ forge reforge --apply
126
+ # Guarda backup en .forge/backup/
127
+ # Si falla: forge rollback --last
128
+ ```
129
+
130
+ ### 5. Estado actual (after)
131
+
132
+ ```bash
133
+ node scripts/inspect.mjs --json
134
+ # Score: 92 (+7)
135
+ # Violaciones: 0
136
+ ```
137
+
138
+ Si el score baja, el cambio se rechaza automáticamente (configurable con `FORGE_ALLOW_DEGRADE=true`).
139
+
140
+ ---
141
+
142
+ ## Evolución Típica de un Proyecto
143
+
144
+ ### Fase 1: Prototipo / MVP
145
+
146
+ ```
147
+ 10 features, 1 equipo, monolith
148
+ Reglas: todas activas
149
+ Foco: velocidad de entrega
150
+ Tolerancia: alta para violaciones temporales
151
+ ```
152
+
153
+ ```bash
154
+ forge quench --allow-warnings
155
+ # Reporta violaciones pero no bloquea
156
+ ```
157
+
158
+ ### Fase 2: Crecimiento
159
+
160
+ ```
161
+ 20 features, 2 equipos, monolith modular
162
+ Reglas: estrictas (R1-R9 bloquean)
163
+ Foco: boundaries
164
+ ```
165
+
166
+ ```bash
167
+ forge quench --strict
168
+ # Las violaciones ERROR bloquean el PR
169
+ ```
170
+
171
+ ### Fase 3: Escalamiento
172
+
173
+ ```
174
+ 40 features, 3+ equipos, modular → microservicios
175
+ Reglas: R1-R9 + custom (CQRS, outbox, SLA)
176
+ Foco: autonomía de equipos
177
+ ```
178
+
179
+ ```bash
180
+ forge inspect --extended
181
+ # Incluye fitness functions custom
182
+ ```
183
+
184
+ ### Fase 4: Madurez
185
+
186
+ ```
187
+ N features, equipos autónomos, servicios independientes
188
+ Reglas: fitness functions distribuidas
189
+ Foco: evolución continua sin regresión
190
+ ```
191
+
192
+ ---
193
+
194
+ ## Smallest Viable Change
195
+
196
+ Cada cambio arquitectónico debe ser el **cambio más pequeño que mejora la arquitectura sin romper funcionalidad**.
197
+
198
+ | Cambio grande (evitar) | Cambio pequeño (preferir) |
199
+ |---|---|
200
+ | Extraer 3 features como microservicios a la vez | Extraer 1 feature, verificar, repetir |
201
+ | Reescribir todo el ORM | Migrar un repositorio por PR |
202
+ | Refactorizar "toda la capa de aplicación" | Refactorizar un caso de uso, probar, continuar |
203
+ | Cambiar de framework | Aislar framework tras interfaces primero, luego reemplazar |
204
+
205
+ ### Patrón: Scaffolding antes de Feature
206
+
207
+ Antes de implementar una feature completa, crear la estructura del feature y verificar que no viola reglas:
208
+
209
+ ```bash
210
+ # Fase 1: scaffold
211
+ mkdir -p src/features/payments/{domain,application/use-cases,adapters/in/http,adapters/out/persistence}
212
+ touch src/features/payments/domain/PaymentEntity.ts
213
+ touch src/features/payments/domain/IPaymentRepository.ts
214
+
215
+ # Fase 2: verify
216
+ forge quench
217
+ # Score: 100 (aún sin implementar, pero los boundaries son correctos)
218
+
219
+ # Fase 3: implementar use-case
220
+ touch src/features/payments/application/use-cases/ProcessPaymentUseCase.ts
221
+
222
+ # Fase 4: verify again
223
+ forge quench
224
+ # Score: 100 (el use-case solo importa de domain y shared)
225
+ ```
226
+
227
+ ---
228
+
229
+ ## Evolución de Boundaries
230
+
231
+ ### Partir un feature
232
+
233
+ ```bash
234
+ # Antes: features/catalog (15k líneas, toca todo)
235
+ # Después: features/catalog (core) + features/search (búsqueda)
236
+
237
+ forge cast search --from catalog
238
+ # 1. Crea features/search con estructura completa
239
+ # 2. Mueve SearchService y SearchIndexer de catalog a search
240
+ # 3. Crea contratos en shared/contracts/catalog/ para que search consuma datos
241
+ # 4. Verifica que no quedan imports de catalog → search ni viceversa
242
+ ```
243
+
244
+ ### Fusionar features
245
+
246
+ ```bash
247
+ # Antes: features/standard-checkout + features/express-checkout
248
+ # (80% del código duplicado entre ambos)
249
+
250
+ forge reforge --merge features/express-checkout into features/checkout
251
+ # 1. Mueve el código único de express a checkout
252
+ # 2. Parametriza el checkout con "mode: standard | express"
253
+ # 3. Elimina features/express-checkout
254
+ # 4. Verifica que nada importaba de express-checkout
255
+ ```
256
+
257
+ ### Mover un shared kernel a package
258
+
259
+ ```bash
260
+ # Antes: src/shared/contracts/ (referencia local)
261
+ # Después: @company/contracts (npm package)
262
+
263
+ forge reforge --publish shared/contracts as @company/contracts
264
+ # 1. Extrae contracts/ a packages/contracts/
265
+ # 2. Configura build con tsc
266
+ # 3. Actualiza imports en todas las features
267
+ # 4. Verifica con detect.mjs que los nuevos imports son válidos
268
+ ```
269
+
270
+ ---
271
+
272
+ ## Anti-patrones
273
+
274
+ | Anti-patrón | Problema | Solución |
275
+ |---|---|---|
276
+ | **Big-Bang Rewrite** | Se reescribe todo. El sistema legacy se congela. El rewrite nunca alcanza el feature parity. | Strangler Fig + ACL. Migrar feature por feature. |
277
+ | **Frozen Architecture** | "No podemos cambiar la estructura ahora". La arquitectura se vuelve un impedimento. | Fitness functions + guided change. El cambio seguro está soportado por diseño. |
278
+ | **Analysis Paralysis** | Demasiado tiempo diseñando, poco tiempo implementando. | Smallest viable change. El diseño emerge, no se predice. |
279
+ | **Tech Debt sin métrica** | Se acumula deuda sin saber cuánta ni dónde. | `forge inspect` da score numérico. La deuda se mide, no se estima. |
280
+ | **Rewrite por moda** | "Pasamos a microservicios porque es moderno". | `reference/modular-monolith.md` — evaluar antes de partir. |
281
+ | **Golden Hammer** | Forzar CQRS, Event Sourcing o Hexagonal en features que no lo necesitan. | Cada referencia tiene "cuándo usarlo". Si no aplica, no lo fuerces. |
282
+
283
+ ---
284
+
285
+ ## Conexión con Forge
286
+
287
+ | Comando | Acción |
288
+ |---|---|
289
+ | `forge inspect` | Score + violaciones + fitness functions |
290
+ | `forge quench` | Ejecuta fitness functions en el código actual |
291
+ | `forge reforge` | Cambio arquitectónico guiado con verificación |
292
+ | `forge relocate` | Migración incremental con rollback |
293
+ | `forge chain` | Verifica que el grafo de dependencias evoluciona saludablemente |
294
+ | `forge graph` | Visualiza la evolución del grafo arquitectónico |
295
+
296
+ ## Ver también
297
+
298
+ - `reference/adr.md` — ADRs como registro de cambios evolutivos
299
+ - `reference/principles.md` — principios que las fitness functions protegen
300
+ - `reference/modular-monolith.md` — evolución de monolith a microservicios
@@ -42,3 +42,10 @@ Inicializa un proyecto para trabajar con Forge como Backend Architecture Operati
42
42
  | Platform layer ausente | SUGGESTION |
43
43
  | Dependencias faltantes | WARNING |
44
44
  | Ownership con huérfanos | WARNING |
45
+
46
+ ## Ver también
47
+
48
+ - `reference/bounded-contexts.md` — identificación de contexts al inicializar
49
+ - `reference/modular-monolith.md` — decisión de estructura al iniciar proyecto
50
+ - `reference/principles.md` — principios que guían la inicialización
51
+ - `reference/evolutionary-architecture.md` — bootstrap como primer paso evolutivo
@@ -60,3 +60,9 @@ Las reglas ignoradas se almacenan en `.forge/hooks-ignore.json`:
60
60
 
61
61
  Cuando una regla está ignorada, el hook no bloquea el commit por
62
62
  violaciones de esa regla, aunque el detector sigue reportándolas.
63
+
64
+ ## Ver también
65
+
66
+ - `reference/quench.md` — validación que el hook ejecuta en pre-commit
67
+ - `reference/evolutionary-architecture.md` — fitness functions como hook
68
+ - `reference/adr.md` — ADRs como insumo para validación en hook