@polymorphism-tech/morph-spec 4.8.1 → 4.8.4
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/README.md +2 -2
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/hooks/dev/guard-version-numbers.js +1 -1
- package/framework/skills/level-1-workflows/phase-clarify/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/phase-codebase-analysis/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/phase-design/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/phase-implement/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/phase-setup/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/phase-tasks/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/phase-uiux/SKILL.md +1 -1
- package/package.json +4 -4
- package/.morph/analytics/threads-log.jsonl +0 -54
- package/.morph/state.json +0 -198
- package/docs/ARCHITECTURE.md +0 -328
- package/docs/COMMAND-FLOWS.md +0 -398
- package/docs/plans/2026-02-22-claude-docs-morph-alignment-analysis.md +0 -514
- package/docs/plans/2026-02-22-claude-settings.md +0 -517
- package/docs/plans/2026-02-22-morph-cc-alignment-impl.md +0 -730
- package/docs/plans/2026-02-22-morph-spec-next.md +0 -480
- package/docs/plans/2026-02-22-native-alignment-design.md +0 -201
- package/docs/plans/2026-02-22-native-alignment-impl.md +0 -927
- package/docs/plans/2026-02-22-native-enrichment-design.md +0 -246
- package/docs/plans/2026-02-22-native-enrichment.md +0 -737
- package/docs/plans/2026-02-23-ddd-architecture-refactor.md +0 -1155
- package/docs/plans/2026-02-23-ddd-nextsteps.md +0 -684
- package/docs/plans/2026-02-23-infra-architect-refactor.md +0 -439
- package/docs/plans/2026-02-23-nextjs-code-review-design.md +0 -157
- package/docs/plans/2026-02-23-nextjs-code-review-impl.md +0 -1256
- package/docs/plans/2026-02-23-nextjs-standards-design.md +0 -150
- package/docs/plans/2026-02-23-nextjs-standards-impl.md +0 -1848
- package/docs/plans/2026-02-24-cli-radical-simplification.md +0 -592
- package/docs/plans/2026-02-24-framework-failure-points.md +0 -125
- package/docs/plans/2026-02-24-morph-init-design.md +0 -337
- package/docs/plans/2026-02-24-morph-init-impl.md +0 -1269
- package/docs/plans/2026-02-24-tutorial-command-design.md +0 -71
- package/docs/plans/2026-02-24-tutorial-command.md +0 -298
- package/scripts/bump-version.js +0 -248
- package/scripts/generate-refs.js +0 -336
- package/scripts/generate-standards-registry.js +0 -44
- package/scripts/install-dev-hooks.js +0 -138
- package/scripts/scan-nextjs.mjs +0 -169
- package/scripts/validate-real.mjs +0 -255
|
@@ -1,1155 +0,0 @@
|
|
|
1
|
-
# DDD Architecture Refactor Implementation Plan
|
|
2
|
-
|
|
3
|
-
**Status:** COMPLETE
|
|
4
|
-
|
|
5
|
-
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
|
6
|
-
|
|
7
|
-
**Goal:** Integrar DDD tático de forma calibrada ao MORPH-SPEC — sem overengineering. 3 níveis de complexidade, detecção automática na fase de design, domain-architect como Tier 2 líder de modelagem de domínio.
|
|
8
|
-
|
|
9
|
-
**Architecture:** Mantém Clean Architecture como estrutura de camadas. Adiciona DDD tático (Aggregates, Value Objects, Domain Events) como padrão obrigatório calibrado por nível. O `domain-architect` (Tier 2) detecta o nível correto antes de gerar `contracts.cs`. Nível 1 = CRUD simples. Nível 2 = regras de negócio + Aggregate. Nível 3 = múltiplos domínios distintos (opt-in explícito).
|
|
10
|
-
|
|
11
|
-
**Tech Stack:** Node.js (ESM), Handlebars v2.0, `node:test`, C# templates (não compilados), Markdown.
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
## Contexto Importante
|
|
16
|
-
|
|
17
|
-
- `framework/agents.json` — 38 agentes, 4 tiers. `ddd-expert` está em Tier 3 sob `dotnet-senior`
|
|
18
|
-
- `framework/templates/code/dotnet/contracts/contracts.cs` — template único flat (domain entity **comentado**)
|
|
19
|
-
- `framework/templates/docs/spec.md` — sem seções de DDD ou complexidade de domínio
|
|
20
|
-
- `framework/skills/level-1-workflows/phase-design/SKILL.md` — sem detecção de nível de domínio
|
|
21
|
-
- `framework/standards/architecture/ddd/` — 3 arquivos existem (aggregates, entities, value-objects) mas não são referenciados na fase de design
|
|
22
|
-
- `test/commands/doctor.test.js` — valida tier sums; Tier 2 hoje = 3 agentes
|
|
23
|
-
- Standards Registry: `framework/standards/STANDARDS.json` — 74 entradas, regenerar após novos standards
|
|
24
|
-
|
|
25
|
-
---
|
|
26
|
-
|
|
27
|
-
## Parte A — Standards (base de tudo)
|
|
28
|
-
|
|
29
|
-
### Task 1: Criar `complexity-levels.md`
|
|
30
|
-
|
|
31
|
-
**Files:**
|
|
32
|
-
- Create: `framework/standards/architecture/ddd/complexity-levels.md`
|
|
33
|
-
|
|
34
|
-
**Step 1: Escrever o standard**
|
|
35
|
-
|
|
36
|
-
```markdown
|
|
37
|
-
# DDD Complexity Levels
|
|
38
|
-
|
|
39
|
-
> **Scope:** universal
|
|
40
|
-
> **Layer:** 2 (load when: ddd, domain, aggregate, entity, bounded context)
|
|
41
|
-
> **Keywords:** ddd, complexity, aggregate, crud, bounded-context, domain
|
|
42
|
-
|
|
43
|
-
Define o nível de complexidade de domínio para cada feature. O nível determina qual
|
|
44
|
-
template de contracts.cs será usado e quais padrões DDD são mandatórios.
|
|
45
|
-
|
|
46
|
-
---
|
|
47
|
-
|
|
48
|
-
## Os 3 Níveis
|
|
49
|
-
|
|
50
|
-
### Nível 1 — CRUD
|
|
51
|
-
|
|
52
|
-
**Quando usar:**
|
|
53
|
-
- Feature é essencialmente Create/Read/Update/Delete
|
|
54
|
-
- Entidade não tem regras de negócio (apenas validações de campos)
|
|
55
|
-
- Nenhum outro domínio precisa reagir às mudanças desta entidade
|
|
56
|
-
- Exemplos: `Category`, `Tag`, `UserProfile`, `Notification`
|
|
57
|
-
|
|
58
|
-
**O que gerar:**
|
|
59
|
-
- `Entity` simples (sem AggregateRoot)
|
|
60
|
-
- Service interface CRUD
|
|
61
|
-
- DTOs (record)
|
|
62
|
-
- Repository interface básica
|
|
63
|
-
- **SEM** Domain Events, **SEM** Value Objects (a menos que óbvio), **SEM** CQRS
|
|
64
|
-
|
|
65
|
-
---
|
|
66
|
-
|
|
67
|
-
### Nível 2 — Business Logic
|
|
68
|
-
|
|
69
|
-
**Quando usar:**
|
|
70
|
-
- Entidade tem estados com transições controladas por regras (ex: Draft → Confirmed → Shipped)
|
|
71
|
-
- Invariants existem: "só pode cancelar se estiver Ativo"
|
|
72
|
-
- Cálculos derivados (Total, Saldo, Desconto)
|
|
73
|
-
- Outros módulos precisam reagir a mudanças desta entidade
|
|
74
|
-
- Exemplos: `Order`, `Subscription`, `Invoice`, `Project`, `Ticket`
|
|
75
|
-
|
|
76
|
-
**O que gerar:**
|
|
77
|
-
- `AggregateRoot` com factory method estático
|
|
78
|
-
- Value Objects para tipos com regras (Money, Email, PhoneNumber)
|
|
79
|
-
- Domain Events para mudanças de estado relevantes
|
|
80
|
-
- Commands + Queries (CQRS com MediatR)
|
|
81
|
-
- Repository interface por aggregate root
|
|
82
|
-
- **SEM** Bounded Context isolation (módulos se comunicam diretamente)
|
|
83
|
-
|
|
84
|
-
---
|
|
85
|
-
|
|
86
|
-
### Nível 3 — Bounded Context
|
|
87
|
-
|
|
88
|
-
**Quando usar (opt-in explícito apenas):**
|
|
89
|
-
- Sistema tem 3+ domínios distintos onde o mesmo conceito tem modelos diferentes
|
|
90
|
-
- Exemplo: "Customer" em Billing ≠ "Customer" em Support
|
|
91
|
-
- Times distintos vão trabalhar em cada domínio
|
|
92
|
-
- Domínios precisam evoluir independentemente
|
|
93
|
-
- Exemplos: plataforma multi-produto, marketplace, ERP
|
|
94
|
-
|
|
95
|
-
**O que gerar:**
|
|
96
|
-
- Tudo do Nível 2 +
|
|
97
|
-
- Pasta `{BoundedContext}/` em Domain + Application
|
|
98
|
-
- Domain Events para comunicação cross-BC (via MediatR/Service Bus)
|
|
99
|
-
- Referências cross-BC por ID (nunca navigation properties)
|
|
100
|
-
- Glossário de Linguagem Ubíqua por BC
|
|
101
|
-
|
|
102
|
-
**⚠️ Não use Nível 3 por default.** Declare explicitamente na proposta.
|
|
103
|
-
|
|
104
|
-
---
|
|
105
|
-
|
|
106
|
-
## Como Detectar o Nível (fase de design)
|
|
107
|
-
|
|
108
|
-
O `domain-architect` analisa a proposta e responde:
|
|
109
|
-
|
|
110
|
-
```
|
|
111
|
-
1. A entidade principal tem estados com transições? → Sim = Nível 2+
|
|
112
|
-
2. Existem invariants de negócio? → Sim = Nível 2+
|
|
113
|
-
3. Outros domínios precisam reagir a mudanças? → Sim = Nível 2+
|
|
114
|
-
4. Diferentes contextos usam modelos diferentes do mesmo conceito? → Sim = Nível 3
|
|
115
|
-
5. O usuário declarou explicitamente Bounded Context? → Sim = Nível 3
|
|
116
|
-
Se nenhuma das anteriores: Nível 1
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
---
|
|
120
|
-
|
|
121
|
-
## Anti-Patterns por Nível
|
|
122
|
-
|
|
123
|
-
| Anti-Pattern | Nível Afetado | Correção |
|
|
124
|
-
|---|---|---|
|
|
125
|
-
| AggregateRoot para CRUD simples | Nível 1 desnecessário | Use Entity simples |
|
|
126
|
-
| Domain Events sem consumidores | Nível 2 excessivo | Remova ou use Nível 1 |
|
|
127
|
-
| Bounded Context para app single-domain | Nível 3 prematuro | Use Nível 2 |
|
|
128
|
-
| Entidade anêmica com regras no Service | Todos | Mova regras para o Aggregate |
|
|
129
|
-
| Value Objects para todo primitivo | Nível 2 excessivo | Só quando há regras/validação |
|
|
130
|
-
|
|
131
|
-
---
|
|
132
|
-
|
|
133
|
-
*MORPH-SPEC by Polymorphism Tech*
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
**Step 2: Verificar que o arquivo foi criado corretamente**
|
|
137
|
-
|
|
138
|
-
```bash
|
|
139
|
-
ls framework/standards/architecture/ddd/
|
|
140
|
-
```
|
|
141
|
-
Esperado: `aggregates.md`, `complexity-levels.md`, `entities.md`, `value-objects.md`
|
|
142
|
-
|
|
143
|
-
**Step 3: Commit**
|
|
144
|
-
|
|
145
|
-
```bash
|
|
146
|
-
git add framework/standards/architecture/ddd/complexity-levels.md
|
|
147
|
-
git commit -m "feat(standards): add DDD complexity levels — Level 1 CRUD, Level 2 Aggregate, Level 3 BC"
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
---
|
|
151
|
-
|
|
152
|
-
### Task 2: Criar `bounded-contexts.md`
|
|
153
|
-
|
|
154
|
-
**Files:**
|
|
155
|
-
- Create: `framework/standards/architecture/ddd/bounded-contexts.md`
|
|
156
|
-
|
|
157
|
-
**Step 1: Escrever o standard**
|
|
158
|
-
|
|
159
|
-
```markdown
|
|
160
|
-
# DDD Bounded Contexts
|
|
161
|
-
|
|
162
|
-
> **Scope:** universal
|
|
163
|
-
> **Layer:** 2 (load when: bounded context, bc, multi-domain, context map)
|
|
164
|
-
> **Keywords:** bounded-context, ddd, domain, ubiquitous-language, context-map
|
|
165
|
-
|
|
166
|
-
Bounded Context (BC) é a fronteira onde um modelo de domínio tem significado único.
|
|
167
|
-
Use apenas em sistemas com múltiplos domínios distintos (Nível 3 de complexidade).
|
|
168
|
-
|
|
169
|
-
---
|
|
170
|
-
|
|
171
|
-
## Estrutura de Pastas (Nível 3)
|
|
172
|
-
|
|
173
|
-
```
|
|
174
|
-
src/{App}.Domain/
|
|
175
|
-
Billing/ ← Bounded Context
|
|
176
|
-
Aggregates/
|
|
177
|
-
Subscription.cs
|
|
178
|
-
Invoice.cs
|
|
179
|
-
ValueObjects/
|
|
180
|
-
PlanTier.cs
|
|
181
|
-
BillingCycle.cs
|
|
182
|
-
Events/
|
|
183
|
-
SubscriptionCreatedEvent.cs
|
|
184
|
-
SubscriptionCancelledEvent.cs
|
|
185
|
-
Exceptions/
|
|
186
|
-
SubscriptionNotFoundException.cs
|
|
187
|
-
|
|
188
|
-
Orders/ ← Bounded Context
|
|
189
|
-
Aggregates/
|
|
190
|
-
Order.cs
|
|
191
|
-
ValueObjects/
|
|
192
|
-
Money.cs
|
|
193
|
-
Events/
|
|
194
|
-
OrderConfirmedEvent.cs
|
|
195
|
-
|
|
196
|
-
src/{App}.Application/
|
|
197
|
-
Billing/ ← espelha Domain
|
|
198
|
-
Commands/
|
|
199
|
-
CreateSubscriptionCommand.cs
|
|
200
|
-
Queries/
|
|
201
|
-
GetActiveSubscriptionQuery.cs
|
|
202
|
-
Handlers/
|
|
203
|
-
CreateSubscriptionHandler.cs
|
|
204
|
-
|
|
205
|
-
src/{App}.Infrastructure/
|
|
206
|
-
Billing/ ← espelha Application
|
|
207
|
-
Repositories/
|
|
208
|
-
SubscriptionRepository.cs
|
|
209
|
-
Configurations/
|
|
210
|
-
SubscriptionConfiguration.cs
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
---
|
|
214
|
-
|
|
215
|
-
## Regras de Comunicação Cross-BC
|
|
216
|
-
|
|
217
|
-
```csharp
|
|
218
|
-
// ✅ CORRETO: referenciar outro BC por ID
|
|
219
|
-
public class Order : AggregateRoot
|
|
220
|
-
{
|
|
221
|
-
public Guid CustomerId { get; private set; } // ID, não navigation property
|
|
222
|
-
// NÃO: public Customer Customer { get; } // cross-BC navigation PROIBIDO
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// ✅ CORRETO: comunicação via Domain Events (MediatR ou Service Bus)
|
|
226
|
-
public class OrderConfirmedHandler : INotificationHandler<OrderConfirmedEvent>
|
|
227
|
-
{
|
|
228
|
-
// Billing BC reage ao evento do Orders BC
|
|
229
|
-
public async Task Handle(OrderConfirmedEvent notification, CancellationToken ct)
|
|
230
|
-
{
|
|
231
|
-
await _billingService.ChargeForOrderAsync(notification.OrderId, ct);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// ❌ ERRADO: acesso direto ao repositório de outro BC
|
|
236
|
-
public class OrderService
|
|
237
|
-
{
|
|
238
|
-
private readonly ICustomerRepository _customerRepo; // cross-BC — PROIBIDO
|
|
239
|
-
}
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
---
|
|
243
|
-
|
|
244
|
-
## Linguagem Ubíqua por BC
|
|
245
|
-
|
|
246
|
-
Cada BC define seu próprio glossário. O mesmo termo pode ter significados diferentes:
|
|
247
|
-
|
|
248
|
-
| Termo | BC: Orders | BC: Support |
|
|
249
|
-
|-------|-----------|-------------|
|
|
250
|
-
| `Customer` | Quem fez um pedido, tem `Orders[]` | Quem abriu ticket, tem `SLA` e `Priority` |
|
|
251
|
-
| `Status` | `Draft/Confirmed/Shipped` | `Open/InProgress/Resolved` |
|
|
252
|
-
|
|
253
|
-
Documente em `1-design/ubiquitous-language.md` por feature (ver standard).
|
|
254
|
-
|
|
255
|
-
---
|
|
256
|
-
|
|
257
|
-
## Quando NÃO usar Bounded Contexts
|
|
258
|
-
|
|
259
|
-
- App single-domain (ex: sistema de agendamentos)
|
|
260
|
-
- MVP ou projeto com um único time
|
|
261
|
-
- Quando os "domínios" compartilham o mesmo modelo de dados sem conflito
|
|
262
|
-
|
|
263
|
-
Em todos esses casos: use Nível 2 (Aggregates sem BC isolation).
|
|
264
|
-
|
|
265
|
-
---
|
|
266
|
-
|
|
267
|
-
*MORPH-SPEC by Polymorphism Tech*
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
**Step 2: Commit**
|
|
271
|
-
|
|
272
|
-
```bash
|
|
273
|
-
git add framework/standards/architecture/ddd/bounded-contexts.md
|
|
274
|
-
git commit -m "feat(standards): add Bounded Contexts standard — structure, cross-BC rules, when NOT to use"
|
|
275
|
-
```
|
|
276
|
-
|
|
277
|
-
---
|
|
278
|
-
|
|
279
|
-
### Task 3: Criar `ubiquitous-language.md`
|
|
280
|
-
|
|
281
|
-
**Files:**
|
|
282
|
-
- Create: `framework/standards/architecture/ddd/ubiquitous-language.md`
|
|
283
|
-
|
|
284
|
-
**Step 1: Escrever o standard**
|
|
285
|
-
|
|
286
|
-
```markdown
|
|
287
|
-
# DDD Ubiquitous Language
|
|
288
|
-
|
|
289
|
-
> **Scope:** universal
|
|
290
|
-
> **Layer:** 2 (load when: ubiquitous language, glossary, domain language)
|
|
291
|
-
> **Keywords:** ubiquitous-language, ddd, glossary, domain-terms
|
|
292
|
-
|
|
293
|
-
Linguagem Ubíqua é o vocabulário compartilhado entre devs e domínio de negócio.
|
|
294
|
-
Usada em Nível 2+ para nomear Aggregates, Value Objects, Events e métodos.
|
|
295
|
-
|
|
296
|
-
---
|
|
297
|
-
|
|
298
|
-
## Formato do Glossário (output: `ubiquitous-language.md`)
|
|
299
|
-
|
|
300
|
-
```markdown
|
|
301
|
-
# Ubiquitous Language: {Feature/BoundedContext}
|
|
302
|
-
|
|
303
|
-
## Termos do Domínio
|
|
304
|
-
|
|
305
|
-
| Termo | Definição | Contexto | Mapeamento em Código |
|
|
306
|
-
|-------|-----------|----------|---------------------|
|
|
307
|
-
| Subscription | Contrato ativo de uso da plataforma por um tenant | Billing | `Subscription` (AggregateRoot) |
|
|
308
|
-
| PlanTier | Nível do plano (Free/Pro/Enterprise) | Billing | `PlanTier` (ValueObject) |
|
|
309
|
-
| Activation | Ato de ativar uma Subscription após pagamento | Billing | `Subscription.Activate()` |
|
|
310
|
-
| Churn | Cancelamento não-renovado de Subscription | Billing | `SubscriptionCancelledEvent` |
|
|
311
|
-
|
|
312
|
-
## Estados e Transições
|
|
313
|
-
|
|
314
|
-
| Estado | Descrição | Transições Permitidas |
|
|
315
|
-
|--------|-----------|----------------------|
|
|
316
|
-
| Draft | Subscription criada, aguardando pagamento | → Active |
|
|
317
|
-
| Active | Subscription paga e em uso | → Suspended, Cancelled |
|
|
318
|
-
| Suspended | Pagamento falhou, acesso bloqueado | → Active, Cancelled |
|
|
319
|
-
| Cancelled | Encerrada definitivamente | (terminal) |
|
|
320
|
-
|
|
321
|
-
## Invariants em Linguagem Natural
|
|
322
|
-
|
|
323
|
-
- "Uma Subscription só pode ser Cancelada se estiver Active ou Suspended"
|
|
324
|
-
- "Um Tenant não pode ter duas Subscriptions Active simultaneamente"
|
|
325
|
-
- "PlanTier não pode ser rebaixado durante um ciclo de cobrança ativo"
|
|
326
|
-
```
|
|
327
|
-
|
|
328
|
-
---
|
|
329
|
-
|
|
330
|
-
## Regras de Nomenclatura
|
|
331
|
-
|
|
332
|
-
1. Use termos do glossário como nomes de classe/método — nunca invente sinonimos
|
|
333
|
-
2. Eventos no passado: `OrderConfirmed`, não `OrderConfirmation` ou `ConfirmOrder`
|
|
334
|
-
3. Commands no imperativo: `CreateOrder`, não `OrderCreation`
|
|
335
|
-
4. Value Objects no substantivo: `Money`, `Email`, não `MoneyValue` ou `EmailAddress`
|
|
336
|
-
5. Métodos do Aggregate espelham ações do negócio: `order.Confirm()`, não `order.SetStatusToConfirmed()`
|
|
337
|
-
|
|
338
|
-
---
|
|
339
|
-
|
|
340
|
-
## Quando Gerar
|
|
341
|
-
|
|
342
|
-
- **Nível 2:** Gerar junto com `contracts.cs` na fase de design (seção inline no spec.md)
|
|
343
|
-
- **Nível 3:** Gerar arquivo separado `1-design/ubiquitous-language.md` por BC
|
|
344
|
-
|
|
345
|
-
---
|
|
346
|
-
|
|
347
|
-
*MORPH-SPEC by Polymorphism Tech*
|
|
348
|
-
```
|
|
349
|
-
|
|
350
|
-
**Step 2: Commit**
|
|
351
|
-
|
|
352
|
-
```bash
|
|
353
|
-
git add framework/standards/architecture/ddd/ubiquitous-language.md
|
|
354
|
-
git commit -m "feat(standards): add Ubiquitous Language standard — glossary format, naming rules, when to generate"
|
|
355
|
-
```
|
|
356
|
-
|
|
357
|
-
---
|
|
358
|
-
|
|
359
|
-
### Task 4: Regenerar Standards Registry
|
|
360
|
-
|
|
361
|
-
**Files:**
|
|
362
|
-
- Modify: `framework/standards/STANDARDS.json` (via script)
|
|
363
|
-
|
|
364
|
-
**Step 1: Rodar o script gerador**
|
|
365
|
-
|
|
366
|
-
```bash
|
|
367
|
-
node scripts/generate-standards-registry.js
|
|
368
|
-
```
|
|
369
|
-
|
|
370
|
-
**Step 2: Verificar que os 3 novos standards foram adicionados**
|
|
371
|
-
|
|
372
|
-
```bash
|
|
373
|
-
node -e "
|
|
374
|
-
const s = JSON.parse(require('fs').readFileSync('framework/standards/STANDARDS.json'));
|
|
375
|
-
const ddd = s.filter(x => x.id.includes('ddd'));
|
|
376
|
-
console.log(ddd.map(x => x.id));
|
|
377
|
-
"
|
|
378
|
-
```
|
|
379
|
-
Esperado: lista incluindo `complexity-levels`, `bounded-contexts`, `ubiquitous-language`.
|
|
380
|
-
|
|
381
|
-
**Step 3: Commit**
|
|
382
|
-
|
|
383
|
-
```bash
|
|
384
|
-
git add framework/standards/STANDARDS.json
|
|
385
|
-
git commit -m "chore(standards): regenerate registry — 3 new DDD standards (77 total)"
|
|
386
|
-
```
|
|
387
|
-
|
|
388
|
-
---
|
|
389
|
-
|
|
390
|
-
## Parte B — Templates
|
|
391
|
-
|
|
392
|
-
### Task 5: Atualizar `spec.md` — Adicionar seções DDD
|
|
393
|
-
|
|
394
|
-
**Files:**
|
|
395
|
-
- Modify: `framework/templates/docs/spec.md`
|
|
396
|
-
|
|
397
|
-
Adicionar **após** a seção `## Technical Design` e **antes** de `## UI/UX Design`:
|
|
398
|
-
|
|
399
|
-
```markdown
|
|
400
|
-
---
|
|
401
|
-
|
|
402
|
-
## Domain Complexity
|
|
403
|
-
|
|
404
|
-
**Nível:** {1 — CRUD / 2 — Business Logic / 3 — Bounded Context}
|
|
405
|
-
|
|
406
|
-
**Justificativa:** {Por que este nível foi escolhido — mencionar invariants, eventos ou BCs se Nível 2+}
|
|
407
|
-
|
|
408
|
-
**Padrões Aplicados:**
|
|
409
|
-
- {Nível 1: Entity simples, Service CRUD, DTOs}
|
|
410
|
-
- {Nível 2: AggregateRoot, Value Objects, Domain Events, CQRS}
|
|
411
|
-
- {Nível 3: Bounded Context isolation, cross-BC events, ubiquitous language por BC}
|
|
412
|
-
|
|
413
|
-
**Padrões Omitidos e Motivo:**
|
|
414
|
-
- {ex: Bounded Contexts — sistema single-domain, desnecessário}
|
|
415
|
-
|
|
416
|
-
---
|
|
417
|
-
|
|
418
|
-
## Aggregate Blueprint (Nível 2+ apenas)
|
|
419
|
-
|
|
420
|
-
> Omitir esta seção se Nível 1 (CRUD).
|
|
421
|
-
|
|
422
|
-
### Aggregate Root: {EntityName}
|
|
423
|
-
|
|
424
|
-
**Invariants:**
|
|
425
|
-
- {Invariant 1 em linguagem natural}
|
|
426
|
-
- {Invariant 2}
|
|
427
|
-
|
|
428
|
-
**Estados e Transições:**
|
|
429
|
-
```
|
|
430
|
-
{Status.Draft} → Confirm() → {Status.Confirmed} → Ship() → {Status.Shipped}
|
|
431
|
-
→ Cancel() → {Status.Cancelled}
|
|
432
|
-
```
|
|
433
|
-
|
|
434
|
-
**Domain Events:**
|
|
435
|
-
- `{EntityName}CreatedEvent` — publicado ao criar
|
|
436
|
-
- `{EntityName}ConfirmedEvent` — publicado ao confirmar
|
|
437
|
-
|
|
438
|
-
**Value Objects:**
|
|
439
|
-
- `{ValueObjectName}` — {por que não é um primitivo simples}
|
|
440
|
-
|
|
441
|
-
**Referências Cross-Aggregate (por ID apenas):**
|
|
442
|
-
- `{OtherEntity}Id: Guid` — nunca navigation property
|
|
443
|
-
|
|
444
|
-
### Ubiquitous Language (Nível 2)
|
|
445
|
-
|
|
446
|
-
| Termo | Definição | Código |
|
|
447
|
-
|-------|-----------|--------|
|
|
448
|
-
| {Termo} | {Definição do negócio} | `{ClassName.MethodName()}` |
|
|
449
|
-
|
|
450
|
-
---
|
|
451
|
-
```
|
|
452
|
-
|
|
453
|
-
**Step 1: Ler o template atual para confirmar localização exata**
|
|
454
|
-
|
|
455
|
-
```bash
|
|
456
|
-
# Verificar linha da seção Technical Design
|
|
457
|
-
grep -n "Technical Design\|UI/UX Design" framework/templates/docs/spec.md
|
|
458
|
-
```
|
|
459
|
-
|
|
460
|
-
**Step 2: Editar o template** — inserir o bloco acima entre as duas seções.
|
|
461
|
-
|
|
462
|
-
**Step 3: Verificar que o template ainda renderiza corretamente**
|
|
463
|
-
|
|
464
|
-
```bash
|
|
465
|
-
npx morph-spec template validate docs/spec
|
|
466
|
-
```
|
|
467
|
-
|
|
468
|
-
**Step 4: Commit**
|
|
469
|
-
|
|
470
|
-
```bash
|
|
471
|
-
git add framework/templates/docs/spec.md
|
|
472
|
-
git commit -m "feat(templates): add Domain Complexity + Aggregate Blueprint sections to spec.md"
|
|
473
|
-
```
|
|
474
|
-
|
|
475
|
-
---
|
|
476
|
-
|
|
477
|
-
### Task 6: Criar `contracts-level1.cs` (CRUD)
|
|
478
|
-
|
|
479
|
-
**Files:**
|
|
480
|
-
- Create: `framework/templates/code/dotnet/contracts/contracts-level1.cs`
|
|
481
|
-
|
|
482
|
-
O template Nível 1 é o que existe hoje, porém sem domain entity comentada e sem Aggregate patterns. Cópia limpa do atual com ajustes:
|
|
483
|
-
|
|
484
|
-
```csharp
|
|
485
|
-
// ============================================================
|
|
486
|
-
// CONTRACTS: {{titleCase FEATURE_NAME}} — Level 1 (CRUD)
|
|
487
|
-
// Generated by MORPH Framework | Date: {{DATE}}
|
|
488
|
-
// Domain Complexity: CRUD — Entity simples, sem invariants de negócio
|
|
489
|
-
// ============================================================
|
|
490
|
-
|
|
491
|
-
using System;
|
|
492
|
-
using System.Collections.Generic;
|
|
493
|
-
using System.Threading;
|
|
494
|
-
using System.Threading.Tasks;
|
|
495
|
-
|
|
496
|
-
namespace {{NAMESPACE}}.Application.Features.{{pascalCase FEATURE_NAME}};
|
|
497
|
-
|
|
498
|
-
#region Service Interfaces
|
|
499
|
-
|
|
500
|
-
public interface I{{pascalCase FEATURE_NAME}}Service
|
|
501
|
-
{
|
|
502
|
-
Task<{{pascalCase FEATURE_NAME}}Dto?> GetByIdAsync(Guid id, CancellationToken ct = default);
|
|
503
|
-
Task<List<{{pascalCase FEATURE_NAME}}Dto>> GetAllAsync(CancellationToken ct = default);
|
|
504
|
-
Task<{{pascalCase FEATURE_NAME}}Dto> CreateAsync(Create{{pascalCase FEATURE_NAME}}Request request, CancellationToken ct = default);
|
|
505
|
-
Task UpdateAsync(Guid id, Update{{pascalCase FEATURE_NAME}}Request request, CancellationToken ct = default);
|
|
506
|
-
Task DeleteAsync(Guid id, CancellationToken ct = default);
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
#endregion
|
|
510
|
-
|
|
511
|
-
#region DTOs
|
|
512
|
-
|
|
513
|
-
public record {{pascalCase FEATURE_NAME}}Dto(
|
|
514
|
-
Guid Id,
|
|
515
|
-
string Name,
|
|
516
|
-
DateTime CreatedAt,
|
|
517
|
-
DateTime? UpdatedAt
|
|
518
|
-
);
|
|
519
|
-
|
|
520
|
-
public record Create{{pascalCase FEATURE_NAME}}Request(
|
|
521
|
-
string Name
|
|
522
|
-
// Add fields
|
|
523
|
-
);
|
|
524
|
-
|
|
525
|
-
public record Update{{pascalCase FEATURE_NAME}}Request(
|
|
526
|
-
string Name
|
|
527
|
-
// Add fields
|
|
528
|
-
);
|
|
529
|
-
|
|
530
|
-
#endregion
|
|
531
|
-
|
|
532
|
-
#region Repository Interface
|
|
533
|
-
|
|
534
|
-
public interface I{{pascalCase FEATURE_NAME}}Repository
|
|
535
|
-
{
|
|
536
|
-
Task<{{pascalCase FEATURE_NAME}}?> GetByIdAsync(Guid id, CancellationToken ct = default);
|
|
537
|
-
Task<List<{{pascalCase FEATURE_NAME}}>> GetAllAsync(CancellationToken ct = default);
|
|
538
|
-
Task AddAsync({{pascalCase FEATURE_NAME}} entity, CancellationToken ct = default);
|
|
539
|
-
void Update({{pascalCase FEATURE_NAME}} entity);
|
|
540
|
-
void Remove({{pascalCase FEATURE_NAME}} entity);
|
|
541
|
-
Task SaveChangesAsync(CancellationToken ct = default);
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
#endregion
|
|
545
|
-
|
|
546
|
-
#region Exceptions
|
|
547
|
-
|
|
548
|
-
public class {{pascalCase FEATURE_NAME}}NotFoundException(Guid id)
|
|
549
|
-
: Exception($"{{pascalCase FEATURE_NAME}} '{id}' not found.");
|
|
550
|
-
|
|
551
|
-
#endregion
|
|
552
|
-
```
|
|
553
|
-
|
|
554
|
-
**Step 1: Criar o arquivo** com o conteúdo acima.
|
|
555
|
-
|
|
556
|
-
**Step 2: Verificar que o template renderiza sem erros**
|
|
557
|
-
|
|
558
|
-
```bash
|
|
559
|
-
npx morph-spec template render \
|
|
560
|
-
code/dotnet/contracts/contracts-level1.cs \
|
|
561
|
-
/tmp/test-level1.cs \
|
|
562
|
-
'{"FEATURE_NAME":"category","NAMESPACE":"MyApp","DATE":"2026-02-23"}'
|
|
563
|
-
cat /tmp/test-level1.cs
|
|
564
|
-
```
|
|
565
|
-
Esperado: C# válido sem placeholders Handlebars não substituídos.
|
|
566
|
-
|
|
567
|
-
**Step 3: Commit**
|
|
568
|
-
|
|
569
|
-
```bash
|
|
570
|
-
git add framework/templates/code/dotnet/contracts/contracts-level1.cs
|
|
571
|
-
git commit -m "feat(templates): add contracts-level1.cs — CRUD pattern, no Domain Events or Aggregates"
|
|
572
|
-
```
|
|
573
|
-
|
|
574
|
-
---
|
|
575
|
-
|
|
576
|
-
### Task 7: Criar `contracts-level2.cs` (Aggregate)
|
|
577
|
-
|
|
578
|
-
**Files:**
|
|
579
|
-
- Create: `framework/templates/code/dotnet/contracts/contracts-level2.cs`
|
|
580
|
-
|
|
581
|
-
```csharp
|
|
582
|
-
// ============================================================
|
|
583
|
-
// CONTRACTS: {{titleCase FEATURE_NAME}} — Level 2 (Business Logic)
|
|
584
|
-
// Generated by MORPH Framework | Date: {{DATE}}
|
|
585
|
-
// Domain Complexity: Aggregate com invariants, Domain Events, CQRS
|
|
586
|
-
// Ref: framework/standards/architecture/ddd/aggregates.md
|
|
587
|
-
// ============================================================
|
|
588
|
-
|
|
589
|
-
using System;
|
|
590
|
-
using System.Collections.Generic;
|
|
591
|
-
using System.Threading;
|
|
592
|
-
using System.Threading.Tasks;
|
|
593
|
-
using MediatR;
|
|
594
|
-
|
|
595
|
-
namespace {{NAMESPACE}}.Domain.{{pascalCase FEATURE_NAME}}s;
|
|
596
|
-
|
|
597
|
-
// ===== AGGREGATE ROOT =====
|
|
598
|
-
// Implemente em: Domain/{{pascalCase FEATURE_NAME}}s/Aggregates/{{pascalCase FEATURE_NAME}}.cs
|
|
599
|
-
//
|
|
600
|
-
// Invariants documentados no spec.md > Aggregate Blueprint:
|
|
601
|
-
// - {Invariant 1}
|
|
602
|
-
// - {Invariant 2}
|
|
603
|
-
//
|
|
604
|
-
// public sealed class {{pascalCase FEATURE_NAME}} : AggregateRoot
|
|
605
|
-
// {
|
|
606
|
-
// private {{pascalCase FEATURE_NAME}}() { } // EF constructor
|
|
607
|
-
//
|
|
608
|
-
// public static {{pascalCase FEATURE_NAME}} Create(/* params */) { /* validate + RaiseDomainEvent */ }
|
|
609
|
-
// public void {Action}() { /* validate invariant + mutate + RaiseDomainEvent */ }
|
|
610
|
-
// }
|
|
611
|
-
|
|
612
|
-
// ===== VALUE OBJECTS =====
|
|
613
|
-
// Implemente em: Domain/{{pascalCase FEATURE_NAME}}s/ValueObjects/
|
|
614
|
-
// (inclua apenas se houver primitivos com regras de validação/negócio)
|
|
615
|
-
|
|
616
|
-
// ===== DOMAIN EVENTS =====
|
|
617
|
-
|
|
618
|
-
namespace {{NAMESPACE}}.Domain.{{pascalCase FEATURE_NAME}}s.Events;
|
|
619
|
-
|
|
620
|
-
public record {{pascalCase FEATURE_NAME}}CreatedEvent(
|
|
621
|
-
Guid {{pascalCase FEATURE_NAME}}Id,
|
|
622
|
-
Guid UserId
|
|
623
|
-
) : DomainEvent;
|
|
624
|
-
|
|
625
|
-
// Adicione eventos para cada mudança de estado relevante
|
|
626
|
-
// public record {{pascalCase FEATURE_NAME}}ConfirmedEvent(Guid {{pascalCase FEATURE_NAME}}Id) : DomainEvent;
|
|
627
|
-
|
|
628
|
-
// ===== COMMANDS (CQRS) =====
|
|
629
|
-
|
|
630
|
-
namespace {{NAMESPACE}}.Application.{{pascalCase FEATURE_NAME}}s.Commands;
|
|
631
|
-
|
|
632
|
-
public record Create{{pascalCase FEATURE_NAME}}Command(
|
|
633
|
-
Guid UserId
|
|
634
|
-
// Add fields
|
|
635
|
-
) : IRequest<Create{{pascalCase FEATURE_NAME}}Result>;
|
|
636
|
-
|
|
637
|
-
public record Create{{pascalCase FEATURE_NAME}}Result(Guid Id);
|
|
638
|
-
|
|
639
|
-
// public record Update{{pascalCase FEATURE_NAME}}Command(...) : IRequest;
|
|
640
|
-
// public record Delete{{pascalCase FEATURE_NAME}}Command(Guid Id) : IRequest;
|
|
641
|
-
|
|
642
|
-
// ===== QUERIES =====
|
|
643
|
-
|
|
644
|
-
namespace {{NAMESPACE}}.Application.{{pascalCase FEATURE_NAME}}s.Queries;
|
|
645
|
-
|
|
646
|
-
public record Get{{pascalCase FEATURE_NAME}}Query(Guid Id) : IRequest<{{pascalCase FEATURE_NAME}}Dto?>;
|
|
647
|
-
public record List{{pascalCase FEATURE_NAME}}sQuery(/* filters */) : IRequest<List<{{pascalCase FEATURE_NAME}}Dto>>;
|
|
648
|
-
|
|
649
|
-
// ===== DTOs (read models — nunca expõe o Aggregate) =====
|
|
650
|
-
|
|
651
|
-
namespace {{NAMESPACE}}.Application.{{pascalCase FEATURE_NAME}}s;
|
|
652
|
-
|
|
653
|
-
public record {{pascalCase FEATURE_NAME}}Dto(
|
|
654
|
-
Guid Id,
|
|
655
|
-
string Status,
|
|
656
|
-
DateTime CreatedAt,
|
|
657
|
-
DateTime? UpdatedAt
|
|
658
|
-
);
|
|
659
|
-
|
|
660
|
-
// ===== REPOSITORY (um por Aggregate Root) =====
|
|
661
|
-
|
|
662
|
-
namespace {{NAMESPACE}}.Domain.{{pascalCase FEATURE_NAME}}s;
|
|
663
|
-
|
|
664
|
-
public interface I{{pascalCase FEATURE_NAME}}Repository
|
|
665
|
-
{
|
|
666
|
-
Task<{{pascalCase FEATURE_NAME}}?> GetAsync(Guid id, CancellationToken ct = default);
|
|
667
|
-
Task AddAsync({{pascalCase FEATURE_NAME}} aggregate, CancellationToken ct = default);
|
|
668
|
-
Task UpdateAsync({{pascalCase FEATURE_NAME}} aggregate, CancellationToken ct = default);
|
|
669
|
-
Task<bool> ExistsAsync(Guid id, CancellationToken ct = default);
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
// ===== EXCEPTIONS =====
|
|
673
|
-
|
|
674
|
-
public class {{pascalCase FEATURE_NAME}}NotFoundException(Guid id)
|
|
675
|
-
: DomainException($"{{pascalCase FEATURE_NAME}} '{id}' not found.");
|
|
676
|
-
|
|
677
|
-
public class {{pascalCase FEATURE_NAME}}InvalidStateException(string message)
|
|
678
|
-
: DomainException(message);
|
|
679
|
-
```
|
|
680
|
-
|
|
681
|
-
**Step 1: Criar o arquivo.**
|
|
682
|
-
|
|
683
|
-
**Step 2: Verificar renderização**
|
|
684
|
-
|
|
685
|
-
```bash
|
|
686
|
-
npx morph-spec template render \
|
|
687
|
-
code/dotnet/contracts/contracts-level2.cs \
|
|
688
|
-
/tmp/test-level2.cs \
|
|
689
|
-
'{"FEATURE_NAME":"order","NAMESPACE":"MyApp","DATE":"2026-02-23"}'
|
|
690
|
-
```
|
|
691
|
-
Esperado: sem placeholders não resolvidos, namespaces corretos.
|
|
692
|
-
|
|
693
|
-
**Step 3: Commit**
|
|
694
|
-
|
|
695
|
-
```bash
|
|
696
|
-
git add framework/templates/code/dotnet/contracts/contracts-level2.cs
|
|
697
|
-
git commit -m "feat(templates): add contracts-level2.cs — Aggregate, Domain Events, CQRS pattern"
|
|
698
|
-
```
|
|
699
|
-
|
|
700
|
-
---
|
|
701
|
-
|
|
702
|
-
### Task 8: Criar `contracts-level3.cs` (Bounded Context)
|
|
703
|
-
|
|
704
|
-
**Files:**
|
|
705
|
-
- Create: `framework/templates/code/dotnet/contracts/contracts-level3.cs`
|
|
706
|
-
|
|
707
|
-
Nível 3 estende o Nível 2 com isolamento de BC e comunicação cross-BC explícita:
|
|
708
|
-
|
|
709
|
-
```csharp
|
|
710
|
-
// ============================================================
|
|
711
|
-
// CONTRACTS: {{titleCase FEATURE_NAME}} — Level 3 (Bounded Context)
|
|
712
|
-
// Generated by MORPH Framework | Date: {{DATE}}
|
|
713
|
-
// Bounded Context: {{BOUNDED_CONTEXT}}
|
|
714
|
-
// Ref: framework/standards/architecture/ddd/bounded-contexts.md
|
|
715
|
-
// ============================================================
|
|
716
|
-
//
|
|
717
|
-
// ⚠️ LEMBRE-SE DAS REGRAS DE BC:
|
|
718
|
-
// 1. Referências cross-BC APENAS por ID (nunca navigation property)
|
|
719
|
-
// 2. Comunicação cross-BC APENAS via Domain Events
|
|
720
|
-
// 3. Cada BC tem seu próprio repositório (nunca compartilhar DbContext)
|
|
721
|
-
// 4. Linguagem Ubíqua documentada em 1-design/ubiquitous-language.md
|
|
722
|
-
|
|
723
|
-
// ===== TUDO DO NÍVEL 2 + ISOLAMENTO DE BC =====
|
|
724
|
-
// (inclua todos os contracts do Level 2 aqui com o namespace do BC)
|
|
725
|
-
|
|
726
|
-
namespace {{NAMESPACE}}.Domain.{{BOUNDED_CONTEXT}}.{{pascalCase FEATURE_NAME}}s;
|
|
727
|
-
|
|
728
|
-
// Aggregates, Value Objects, Domain Events — mesma estrutura do Level 2
|
|
729
|
-
// mas dentro do namespace do Bounded Context
|
|
730
|
-
|
|
731
|
-
// ===== INTEGRATION EVENTS (comunicação cross-BC) =====
|
|
732
|
-
|
|
733
|
-
namespace {{NAMESPACE}}.Domain.{{BOUNDED_CONTEXT}}.IntegrationEvents;
|
|
734
|
-
|
|
735
|
-
// Integration Events são diferentes de Domain Events:
|
|
736
|
-
// - Domain Event: dentro do mesmo BC (MediatR, síncrono)
|
|
737
|
-
// - Integration Event: cross-BC (Service Bus ou MediatR INotification, assíncrono)
|
|
738
|
-
|
|
739
|
-
public record {{pascalCase FEATURE_NAME}}IntegrationEvent(
|
|
740
|
-
Guid {{pascalCase FEATURE_NAME}}Id,
|
|
741
|
-
string EventType,
|
|
742
|
-
DateTime OccurredAt
|
|
743
|
-
);
|
|
744
|
-
|
|
745
|
-
// ===== CROSS-BC REFERENCES (por ID apenas) =====
|
|
746
|
-
//
|
|
747
|
-
// Exemplo de como referenciar outro BC:
|
|
748
|
-
// public sealed class {{pascalCase FEATURE_NAME}} : AggregateRoot
|
|
749
|
-
// {
|
|
750
|
-
// public Guid ExternalBcEntityId { get; private set; } // ✅ por ID
|
|
751
|
-
// // public ExternalEntity External { get; } // ❌ proibido
|
|
752
|
-
// }
|
|
753
|
-
```
|
|
754
|
-
|
|
755
|
-
**Step 1: Criar o arquivo.**
|
|
756
|
-
|
|
757
|
-
**Step 2: Verificar renderização com BOUNDED_CONTEXT**
|
|
758
|
-
|
|
759
|
-
```bash
|
|
760
|
-
npx morph-spec template render \
|
|
761
|
-
code/dotnet/contracts/contracts-level3.cs \
|
|
762
|
-
/tmp/test-level3.cs \
|
|
763
|
-
'{"FEATURE_NAME":"subscription","NAMESPACE":"MyApp","BOUNDED_CONTEXT":"Billing","DATE":"2026-02-23"}'
|
|
764
|
-
```
|
|
765
|
-
|
|
766
|
-
**Step 3: Commit**
|
|
767
|
-
|
|
768
|
-
```bash
|
|
769
|
-
git add framework/templates/code/dotnet/contracts/contracts-level3.cs
|
|
770
|
-
git commit -m "feat(templates): add contracts-level3.cs — Bounded Context, Integration Events, cross-BC rules"
|
|
771
|
-
```
|
|
772
|
-
|
|
773
|
-
---
|
|
774
|
-
|
|
775
|
-
### Task 9: Criar template de estrutura de pastas por nível
|
|
776
|
-
|
|
777
|
-
**Files:**
|
|
778
|
-
- Create: `framework/templates/project-structure/dotnet-ddd.md`
|
|
779
|
-
|
|
780
|
-
```markdown
|
|
781
|
-
# Estrutura de Pastas .NET + DDD
|
|
782
|
-
|
|
783
|
-
> Template de referência. O nível determina quais subpastas são criadas.
|
|
784
|
-
> Gerado durante a fase de Design baseado no Domain Complexity Level.
|
|
785
|
-
|
|
786
|
-
## Nível 1 — CRUD
|
|
787
|
-
|
|
788
|
-
```
|
|
789
|
-
src/{App}.Domain/
|
|
790
|
-
Entities/
|
|
791
|
-
{EntityName}.cs
|
|
792
|
-
Interfaces/
|
|
793
|
-
I{EntityName}Repository.cs
|
|
794
|
-
|
|
795
|
-
src/{App}.Application/
|
|
796
|
-
Features/{FeatureName}/
|
|
797
|
-
{FeatureName}Service.cs
|
|
798
|
-
DTOs/
|
|
799
|
-
{FeatureName}Dto.cs
|
|
800
|
-
Create{FeatureName}Request.cs
|
|
801
|
-
```
|
|
802
|
-
|
|
803
|
-
## Nível 2 — Business Logic
|
|
804
|
-
|
|
805
|
-
```
|
|
806
|
-
src/{App}.Domain/
|
|
807
|
-
{EntityName}s/ ← pasta por aggregate
|
|
808
|
-
Aggregates/
|
|
809
|
-
{EntityName}.cs ← AggregateRoot
|
|
810
|
-
ValueObjects/
|
|
811
|
-
{ValueObjectName}.cs
|
|
812
|
-
Events/
|
|
813
|
-
{EntityName}CreatedEvent.cs
|
|
814
|
-
Exceptions/
|
|
815
|
-
{EntityName}NotFoundException.cs
|
|
816
|
-
I{EntityName}Repository.cs
|
|
817
|
-
|
|
818
|
-
src/{App}.Application/
|
|
819
|
-
{EntityName}s/
|
|
820
|
-
Commands/
|
|
821
|
-
Create{EntityName}Command.cs
|
|
822
|
-
Create{EntityName}Handler.cs
|
|
823
|
-
Queries/
|
|
824
|
-
Get{EntityName}Query.cs
|
|
825
|
-
Get{EntityName}Handler.cs
|
|
826
|
-
{EntityName}Dto.cs
|
|
827
|
-
```
|
|
828
|
-
|
|
829
|
-
## Nível 3 — Bounded Context
|
|
830
|
-
|
|
831
|
-
```
|
|
832
|
-
src/{App}.Domain/
|
|
833
|
-
{BoundedContext}/ ← pasta por BC
|
|
834
|
-
{EntityName}s/ ← mesma estrutura do Nível 2
|
|
835
|
-
Aggregates/
|
|
836
|
-
ValueObjects/
|
|
837
|
-
Events/
|
|
838
|
-
IntegrationEvents/ ← eventos cross-BC
|
|
839
|
-
|
|
840
|
-
src/{App}.Application/
|
|
841
|
-
{BoundedContext}/ ← espelha Domain
|
|
842
|
-
{EntityName}s/
|
|
843
|
-
Commands/
|
|
844
|
-
Queries/
|
|
845
|
-
```
|
|
846
|
-
```
|
|
847
|
-
|
|
848
|
-
**Step 1: Criar o arquivo.**
|
|
849
|
-
|
|
850
|
-
**Step 2: Commit**
|
|
851
|
-
|
|
852
|
-
```bash
|
|
853
|
-
git add framework/templates/project-structure/dotnet-ddd.md
|
|
854
|
-
git commit -m "feat(templates): add dotnet-ddd.md folder structure template — 3 levels"
|
|
855
|
-
```
|
|
856
|
-
|
|
857
|
-
---
|
|
858
|
-
|
|
859
|
-
## Parte C — Agentes
|
|
860
|
-
|
|
861
|
-
### Task 10: Elevar `domain-architect` para Tier 2 em `agents.json`
|
|
862
|
-
|
|
863
|
-
**Files:**
|
|
864
|
-
- Modify: `framework/agents.json`
|
|
865
|
-
|
|
866
|
-
**Contexto:**
|
|
867
|
-
- `ddd-expert` está em Tier 3 na lista de coordinatedBy `dotnet-senior`
|
|
868
|
-
- Precisamos: elevar para Tier 2, renomear para `domain-architect`, remover de `dotnet-senior`
|
|
869
|
-
|
|
870
|
-
**Step 1: Ler o agents.json para localizar ddd-expert e dotnet-senior**
|
|
871
|
-
|
|
872
|
-
```bash
|
|
873
|
-
node -e "
|
|
874
|
-
const a = JSON.parse(require('fs').readFileSync('framework/agents.json'));
|
|
875
|
-
const ddd = a.agents.find(x => x.id === 'ddd-expert');
|
|
876
|
-
const dotnet = a.agents.find(x => x.id === 'dotnet-senior');
|
|
877
|
-
console.log('ddd-expert tier:', ddd?.tier);
|
|
878
|
-
console.log('ddd-expert:', JSON.stringify(ddd, null, 2));
|
|
879
|
-
console.log('dotnet-senior coordinates:', dotnet?.coordinates);
|
|
880
|
-
"
|
|
881
|
-
```
|
|
882
|
-
|
|
883
|
-
**Step 2: Aplicar as mudanças no agents.json**
|
|
884
|
-
|
|
885
|
-
Mudanças necessárias:
|
|
886
|
-
1. Localizar o agente `ddd-expert` (Tier 3)
|
|
887
|
-
2. Alterar:
|
|
888
|
-
- `id`: `ddd-expert` → `domain-architect`
|
|
889
|
-
- `tier`: `3` → `2`
|
|
890
|
-
- `name`: atualizar para "Domain Architect"
|
|
891
|
-
- `role`: "Domain modeling leader — detects complexity level, designs aggregates and bounded contexts"
|
|
892
|
-
- `alwaysActive`: `false` → `true` (ativo nas fases design + implement)
|
|
893
|
-
- `activePhases`: adicionar `["design", "implement"]`
|
|
894
|
-
- `coordinates`: adicionar `["ef-modeler", "event-architect"]` (especialistas que o domain-architect precisa)
|
|
895
|
-
- `reportsTo`: `"dotnet-senior"` → `"standards-architect"`
|
|
896
|
-
3. Em `dotnet-senior.coordinates`: remover `"ddd-expert"`, adicionar `"domain-architect"` como peer (não subordinado)
|
|
897
|
-
4. Atualizar tier sums: Tier 2 de `3` → `4`
|
|
898
|
-
5. Atualizar total de agentes se necessário (renomear não muda count)
|
|
899
|
-
6. Atualizar `version` de `3.1.0-hierarchical` → `3.2.0-hierarchical`
|
|
900
|
-
|
|
901
|
-
**Step 3: Verificar integridade do JSON**
|
|
902
|
-
|
|
903
|
-
```bash
|
|
904
|
-
node -e "
|
|
905
|
-
const a = JSON.parse(require('fs').readFileSync('framework/agents.json'));
|
|
906
|
-
const tier2 = a.agents.filter(x => x.tier === 2);
|
|
907
|
-
console.log('Tier 2 agents:', tier2.map(x => x.id));
|
|
908
|
-
console.log('Count:', tier2.length);
|
|
909
|
-
const da = a.agents.find(x => x.id === 'domain-architect');
|
|
910
|
-
console.log('domain-architect:', da ? 'FOUND' : 'NOT FOUND');
|
|
911
|
-
"
|
|
912
|
-
```
|
|
913
|
-
Esperado: Tier 2 com 4 agentes: `dotnet-senior`, `infra-architect`, `ui-designer`, `domain-architect`.
|
|
914
|
-
|
|
915
|
-
**Step 4: Commit**
|
|
916
|
-
|
|
917
|
-
```bash
|
|
918
|
-
git add framework/agents.json
|
|
919
|
-
git commit -m "feat(agents): elevate domain-architect to Tier 2 — leads domain modeling, detects complexity level"
|
|
920
|
-
```
|
|
921
|
-
|
|
922
|
-
---
|
|
923
|
-
|
|
924
|
-
## Parte D — Skills
|
|
925
|
-
|
|
926
|
-
### Task 11: Atualizar `phase-design` — Adicionar detecção de nível de domínio
|
|
927
|
-
|
|
928
|
-
**Files:**
|
|
929
|
-
- Modify: `framework/skills/level-1-workflows/phase-design/SKILL.md`
|
|
930
|
-
|
|
931
|
-
Adicionar **entre** "Passo 1: Carregar Contexto" e "Passo 2: Analisar Código Existente":
|
|
932
|
-
|
|
933
|
-
```markdown
|
|
934
|
-
### Passo 1.5: Detectar Nível de Domínio (domain-architect)
|
|
935
|
-
|
|
936
|
-
**⚠️ OBRIGATÓRIO:** Execute antes de gerar qualquer contrato.
|
|
937
|
-
|
|
938
|
-
**Ref:** `framework/standards/architecture/ddd/complexity-levels.md`
|
|
939
|
-
|
|
940
|
-
O `domain-architect` analisa a proposta e determina o nível:
|
|
941
|
-
|
|
942
|
-
```
|
|
943
|
-
Responda às perguntas (da proposal.md):
|
|
944
|
-
1. A entidade principal tem estados com transições? (Draft → Confirmed → Shipped)
|
|
945
|
-
2. Existem invariants de negócio? ("só cancela se estiver Ativo")
|
|
946
|
-
3. Cálculos derivados existem? (Total, Saldo, Desconto)
|
|
947
|
-
4. Outros módulos precisam reagir a mudanças? (Domain Events com consumidores)
|
|
948
|
-
5. O usuário declarou Bounded Context na proposta?
|
|
949
|
-
|
|
950
|
-
→ Nenhuma acima: Nível 1 (CRUD)
|
|
951
|
-
→ 1-4 verdadeiros: Nível 2 (Business Logic)
|
|
952
|
-
→ 5 verdadeiro OU múltiplos domínios com modelos conflitantes: Nível 3 (BC)
|
|
953
|
-
```
|
|
954
|
-
|
|
955
|
-
**Documente no spec.md** (seção Domain Complexity) antes de continuar.
|
|
956
|
-
|
|
957
|
-
**Template a usar no Passo 4:**
|
|
958
|
-
|
|
959
|
-
| Nível | Template |
|
|
960
|
-
|-------|----------|
|
|
961
|
-
| 1 | `code/dotnet/contracts/contracts-level1.cs` |
|
|
962
|
-
| 2 | `code/dotnet/contracts/contracts-level2.cs` |
|
|
963
|
-
| 3 | `code/dotnet/contracts/contracts-level3.cs` |
|
|
964
|
-
|
|
965
|
-
**Para Nível 2+:** Defina o Aggregate Blueprint (section no spec.md) antes de gerar contracts.cs.
|
|
966
|
-
|
|
967
|
-
**Para Nível 3:** Adicione `BOUNDED_CONTEXT` como variável ao renderizar o template.
|
|
968
|
-
```
|
|
969
|
-
|
|
970
|
-
**Step 1: Editar o SKILL.md** — inserir o bloco acima na posição correta.
|
|
971
|
-
|
|
972
|
-
**Step 2: Verificar que a referência ao template de complexity-levels.md está correta**
|
|
973
|
-
|
|
974
|
-
```bash
|
|
975
|
-
grep -n "complexity-levels" framework/skills/level-1-workflows/phase-design/SKILL.md
|
|
976
|
-
```
|
|
977
|
-
|
|
978
|
-
**Step 3: Commit**
|
|
979
|
-
|
|
980
|
-
```bash
|
|
981
|
-
git add framework/skills/level-1-workflows/phase-design/SKILL.md
|
|
982
|
-
git commit -m "feat(skills): add domain complexity detection step to phase-design — levels 1/2/3 drive template choice"
|
|
983
|
-
```
|
|
984
|
-
|
|
985
|
-
---
|
|
986
|
-
|
|
987
|
-
## Parte E — Tests
|
|
988
|
-
|
|
989
|
-
### Task 12: Atualizar testes do doctor para Tier 2 = 4 agentes
|
|
990
|
-
|
|
991
|
-
**Files:**
|
|
992
|
-
- Modify: `test/commands/doctor.test.js`
|
|
993
|
-
|
|
994
|
-
**Step 1: Ler o arquivo de teste para localizar as asserções de tier**
|
|
995
|
-
|
|
996
|
-
```bash
|
|
997
|
-
grep -n "tier\|Tier\|domain-architect\|ddd-expert" test/commands/doctor.test.js
|
|
998
|
-
```
|
|
999
|
-
|
|
1000
|
-
**Step 2: Atualizar as asserções**
|
|
1001
|
-
|
|
1002
|
-
Mudar qualquer asserção que espera Tier 2 = 3 agentes para Tier 2 = 4 agentes.
|
|
1003
|
-
Mudar referências a `ddd-expert` para `domain-architect` se existirem.
|
|
1004
|
-
|
|
1005
|
-
**Step 3: Rodar os testes do doctor**
|
|
1006
|
-
|
|
1007
|
-
```bash
|
|
1008
|
-
node --test test/commands/doctor.test.js
|
|
1009
|
-
```
|
|
1010
|
-
Esperado: todos os testes passam.
|
|
1011
|
-
|
|
1012
|
-
**Step 4: Se falhar**, revisar o agents.json — verificar que tier sums no JSON batem com o que o doctor valida.
|
|
1013
|
-
|
|
1014
|
-
**Step 5: Commit**
|
|
1015
|
-
|
|
1016
|
-
```bash
|
|
1017
|
-
git add test/commands/doctor.test.js
|
|
1018
|
-
git commit -m "test(doctor): update tier-2 count to 4 — domain-architect elevated from Tier 3"
|
|
1019
|
-
```
|
|
1020
|
-
|
|
1021
|
-
---
|
|
1022
|
-
|
|
1023
|
-
### Task 13: Adicionar testes de renderização dos 3 templates de contracts
|
|
1024
|
-
|
|
1025
|
-
**Files:**
|
|
1026
|
-
- Create: `test/templates/contracts-levels.test.js`
|
|
1027
|
-
|
|
1028
|
-
**Step 1: Escrever os testes**
|
|
1029
|
-
|
|
1030
|
-
```javascript
|
|
1031
|
-
import { test } from 'node:test';
|
|
1032
|
-
import assert from 'node:assert/strict';
|
|
1033
|
-
import { execSync } from 'node:child_process';
|
|
1034
|
-
import { existsSync, unlinkSync } from 'node:fs';
|
|
1035
|
-
import { join } from 'node:path';
|
|
1036
|
-
import { tmpdir } from 'node:os';
|
|
1037
|
-
|
|
1038
|
-
const BASE_VARS = { NAMESPACE: 'MyApp', DATE: '2026-01-01' };
|
|
1039
|
-
|
|
1040
|
-
function renderTemplate(templateId, vars) {
|
|
1041
|
-
const out = join(tmpdir(), `morph-test-${Date.now()}.cs`);
|
|
1042
|
-
const varsJson = JSON.stringify({ ...BASE_VARS, ...vars });
|
|
1043
|
-
execSync(`node bin/morph-spec.js template render "${templateId}" "${out}" '${varsJson}'`);
|
|
1044
|
-
const content = require('fs').readFileSync(out, 'utf8');
|
|
1045
|
-
try { unlinkSync(out); } catch {}
|
|
1046
|
-
return content;
|
|
1047
|
-
}
|
|
1048
|
-
|
|
1049
|
-
test('Level 1 contracts render without Handlebars placeholders', () => {
|
|
1050
|
-
const out = renderTemplate('code/dotnet/contracts/contracts-level1.cs', {
|
|
1051
|
-
FEATURE_NAME: 'category'
|
|
1052
|
-
});
|
|
1053
|
-
assert.ok(!out.includes('{{'), 'Should have no unresolved Handlebars');
|
|
1054
|
-
assert.ok(out.includes('ICategoryService'), 'Should contain service interface');
|
|
1055
|
-
assert.ok(!out.includes('AggregateRoot'), 'Level 1 should NOT have AggregateRoot');
|
|
1056
|
-
assert.ok(!out.includes('DomainEvent'), 'Level 1 should NOT have Domain Events');
|
|
1057
|
-
});
|
|
1058
|
-
|
|
1059
|
-
test('Level 2 contracts render without Handlebars placeholders', () => {
|
|
1060
|
-
const out = renderTemplate('code/dotnet/contracts/contracts-level2.cs', {
|
|
1061
|
-
FEATURE_NAME: 'order'
|
|
1062
|
-
});
|
|
1063
|
-
assert.ok(!out.includes('{{'), 'Should have no unresolved Handlebars');
|
|
1064
|
-
assert.ok(out.includes('DomainEvent'), 'Level 2 should have Domain Events');
|
|
1065
|
-
assert.ok(out.includes('IRequest'), 'Level 2 should have CQRS commands');
|
|
1066
|
-
assert.ok(out.includes('IOrderRepository'), 'Level 2 should have Repository');
|
|
1067
|
-
});
|
|
1068
|
-
|
|
1069
|
-
test('Level 3 contracts render with BOUNDED_CONTEXT variable', () => {
|
|
1070
|
-
const out = renderTemplate('code/dotnet/contracts/contracts-level3.cs', {
|
|
1071
|
-
FEATURE_NAME: 'subscription',
|
|
1072
|
-
BOUNDED_CONTEXT: 'Billing'
|
|
1073
|
-
});
|
|
1074
|
-
assert.ok(!out.includes('{{'), 'Should have no unresolved Handlebars');
|
|
1075
|
-
assert.ok(out.includes('Billing'), 'Should include Bounded Context name');
|
|
1076
|
-
assert.ok(out.includes('IntegrationEvent'), 'Level 3 should have Integration Events');
|
|
1077
|
-
});
|
|
1078
|
-
|
|
1079
|
-
test('Level 1 uses Guid not int for Id', () => {
|
|
1080
|
-
const out = renderTemplate('code/dotnet/contracts/contracts-level1.cs', {
|
|
1081
|
-
FEATURE_NAME: 'tag'
|
|
1082
|
-
});
|
|
1083
|
-
assert.ok(out.includes('Guid id'), 'Should use Guid not int');
|
|
1084
|
-
assert.ok(!out.includes('int id'), 'Should NOT use int id');
|
|
1085
|
-
});
|
|
1086
|
-
```
|
|
1087
|
-
|
|
1088
|
-
**Step 2: Rodar os testes**
|
|
1089
|
-
|
|
1090
|
-
```bash
|
|
1091
|
-
node --test test/templates/contracts-levels.test.js
|
|
1092
|
-
```
|
|
1093
|
-
Esperado: 4 testes passam.
|
|
1094
|
-
|
|
1095
|
-
**Step 3: Rodar a suite completa para garantir nada quebrou**
|
|
1096
|
-
|
|
1097
|
-
```bash
|
|
1098
|
-
node --test
|
|
1099
|
-
```
|
|
1100
|
-
Esperado: todos os testes anteriores continuam passando.
|
|
1101
|
-
|
|
1102
|
-
**Step 4: Commit**
|
|
1103
|
-
|
|
1104
|
-
```bash
|
|
1105
|
-
git add test/templates/contracts-levels.test.js
|
|
1106
|
-
git commit -m "test(templates): add Level 1/2/3 contracts rendering tests — 4 tests"
|
|
1107
|
-
```
|
|
1108
|
-
|
|
1109
|
-
---
|
|
1110
|
-
|
|
1111
|
-
## Verificação Final
|
|
1112
|
-
|
|
1113
|
-
```bash
|
|
1114
|
-
# 1. Suite completa de testes
|
|
1115
|
-
node --test
|
|
1116
|
-
|
|
1117
|
-
# 2. Verificar standards adicionados
|
|
1118
|
-
npx morph-spec standards --list --category ddd
|
|
1119
|
-
|
|
1120
|
-
# 3. Verificar templates existem
|
|
1121
|
-
ls framework/templates/code/dotnet/contracts/
|
|
1122
|
-
|
|
1123
|
-
# 4. Verificar domain-architect no Tier 2
|
|
1124
|
-
node -e "
|
|
1125
|
-
const a = JSON.parse(require('fs').readFileSync('framework/agents.json'));
|
|
1126
|
-
const tier2 = a.agents.filter(x => x.tier === 2).map(x => x.id);
|
|
1127
|
-
console.log('Tier 2:', tier2);
|
|
1128
|
-
"
|
|
1129
|
-
|
|
1130
|
-
# 5. Verificar phase-design skill atualizado
|
|
1131
|
-
grep -n "1.5\|Detectar Nível\|complexity-levels" \
|
|
1132
|
-
framework/skills/level-1-workflows/phase-design/SKILL.md
|
|
1133
|
-
```
|
|
1134
|
-
|
|
1135
|
-
---
|
|
1136
|
-
|
|
1137
|
-
## Resumo das Mudanças
|
|
1138
|
-
|
|
1139
|
-
| Categoria | Arquivos | Tipo |
|
|
1140
|
-
|-----------|---------|------|
|
|
1141
|
-
| Standards | `complexity-levels.md`, `bounded-contexts.md`, `ubiquitous-language.md` | Novo |
|
|
1142
|
-
| Standards | `STANDARDS.json` | Atualizado (77 entradas) |
|
|
1143
|
-
| Templates | `contracts-level1.cs`, `contracts-level2.cs`, `contracts-level3.cs` | Novo |
|
|
1144
|
-
| Templates | `spec.md` | Atualizado (+2 seções) |
|
|
1145
|
-
| Templates | `project-structure/dotnet-ddd.md` | Novo |
|
|
1146
|
-
| Agents | `agents.json` | Atualizado (domain-architect → Tier 2) |
|
|
1147
|
-
| Skills | `phase-design/SKILL.md` | Atualizado (+Passo 1.5) |
|
|
1148
|
-
| Tests | `doctor.test.js` | Atualizado (tier 2 count) |
|
|
1149
|
-
| Tests | `contracts-levels.test.js` | Novo (4 testes) |
|
|
1150
|
-
|
|
1151
|
-
**13 tarefas — ~2h de execução com TDD.**
|
|
1152
|
-
|
|
1153
|
-
---
|
|
1154
|
-
|
|
1155
|
-
*MORPH-SPEC by Polymorphism Tech*
|