dflow-sdd-ddd 0.3.0 → 0.4.0
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/CHANGELOG.md +92 -0
- package/README.en.md +5 -5
- package/README.md +8 -8
- package/docs/examples-by-stack.md +516 -0
- package/docs/release-versioning-policy.md +13 -0
- package/lib/init.js +187 -24
- package/package.json +1 -1
- package/templates/brownfield/scaffolding/CLAUDE-md-snippet.md +25 -15
- package/templates/brownfield/scaffolding/Git-principles-gitflow.md +1 -1
- package/templates/brownfield/scaffolding/Git-principles-trunk.md +1 -1
- package/templates/brownfield/scaffolding/_conventions.md +1 -1
- package/templates/brownfield/scaffolding/_overview.md +40 -29
- package/templates/brownfield/templates/CLAUDE.md +25 -17
- package/templates/brownfield/templates/context-definition.md +4 -4
- package/templates/brownfield/templates/context-map.md +1 -1
- package/templates/brownfield/templates/lightweight-spec.md +3 -1
- package/templates/brownfield/templates/models.md +1 -1
- package/templates/brownfield/templates/phase-spec.md +10 -8
- package/templates/brownfield/templates/tech-debt.md +2 -2
- package/templates/greenfield/scaffolding/CLAUDE-md-snippet.md +6 -6
- package/templates/greenfield/scaffolding/Git-principles-gitflow.md +2 -2
- package/templates/greenfield/scaffolding/Git-principles-trunk.md +2 -2
- package/templates/greenfield/scaffolding/_overview.md +29 -11
- package/templates/greenfield/templates/CLAUDE.md +5 -5
|
@@ -44,6 +44,19 @@ Examples:
|
|
|
44
44
|
- Changing command behavior in a way that invalidates existing docs.
|
|
45
45
|
- Replacing a workflow contract that AI agents rely on.
|
|
46
46
|
|
|
47
|
+
Counter-examples (NOT breaking):
|
|
48
|
+
|
|
49
|
+
- Replacing stack-specific framing with stack-neutral umbrella terms when
|
|
50
|
+
the architectural meaning is unchanged (e.g., "Code-Behind" →
|
|
51
|
+
"delivery/entrypoint code") — adopter's existing files are not
|
|
52
|
+
rewritten by Dflow.
|
|
53
|
+
- Renaming an init placeholder if the previous name still resolves via a
|
|
54
|
+
backward-compat alias and the substituted value is identical (e.g.,
|
|
55
|
+
`{ASP.NET Core version}` continuing to resolve while `{Framework version}`
|
|
56
|
+
becomes canonical).
|
|
57
|
+
- Adding a new optional placeholder; existing templates that don't use it
|
|
58
|
+
are unaffected.
|
|
59
|
+
|
|
47
60
|
## Release Ownership
|
|
48
61
|
|
|
49
62
|
Dflow currently has four release surfaces:
|
package/lib/init.js
CHANGED
|
@@ -383,7 +383,16 @@ async function detectProjectSignals(cwd) {
|
|
|
383
383
|
name.endsWith('.csproj') ||
|
|
384
384
|
name === 'program.cs' ||
|
|
385
385
|
name === 'startup.cs' ||
|
|
386
|
-
name === 'package.json'
|
|
386
|
+
name === 'package.json' ||
|
|
387
|
+
name === 'pom.xml' ||
|
|
388
|
+
name === 'build.gradle' ||
|
|
389
|
+
name === 'build.gradle.kts' ||
|
|
390
|
+
name === 'pyproject.toml' ||
|
|
391
|
+
name === 'requirements.txt' ||
|
|
392
|
+
name === 'go.mod' ||
|
|
393
|
+
name === 'cargo.toml' ||
|
|
394
|
+
name === 'composer.json' ||
|
|
395
|
+
name === 'gemfile'
|
|
387
396
|
);
|
|
388
397
|
|
|
389
398
|
const hasWebFormsFiles = Array.from(baseNames).some((name) =>
|
|
@@ -412,9 +421,36 @@ async function detectProjectSignals(cwd) {
|
|
|
412
421
|
trackHint = 'brownfield';
|
|
413
422
|
}
|
|
414
423
|
|
|
424
|
+
const stackHints = [];
|
|
425
|
+
if (coreSignal || webFormsSignal || csprojFiles.length > 0) {
|
|
426
|
+
stackHints.push('dotnet');
|
|
427
|
+
}
|
|
428
|
+
if (baseNames.has('pom.xml') || baseNames.has('build.gradle') || baseNames.has('build.gradle.kts')) {
|
|
429
|
+
stackHints.push('java');
|
|
430
|
+
}
|
|
431
|
+
if (baseNames.has('package.json')) {
|
|
432
|
+
stackHints.push('nodejs');
|
|
433
|
+
}
|
|
434
|
+
if (baseNames.has('pyproject.toml') || baseNames.has('requirements.txt')) {
|
|
435
|
+
stackHints.push('python');
|
|
436
|
+
}
|
|
437
|
+
if (baseNames.has('go.mod')) {
|
|
438
|
+
stackHints.push('go');
|
|
439
|
+
}
|
|
440
|
+
if (baseNames.has('cargo.toml')) {
|
|
441
|
+
stackHints.push('rust');
|
|
442
|
+
}
|
|
443
|
+
if (baseNames.has('composer.json')) {
|
|
444
|
+
stackHints.push('php');
|
|
445
|
+
}
|
|
446
|
+
if (baseNames.has('gemfile')) {
|
|
447
|
+
stackHints.push('ruby');
|
|
448
|
+
}
|
|
449
|
+
|
|
415
450
|
return {
|
|
416
451
|
hasSourceTree: hasSourceTree || relNames.has('src'),
|
|
417
|
-
trackHint
|
|
452
|
+
trackHint,
|
|
453
|
+
stackHints
|
|
418
454
|
};
|
|
419
455
|
}
|
|
420
456
|
|
|
@@ -1104,13 +1140,20 @@ async function readPackagedTemplate(edition, sourceRel) {
|
|
|
1104
1140
|
}
|
|
1105
1141
|
}
|
|
1106
1142
|
|
|
1143
|
+
const PLACEHOLDER_ALIASES = {
|
|
1144
|
+
'{Framework version}': ['{ASP.NET Core version}', '{ASP.NET WebForms version}', '{.NET Framework version}'],
|
|
1145
|
+
'{ORM / persistence}': ['{ORM / Data Access}'],
|
|
1146
|
+
'{ORM version}': ['{EF Core version}'],
|
|
1147
|
+
'{Mediator}': ['{MediatR version}']
|
|
1148
|
+
};
|
|
1149
|
+
|
|
1107
1150
|
function buildSubstitutionMap(cwd, answers) {
|
|
1108
1151
|
const extracted = extractTechStackPlaceholders(answers.techStackSummary);
|
|
1109
1152
|
const gitSelection = answers.optionalFiles.filter((key) => key === 'git-trunk' || key === 'git-flow');
|
|
1110
1153
|
const gitStyle = gitSelection.length === 1 ? (gitSelection[0] === 'git-trunk' ? 'trunk' : 'gitflow') : null;
|
|
1111
1154
|
const systemName = path.basename(cwd);
|
|
1112
1155
|
|
|
1113
|
-
|
|
1156
|
+
const map = new Map([
|
|
1114
1157
|
['{YYYY-MM-DD}', currentLocalDate()],
|
|
1115
1158
|
['{System Name}', systemName],
|
|
1116
1159
|
['{系統名稱}', systemName],
|
|
@@ -1120,15 +1163,26 @@ function buildSubstitutionMap(cwd, answers) {
|
|
|
1120
1163
|
['{migration-context}', answers.migrationContext],
|
|
1121
1164
|
['{prose-language}', answers.proseLanguage],
|
|
1122
1165
|
['{dflow-version}', pkg.version],
|
|
1123
|
-
['{
|
|
1124
|
-
['{
|
|
1125
|
-
['{
|
|
1166
|
+
['{Language}', extracted.language || '{Language}'],
|
|
1167
|
+
['{Framework}', extracted.framework || '{Framework}'],
|
|
1168
|
+
['{Framework version}', extracted.frameworkVersion || '{Framework version}'],
|
|
1169
|
+
['{ORM / persistence}', extracted.ormPersistence || '{ORM / persistence}'],
|
|
1170
|
+
['{ORM version}', extracted.ormVersion || '{ORM version}'],
|
|
1171
|
+
['{Mediator}', extracted.mediator || '{Mediator}'],
|
|
1126
1172
|
['{Test framework}', extracted.testFramework || '{Test framework}'],
|
|
1127
|
-
['{ASP.NET WebForms version}', extracted.webFormsVersion || '{ASP.NET WebForms version}'],
|
|
1128
|
-
['{.NET Framework version}', extracted.dotNetFrameworkVersion || '{.NET Framework version}'],
|
|
1129
|
-
['{ORM / Data Access}', extracted.ormDataAccess || '{ORM / Data Access}'],
|
|
1130
1173
|
['{gitflow|trunk}', gitStyle || '{gitflow|trunk}']
|
|
1131
1174
|
]);
|
|
1175
|
+
|
|
1176
|
+
for (const [canonical, aliases] of Object.entries(PLACEHOLDER_ALIASES)) {
|
|
1177
|
+
const value = map.get(canonical);
|
|
1178
|
+
if (value === undefined) continue;
|
|
1179
|
+
const canonicalResolved = value !== canonical;
|
|
1180
|
+
for (const alias of aliases) {
|
|
1181
|
+
map.set(alias, canonicalResolved ? value : alias);
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
return map;
|
|
1132
1186
|
}
|
|
1133
1187
|
|
|
1134
1188
|
function substitutePlaceholders(content, substitution) {
|
|
@@ -1139,19 +1193,104 @@ function substitutePlaceholders(content, substitution) {
|
|
|
1139
1193
|
return result;
|
|
1140
1194
|
}
|
|
1141
1195
|
|
|
1196
|
+
const LANGUAGE_PATTERNS = [
|
|
1197
|
+
/\bC#\s*[0-9]+(?:\.[0-9]+)?\b/i,
|
|
1198
|
+
/\bC#\b/,
|
|
1199
|
+
/\bTypeScript\s*[0-9]+(?:\.[0-9]+)?\b/i,
|
|
1200
|
+
/\bTypeScript\b/i,
|
|
1201
|
+
/\bJavaScript\b/i,
|
|
1202
|
+
/\bKotlin\s*[0-9]+(?:\.[0-9]+)?\b/i,
|
|
1203
|
+
/\bKotlin\b/i,
|
|
1204
|
+
/\bJava\s*[0-9]+\b/i,
|
|
1205
|
+
/\bJava\b/i,
|
|
1206
|
+
/\bPython\s*[0-9]+(?:\.[0-9]+)?\b/i,
|
|
1207
|
+
/\bPython\b/i,
|
|
1208
|
+
/\bGolang\b/i,
|
|
1209
|
+
/\bGo\s*[0-9]+(?:\.[0-9]+)?\b/i,
|
|
1210
|
+
/\bPHP\s*[0-9]+(?:\.[0-9]+)?\b/i,
|
|
1211
|
+
/\bPHP\b/i,
|
|
1212
|
+
/\bRuby\b/i
|
|
1213
|
+
];
|
|
1214
|
+
|
|
1215
|
+
const FRAMEWORK_VERSION_PATTERNS = [
|
|
1216
|
+
/\bASP\.?NET\s+Core\s*[0-9]+(?:\.[0-9]+)?\b/i,
|
|
1217
|
+
/\b(?:ASP\.?NET\s+WebForms|WebForms)(?:\s+[0-9]+(?:\.[0-9]+)?)?\b/i,
|
|
1218
|
+
/\b\.NET\s+Framework\s*[0-9]+(?:\.[0-9]+)?\b/i,
|
|
1219
|
+
/\bSpring\s+Boot\s*[0-9]+(?:\.[0-9]+)?\b/i,
|
|
1220
|
+
/\bSpring\s+MVC\b/i,
|
|
1221
|
+
/\bNestJS\s*[0-9]+(?:\.[0-9]+)?\b/i,
|
|
1222
|
+
/\bFastify\s*[0-9]+(?:\.[0-9]+)?\b/i,
|
|
1223
|
+
/\bExpress(?:\.js)?\s*[0-9]+(?:\.[0-9]+)?\b/i,
|
|
1224
|
+
/\bDjango\s*[0-9]+(?:\.[0-9]+)?\b/i,
|
|
1225
|
+
/\bFastAPI\s*[0-9]+(?:\.[0-9]+)?\b/i,
|
|
1226
|
+
/\bFlask\s*[0-9]+(?:\.[0-9]+)?\b/i,
|
|
1227
|
+
/\bGin\s+v?[0-9]+(?:\.[0-9]+)?\b/i,
|
|
1228
|
+
/\bEcho\s+v?[0-9]+(?:\.[0-9]+)?\b/i,
|
|
1229
|
+
/\bLaravel\s*[0-9]+(?:\.[0-9]+)?\b/i
|
|
1230
|
+
];
|
|
1231
|
+
|
|
1232
|
+
const FRAMEWORK_PATTERNS = [
|
|
1233
|
+
/\bASP\.?NET\s+Core\b/i,
|
|
1234
|
+
/\b(?:ASP\.?NET\s+WebForms|WebForms)\b/i,
|
|
1235
|
+
/\b\.NET\s+Framework\b/i,
|
|
1236
|
+
/\bSpring\s+Boot\b/i,
|
|
1237
|
+
/\bSpring\s+MVC\b/i,
|
|
1238
|
+
/\bNestJS\b/i,
|
|
1239
|
+
/\bFastify\b/i,
|
|
1240
|
+
/\bExpress(?:\.js)?\b/i,
|
|
1241
|
+
/\bDjango\b/i,
|
|
1242
|
+
/\bFastAPI\b/i,
|
|
1243
|
+
/\bFlask\b/i,
|
|
1244
|
+
/\bGin\b/i,
|
|
1245
|
+
/\bEcho\b/i,
|
|
1246
|
+
/\bLaravel\b/i
|
|
1247
|
+
];
|
|
1248
|
+
|
|
1249
|
+
const ORM_VERSION_PATTERNS = [
|
|
1250
|
+
/\b(?:EF\s+Core|Entity\s+Framework\s+Core)\s*[0-9]+(?:\.[0-9]+)?\b/i,
|
|
1251
|
+
/\b(?:EF6|Entity\s+Framework\s+6)\b/i,
|
|
1252
|
+
/\bHibernate\s*[0-9]+(?:\.[0-9]+)?\b/i,
|
|
1253
|
+
/\bSpring\s+Data\s+JPA\b/i,
|
|
1254
|
+
/\bJPA\s*[0-9]+(?:\.[0-9]+)?\b/i,
|
|
1255
|
+
/\bSQLAlchemy\s*[0-9]+(?:\.[0-9]+)?\b/i,
|
|
1256
|
+
/\bPrisma\s*[0-9]+(?:\.[0-9]+)?\b/i,
|
|
1257
|
+
/\bTypeORM\s*[0-9]+(?:\.[0-9]+)?\b/i,
|
|
1258
|
+
/\bMikro-?ORM\s*[0-9]+(?:\.[0-9]+)?\b/i,
|
|
1259
|
+
/\bGORM\s+v?[0-9]+(?:\.[0-9]+)?\b/i,
|
|
1260
|
+
/\bEloquent\s*[0-9]+(?:\.[0-9]+)?\b/i,
|
|
1261
|
+
/\bDoctrine\s*[0-9]+(?:\.[0-9]+)?\b/i
|
|
1262
|
+
];
|
|
1263
|
+
|
|
1264
|
+
const ORM_PERSISTENCE_PATTERNS = [
|
|
1265
|
+
/\b(?:EF\s+Core|Entity\s+Framework\s+Core)\b/i,
|
|
1266
|
+
/\b(?:EF6|Entity\s+Framework\s+6|Dapper|ADO\.NET)\b/i,
|
|
1267
|
+
/\bHibernate\b/i,
|
|
1268
|
+
/\bSpring\s+Data\s+JPA\b/i,
|
|
1269
|
+
/\bJPA\b/i,
|
|
1270
|
+
/\bSQLAlchemy\b/i,
|
|
1271
|
+
/\bSQLModel\b/i,
|
|
1272
|
+
/\bPrisma\b/i,
|
|
1273
|
+
/\bTypeORM\b/i,
|
|
1274
|
+
/\bMikro-?ORM\b/i,
|
|
1275
|
+
/\bGORM\b/i,
|
|
1276
|
+
/\bsqlx\b/i,
|
|
1277
|
+
/\bEloquent\b/i,
|
|
1278
|
+
/\bDoctrine\b/i
|
|
1279
|
+
];
|
|
1280
|
+
|
|
1142
1281
|
function extractTechStackPlaceholders(text) {
|
|
1143
1282
|
if (!text || text.toLowerCase() === 'unknown') {
|
|
1144
1283
|
return {};
|
|
1145
1284
|
}
|
|
1146
1285
|
|
|
1147
1286
|
return {
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1287
|
+
language: firstPatternMatch(text, LANGUAGE_PATTERNS),
|
|
1288
|
+
framework: firstPatternMatch(text, FRAMEWORK_PATTERNS),
|
|
1289
|
+
frameworkVersion: firstPatternMatch(text, FRAMEWORK_VERSION_PATTERNS),
|
|
1290
|
+
ormPersistence: firstPatternMatch(text, ORM_PERSISTENCE_PATTERNS),
|
|
1291
|
+
ormVersion: firstPatternMatch(text, ORM_VERSION_PATTERNS),
|
|
1292
|
+
mediator: firstMatch(text, /\bMediatR\s*[0-9]+(?:\.[0-9]+)?\b/i),
|
|
1293
|
+
testFramework: extractTestFramework(text)
|
|
1155
1294
|
};
|
|
1156
1295
|
}
|
|
1157
1296
|
|
|
@@ -1160,15 +1299,38 @@ function firstMatch(text, regex) {
|
|
|
1160
1299
|
return match ? match[0] : null;
|
|
1161
1300
|
}
|
|
1162
1301
|
|
|
1163
|
-
function
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1302
|
+
function firstPatternMatch(text, patterns) {
|
|
1303
|
+
for (const pattern of patterns) {
|
|
1304
|
+
const match = text.match(pattern);
|
|
1305
|
+
if (match) {
|
|
1306
|
+
return match[0];
|
|
1307
|
+
}
|
|
1169
1308
|
}
|
|
1170
|
-
|
|
1171
|
-
|
|
1309
|
+
return null;
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
function extractTestFramework(text) {
|
|
1313
|
+
const patterns = [
|
|
1314
|
+
[/\bxUnit\b/i, 'xUnit'],
|
|
1315
|
+
[/\bNUnit\b/i, 'NUnit'],
|
|
1316
|
+
[/\bMSTest\b/i, 'MSTest'],
|
|
1317
|
+
[/\bJUnit\s*[0-9]+\b/i, null],
|
|
1318
|
+
[/\bJUnit\b/i, 'JUnit'],
|
|
1319
|
+
[/\bVitest\b/i, 'Vitest'],
|
|
1320
|
+
[/\bJest\b/i, 'Jest'],
|
|
1321
|
+
[/\bMocha\b/i, 'Mocha'],
|
|
1322
|
+
[/\bpytest\b/i, 'pytest'],
|
|
1323
|
+
[/\bunittest\b/i, 'unittest'],
|
|
1324
|
+
[/\bgo\s+test\b/i, 'go test'],
|
|
1325
|
+
[/\bPHPUnit\b/i, 'PHPUnit'],
|
|
1326
|
+
[/\bPest\b/i, 'Pest']
|
|
1327
|
+
];
|
|
1328
|
+
|
|
1329
|
+
for (const [pattern, name] of patterns) {
|
|
1330
|
+
const match = text.match(pattern);
|
|
1331
|
+
if (match) {
|
|
1332
|
+
return name || match[0];
|
|
1333
|
+
}
|
|
1172
1334
|
}
|
|
1173
1335
|
return null;
|
|
1174
1336
|
}
|
|
@@ -1386,6 +1548,7 @@ Recommended next steps:
|
|
|
1386
1548
|
- For a new feature, use the Dflow new-feature workflow when it becomes available as a CLI command.
|
|
1387
1549
|
- For brownfield changes, use the Dflow modify-existing workflow when it becomes available as a CLI command.
|
|
1388
1550
|
- Before generating more specs, make sure dflow/specs/shared/_conventions.md has the correct Prose Language section.
|
|
1551
|
+
- For stack-specific examples (.NET, Java/Spring, Node/TypeScript, Python, Go, PHP/Laravel), see docs/examples-by-stack.md in the Dflow repo.
|
|
1389
1552
|
`);
|
|
1390
1553
|
}
|
|
1391
1554
|
|
package/package.json
CHANGED
|
@@ -21,7 +21,7 @@ when merging into an existing `CLAUDE.md`.
|
|
|
21
21
|
## Snippet to merge into `CLAUDE.md`
|
|
22
22
|
|
|
23
23
|
```markdown
|
|
24
|
-
# Project: {系統名稱} —
|
|
24
|
+
# Project: {系統名稱} — {Framework}
|
|
25
25
|
|
|
26
26
|
**重要:所有開發工作都必須遵循本文件定義的流程。**
|
|
27
27
|
|
|
@@ -33,10 +33,12 @@ when merging into an existing `CLAUDE.md`.
|
|
|
33
33
|
|
|
34
34
|
### Background
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
這是一個運行中的既有系統,使用 {Framework} / {Language},{一句話描述業務領域:例如
|
|
37
37
|
「提供員工費用報銷」/「處理 HR 人事流程」/「訂單管理」}。採用 Dflow
|
|
38
|
-
(SDD/DDD workflow guardian skill
|
|
39
|
-
|
|
38
|
+
(SDD/DDD workflow guardian skill)引導開發流程,逐步將 business logic
|
|
39
|
+
embedded in delivery/entrypoint code(presentation/UI layer、controllers、
|
|
40
|
+
handlers、jobs、message consumers、data pipelines、stored procedures)抽離,
|
|
41
|
+
並朝 target architecture 前進。
|
|
40
42
|
|
|
41
43
|
{選填:補上團隊規模、使用者規模、主要 stakeholders 等 context,
|
|
42
44
|
1-3 行即可。完整內容放在 `dflow/specs/shared/_overview.md`。}
|
|
@@ -46,7 +48,7 @@ ASP.NET Core 做準備。
|
|
|
46
48
|
```
|
|
47
49
|
dflow/specs/
|
|
48
50
|
├── shared/ # 專案級治理文件(由 Dflow CLI init 寫入)
|
|
49
|
-
│ ├── _overview.md #
|
|
51
|
+
│ ├── _overview.md # 系統現況與 target architecture
|
|
50
52
|
│ ├── _conventions.md # 規格撰寫慣例
|
|
51
53
|
│ └── Git-principles-*.md # Git 規範(gitflow 或 trunk 版)
|
|
52
54
|
├── domain/ # 領域知識
|
|
@@ -68,12 +70,20 @@ dflow/specs/
|
|
|
68
70
|
└── tech-debt.md
|
|
69
71
|
|
|
70
72
|
src/
|
|
71
|
-
├── Domain/ #
|
|
73
|
+
├── Domain/ # 抽離的領域邏輯(framework-pure)
|
|
72
74
|
│ ├── {BoundedContext}/
|
|
73
75
|
│ └── SharedKernel/
|
|
74
|
-
└──
|
|
76
|
+
└── Delivery/ # delivery-layer code(entrypoints, controllers, handlers)
|
|
75
77
|
```
|
|
76
78
|
|
|
79
|
+
> **目錄命名說明**:上方 `src/Domain/` / `src/Delivery/` 是 Clean
|
|
80
|
+
> Architecture 通用示意,不限 stack。請依專案實際慣例對應(例:Java/Spring 用
|
|
81
|
+
> `src/main/java/com/example/domain/`、Node/TS 用 `src/domain/` /
|
|
82
|
+
> `src/routes/`、Python 用 `domain/` package、Go 用 `internal/domain/` /
|
|
83
|
+
> `internal/handler/`、.NET 用 `src/{Project}.Domain/` + `.csproj` 分層)。
|
|
84
|
+
> 完整 per-stack 範例見 `docs/examples-by-stack.md`。重點是
|
|
85
|
+
> `src/Domain/`(或對應命名)保持與 delivery/entrypoint code 獨立。
|
|
86
|
+
|
|
77
87
|
---
|
|
78
88
|
|
|
79
89
|
## Development Workflow
|
|
@@ -83,9 +93,9 @@ src/
|
|
|
83
93
|
### Core Principles
|
|
84
94
|
|
|
85
95
|
1. **Spec Before Code** — 沒有規格就不寫實作(依 Ceremony Scaling 調整嚴謹度)
|
|
86
|
-
2. **Domain Extraction** — 業務邏輯屬於 `src/Domain/`,不屬於
|
|
96
|
+
2. **Domain Extraction** — 業務邏輯屬於 `src/Domain/`,不屬於 delivery/entrypoint code
|
|
87
97
|
3. **Ubiquitous Language** — 使用 `dflow/specs/domain/glossary.md` 中定義的術語
|
|
88
|
-
4. **Migration Awareness** —
|
|
98
|
+
4. **Migration Awareness** — 每個決策都要考慮 target architecture
|
|
89
99
|
|
|
90
100
|
### Dflow Skill — Canonical Decision Logic Lives in the Skill
|
|
91
101
|
|
|
@@ -114,7 +124,7 @@ AI 的完整決策樹、Workflow Transparency、Ceremony Scaling 三層判準
|
|
|
114
124
|
|
|
115
125
|
- **Git 分支策略**:見 `dflow/specs/shared/Git-principles-{gitflow|trunk}.md`
|
|
116
126
|
- **規格撰寫慣例**:見 `dflow/specs/shared/_conventions.md`
|
|
117
|
-
-
|
|
127
|
+
- **系統現況與 target architecture**:見 `dflow/specs/shared/_overview.md`
|
|
118
128
|
- {其他專案特有規則,例如「JPY 金額必須以最小貨幣單位(yen)儲存」/
|
|
119
129
|
「所有費用報銷需主管審核」/「跨時區行程以 UTC 記錄」}
|
|
120
130
|
|
|
@@ -122,18 +132,18 @@ AI 的完整決策樹、Workflow Transparency、Ceremony Scaling 三層判準
|
|
|
122
132
|
|
|
123
133
|
此目錄中的程式碼必須遵守(與 Dflow skill 的 Domain Layer Rules 一致):
|
|
124
134
|
|
|
125
|
-
- ❌
|
|
135
|
+
- ❌ 不可引用任何 delivery-framework 命名空間(例:HTTP 請求/回應物件、Session/Cookie context、job runner context、CLI flag parser、ViewState 類等)
|
|
126
136
|
- ❌ 不可直接存取資料庫(使用 interface + Repository pattern)
|
|
127
|
-
- ❌ 不可使用
|
|
137
|
+
- ❌ 不可使用 delivery-framework runtime context(例:HTTP request/response、session/cookie、job runner state、CLI args)
|
|
128
138
|
- ❌ 不可有 UI 相關邏輯
|
|
129
|
-
- ✅
|
|
130
|
-
- ✅ 所有公開行為都能在沒有
|
|
139
|
+
- ✅ 語言純粹的 class(不依賴 delivery framework),可直接搬到 target architecture
|
|
140
|
+
- ✅ 所有公開行為都能在沒有 delivery infrastructure 的情況下測試
|
|
131
141
|
|
|
132
142
|
### AI Collaboration Notes
|
|
133
143
|
|
|
134
144
|
- 遇到開發需求時,優先引導使用 `/dflow:` 命令
|
|
135
145
|
- 回答 Domain 相關問題時,優先參考 `dflow/specs/domain/` 中的文件
|
|
136
|
-
- 發現
|
|
146
|
+
- 發現 delivery/entrypoint code 中的業務邏輯時,建議抽離到 `src/Domain/`
|
|
137
147
|
- 建立分支前,確認命名符合規範且對應 spec 存在
|
|
138
148
|
- 詳細 Git 操作規則見 `dflow/specs/shared/Git-principles-{gitflow|trunk}.md`
|
|
139
149
|
```
|
|
@@ -170,7 +170,7 @@ before making key Git operations:
|
|
|
170
170
|
`behavior.md` reflect the feature's net BR changes
|
|
171
171
|
- [ ] `dflow/specs/domain/glossary.md` updated with any new terms
|
|
172
172
|
- [ ] `dflow/specs/migration/tech-debt.md` updated with any debt discovered
|
|
173
|
-
- [ ] Domain layer (`src/Domain/`) has no
|
|
173
|
+
- [ ] Domain layer (`src/Domain/`) has no delivery-framework references
|
|
174
174
|
|
|
175
175
|
---
|
|
176
176
|
|
|
@@ -134,7 +134,7 @@ and circle the chosen one.
|
|
|
134
134
|
`behavior.md` reflect the feature's net BR changes
|
|
135
135
|
- [ ] `dflow/specs/domain/glossary.md` updated with any new terms
|
|
136
136
|
- [ ] `dflow/specs/migration/tech-debt.md` updated with any debt discovered
|
|
137
|
-
- [ ] Domain layer (`src/Domain/`) has no
|
|
137
|
+
- [ ] Domain layer (`src/Domain/`) has no delivery-framework references
|
|
138
138
|
- [ ] CI green
|
|
139
139
|
- [ ] PR has at least one review approval
|
|
140
140
|
|
|
@@ -108,7 +108,7 @@ situations.
|
|
|
108
108
|
|------------------------------|--------------------|-----|
|
|
109
109
|
| {e.g. Year-end reporting tweak without BR change} | T2 | Touches logic path even though no new BR; extract to `lightweight-spec.md` for trace |
|
|
110
110
|
| {e.g. Pure label / display text translation} | T3 | No BR change; inline row in `_index.md` |
|
|
111
|
-
| {e.g. UI refresh across multiple
|
|
111
|
+
| {e.g. UI refresh across multiple entrypoints} | T1 (project convention) | We treat multi-entrypoint UI/API refresh as T1 for this project even though Dflow default would be T2, because these changes often leak into business logic embedded in delivery/entrypoint code (presentation/UI layer, controllers, handlers, jobs, message consumers, data pipelines, or stored procedures) |
|
|
112
112
|
|
|
113
113
|
If the team disagrees on tier classification for a specific change,
|
|
114
114
|
run through the T3 four-criteria checklist (in the Dflow skill) and
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# System Overview — {System Name}
|
|
4
4
|
|
|
5
5
|
> Created: {YYYY-MM-DD}
|
|
6
|
-
> Scope: current state and
|
|
6
|
+
> Scope: current state and target-architecture direction of {System Name}
|
|
7
7
|
> Audience: team members onboarding to the system + AI assistants reading
|
|
8
8
|
> `dflow/specs/` for context.
|
|
9
9
|
|
|
@@ -30,33 +30,44 @@ delivers. Keep it non-technical enough that a new hire can skim it in
|
|
|
30
30
|
|
|
31
31
|
## Technical Architecture (Current)
|
|
32
32
|
|
|
33
|
-
This project runs on
|
|
34
|
-
progressively prepare for
|
|
33
|
+
This project runs on {Framework}. The SDD/DDD workflow is used to
|
|
34
|
+
progressively extract domain logic and prepare for the target architecture
|
|
35
|
+
(see "Target Architecture Strategy" below).
|
|
35
36
|
|
|
36
37
|
| Item | Current |
|
|
37
38
|
|------|---------|
|
|
38
|
-
| Framework |
|
|
39
|
-
| Language |
|
|
39
|
+
| Framework | {Framework} ({Framework version}) |
|
|
40
|
+
| Language | {Language} |
|
|
40
41
|
| Database | {e.g. SQL Server 2019, MySQL 8.0} |
|
|
41
|
-
| ORM /
|
|
42
|
-
|
|
|
43
|
-
| Auth | {e.g.
|
|
44
|
-
| Hosting | {e.g.
|
|
42
|
+
| ORM / persistence | {ORM / persistence} ({ORM version}) |
|
|
43
|
+
| Delivery / entrypoint | {Framework} |
|
|
44
|
+
| Auth | {e.g. session auth, OAuth/OIDC, API key, internal SSO} |
|
|
45
|
+
| Hosting | {e.g. on-prem VM, container platform, managed app platform} |
|
|
45
46
|
|
|
46
47
|
### Code Layout (High Level)
|
|
47
48
|
|
|
49
|
+
> **Note on directory naming**: The tree below uses generic Clean
|
|
50
|
+
> Architecture folder names (`src/Domain/`, `src/Delivery/`). Adapt to your
|
|
51
|
+
> stack's conventions — for example, Java/Spring `src/main/java/com/example/domain/`,
|
|
52
|
+
> Node/TS `src/domain/` + `src/routes/`, Python `domain/` package, Go
|
|
53
|
+
> `internal/domain/` + `internal/handler/`, PHP/Laravel `app/Domain/` +
|
|
54
|
+
> `app/Http/`, .NET `src/{Project}.Domain/` + `src/{Project}.WebAPI/`
|
|
55
|
+
> (separate `.csproj` per layer). For full per-stack examples see
|
|
56
|
+
> `docs/examples-by-stack.md`.
|
|
57
|
+
|
|
48
58
|
```
|
|
49
59
|
src/
|
|
50
|
-
├── Domain/ # Extracted domain logic (pure
|
|
60
|
+
├── Domain/ # Extracted domain logic (framework-pure; target architecture)
|
|
51
61
|
│ ├── {BoundedContext}/
|
|
52
62
|
│ └── SharedKernel/
|
|
53
|
-
└──
|
|
63
|
+
└── Delivery/ # delivery-layer code (entrypoints, controllers, handlers)
|
|
54
64
|
```
|
|
55
65
|
|
|
56
66
|
The `src/Domain/` directory is where business logic lives **as it is
|
|
57
|
-
extracted** from
|
|
58
|
-
|
|
59
|
-
|
|
67
|
+
extracted** from delivery/entrypoint code (presentation/UI layer, controllers,
|
|
68
|
+
handlers, jobs, message consumers, data pipelines, or stored procedures).
|
|
69
|
+
Everything in `src/Domain/` must be framework-pure with no delivery-framework
|
|
70
|
+
dependencies (see `CLAUDE.md` and the Dflow skill for the full rule set).
|
|
60
71
|
|
|
61
72
|
---
|
|
62
73
|
|
|
@@ -66,34 +77,34 @@ skill for the full rule set).
|
|
|
66
77
|
top 3–5 known issues the team wants to address as part of migration.
|
|
67
78
|
Link each to `migration/tech-debt.md` entries if they exist.}
|
|
68
79
|
|
|
69
|
-
1. {e.g. Business logic
|
|
70
|
-
calculations
|
|
71
|
-
2. {e.g. Direct SQL in
|
|
80
|
+
1. {e.g. Business logic embedded in delivery/entrypoint code; duplicated
|
|
81
|
+
calculations across multiple flows}
|
|
82
|
+
2. {e.g. Direct SQL in delivery/entrypoint code; inconsistent error handling}
|
|
72
83
|
3. {e.g. Magic numbers / undocumented statuses}
|
|
73
84
|
|
|
74
85
|
---
|
|
75
86
|
|
|
76
|
-
##
|
|
87
|
+
## Target Architecture Strategy
|
|
77
88
|
|
|
78
|
-
This system is being prepared for
|
|
79
|
-
|
|
89
|
+
This system is being prepared for the project's target architecture. The
|
|
90
|
+
strategy follows four principles; expand / adapt each to this project:
|
|
80
91
|
|
|
81
|
-
- **Migration Awareness** — Every feature decision considers
|
|
82
|
-
|
|
92
|
+
- **Migration Awareness** — Every feature decision considers the target
|
|
93
|
+
architecture. We ask "does this make the target architecture harder or easier?"
|
|
83
94
|
- **Domain Extraction** — Business logic gradually moves from
|
|
84
|
-
|
|
85
|
-
extract a little more.
|
|
95
|
+
delivery/entrypoint code to `src/Domain/`. Each feature is an
|
|
96
|
+
opportunity to extract a little more.
|
|
86
97
|
- **Dual-Track Parallel** — We do not force-rewrite existing code; new
|
|
87
|
-
development preferentially uses the Domain layer, and legacy
|
|
98
|
+
development preferentially uses the Domain layer, and legacy entrypoints
|
|
88
99
|
get touched only when they are being modified.
|
|
89
|
-
- **Pragmatic First** —
|
|
100
|
+
- **Pragmatic First** — Target-architecture work does not block feature delivery. If
|
|
90
101
|
a deadline is tight, record the debt in `migration/tech-debt.md`
|
|
91
102
|
and continue.
|
|
92
103
|
|
|
93
|
-
### Target Architecture
|
|
104
|
+
### Target Architecture
|
|
94
105
|
|
|
95
|
-
{1-2 sentences describing where this system is heading: e.g. "
|
|
96
|
-
|
|
106
|
+
{1-2 sentences describing where this system is heading: e.g. "{Framework}
|
|
107
|
+
+ Clean Architecture + {ORM / persistence}, deployed to {hosting platform}."
|
|
97
108
|
Link to any ADR or migration plan doc if one exists.}
|
|
98
109
|
|
|
99
110
|
---
|