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
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,98 @@
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
## 0.4.0 — 2026-05-19 — Language-agnostic templates and skill source
|
|
10
|
+
|
|
11
|
+
**Proposals**:PROPOSAL-030(language-agnostic templates and skill source;
|
|
12
|
+
post-v0.3.0 follow-up; cross-model reviewed with Codex CLI)
|
|
13
|
+
|
|
14
|
+
**變更**:
|
|
15
|
+
|
|
16
|
+
- **`lib/init.js` canonical placeholders + backward-compat aliases**(PROPOSAL-030 Group 1):
|
|
17
|
+
- 新增 canonical placeholders:`{Language}` / `{Framework}` /
|
|
18
|
+
`{Framework version}` / `{ORM / persistence}` / `{ORM version}` /
|
|
19
|
+
`{Mediator}`(與既有 `{Test framework}` 對齊)
|
|
20
|
+
- 既有 .NET-specific placeholder(`{ASP.NET Core version}` /
|
|
21
|
+
`{EF Core version}` / `{MediatR version}` / `{ASP.NET WebForms version}` /
|
|
22
|
+
`{.NET Framework version}` / `{ORM / Data Access}`)保留為 backward-compat
|
|
23
|
+
alias,substitute 同 canonical 值
|
|
24
|
+
- `extractTechStackPlaceholders` regex 擴充:Spring Boot / NestJS /
|
|
25
|
+
Fastify / Express / Django / FastAPI / Flask / Gin / Echo / Laravel
|
|
26
|
+
framework;EF Core / Hibernate / Spring Data JPA / SQLAlchemy /
|
|
27
|
+
Prisma / TypeORM / MikroORM / GORM / Eloquent / Doctrine ORM;JUnit /
|
|
28
|
+
Vitest / Jest / Mocha / pytest / unittest / go test / PHPUnit / Pest
|
|
29
|
+
test framework;C# / TypeScript / JavaScript / Kotlin / Java /
|
|
30
|
+
Python / Go / PHP / Ruby language
|
|
31
|
+
- `detectProjectSignals` 加 `stackHints`(從 `pom.xml` / `package.json` /
|
|
32
|
+
`pyproject.toml` / `go.mod` / `Cargo.toml` / `composer.json` / `Gemfile`
|
|
33
|
+
fingerprint)— **不**自動推 `trackHint`,user 選擇仍主導
|
|
34
|
+
- `printNextSteps` 加 `docs/examples-by-stack.md` pointer
|
|
35
|
+
- **Brownfield skill source + templates language-agnostic**(Group 2 + Group 2 follow-up):
|
|
36
|
+
- `Code-Behind` → umbrella `business logic embedded in delivery/entrypoint
|
|
37
|
+
code`,context 簡寫 `presentation-layer logic` (web/UI/API) 或
|
|
38
|
+
`entrypoint-layer logic` (CLI/batch/pipeline)
|
|
39
|
+
- `ASP.NET Core migration` / `future ASP.NET Core migration` →
|
|
40
|
+
`target architecture`
|
|
41
|
+
- `No System.Web references` → `No delivery-framework references`,明列
|
|
42
|
+
HTTP req/resp、session/cookie、job-runner context、CLI flag parser、
|
|
43
|
+
ViewState equivalents 等多 stack 示例
|
|
44
|
+
- `純 C# class` → `framework-pure class`
|
|
45
|
+
- Layer tag `PAGE` → `DELIVERY`(含 lightweight-spec 的 layer tags
|
|
46
|
+
說明擴及 web/CLI/job/pipeline)
|
|
47
|
+
- SKILL.md 新增 `## Scope: When Dflow Brownfield Applies` 段,明標適用
|
|
48
|
+
business-rule-bearing legacy systems / 不適用純 infra script 或無
|
|
49
|
+
domain model pipeline
|
|
50
|
+
- 目錄樹 `src/Pages/` → `src/Delivery/`(與 umbrella term 對齊)
|
|
51
|
+
- SKILL.md / templates/CLAUDE.md / scaffolding/CLAUDE-md-snippet.md /
|
|
52
|
+
scaffolding/_overview.md 在 directory tree 前加 stack-adapt callout
|
|
53
|
+
- **Greenfield skill source + templates language-agnostic**(Group 3 + Group 3 follow-up):
|
|
54
|
+
- 檔案標題 `# Project — ASP.NET Core + DDD` →
|
|
55
|
+
`# Project — Clean Architecture + DDD`
|
|
56
|
+
- `# X Workflow — ASP.NET Core` → `# X Workflow — Greenfield Clean Architecture`
|
|
57
|
+
- Cross-edition `Same as WebForms version` → `Same as Brownfield edition`
|
|
58
|
+
- `_overview.md` stack table 全用 canonical placeholders
|
|
59
|
+
- `init-project-flow.md` Q2 prompt + substitution table 改 canonical
|
|
60
|
+
- Project Layout `{Project}.Domain` `.NET` 慣例保留 + 加詳細 callout
|
|
61
|
+
列 Java/Spring、Node/TS、Python、Go、PHP/Laravel 對應
|
|
62
|
+
- `PRACTICE_PLAN_tw.md` 開頭加 disclaimer:練習以 .NET 為示例、
|
|
63
|
+
Dflow 本身 language-agnostic
|
|
64
|
+
- Git-principles-{gitflow,trunk} 的 `NuGet dependencies` →
|
|
65
|
+
`external package dependencies`
|
|
66
|
+
- **`docs/examples-by-stack.md` 新增**(Group 4):520 行 public
|
|
67
|
+
appendix,列 7 stack 段(.NET ASP.NET Core greenfield / .NET WebForms
|
|
68
|
+
brownfield / Java Spring Boot / Node TypeScript NestJS / Python FastAPI /
|
|
69
|
+
Go Gin / PHP Laravel),每段:substituted stack table + project
|
|
70
|
+
layout + per-stack domain purity check + test framework + run
|
|
71
|
+
command。`README.md` / `README.en.md` 在 Project Tracks 段加 link。
|
|
72
|
+
- **`planning/init-contract-spec.md` 同步**(Group 1):placeholder
|
|
73
|
+
inventory + alias table + detectProjectSignals stackHints 文件對齊
|
|
74
|
+
|
|
75
|
+
**Breaking / migration notes**:
|
|
76
|
+
|
|
77
|
+
- **無 runtime API 破壞**:v0.3.0 寫入 user project 的 `CLAUDE.md` /
|
|
78
|
+
`_overview.md` 等檔案 Dflow 不會自動覆蓋;backward-compat alias 只保護
|
|
79
|
+
future init 動作的 placeholder 替換不破壞,不主動 migrate 既有檔案
|
|
80
|
+
- 若要對齊新風格,請參考 `docs/examples-by-stack.md` 對既有 init 產出的
|
|
81
|
+
檔案手動修改
|
|
82
|
+
- AI agent 對話中讀 skill source 會自動切到新術語(Code-Behind →
|
|
83
|
+
delivery/entrypoint code、ASP.NET Core migration → target architecture);
|
|
84
|
+
若 user 有自製 prompt / shortcut 用到舊術語,建議同步更新但**不強制**
|
|
85
|
+
|
|
86
|
+
**驗證**:
|
|
87
|
+
|
|
88
|
+
- `npm test`(含新增 Java/Spring Boot greenfield init e2e,assert 不殘留 .NET literals)
|
|
89
|
+
- `scripts/check-repo-consistency.sh`(source ↔ packaged mirror diff clean)
|
|
90
|
+
- 全 repo grep `Code-Behind` / `ASP.NET` / `WebForms` / `NuGet` 在 active skill / template surface 範圍內無殘留(archive/ 與 test fixture 刻意保留)
|
|
91
|
+
|
|
92
|
+
**邊界**:
|
|
93
|
+
|
|
94
|
+
- Tutorial 目錄 `tutorial/**` scenario prose 為教學劇情 artifact,明確
|
|
95
|
+
out of scope(PROPOSAL-030 §影響範圍 已宣告)
|
|
96
|
+
- `test/smoke.mjs` 既有 .NET fixture 保留作為 brownfield init 測試輸入
|
|
97
|
+
- Dist projection 在所有 Group merge 完後一次性 export(Director-only)
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
9
101
|
## 0.3.0 — 2026-05-18 — Workflow flow completion, terminology disambiguation, bilingual public docs
|
|
10
102
|
|
|
11
103
|
**Proposals**:PROPOSAL-022(CLI install 推薦路徑)、PROPOSAL-023(line-ending normalization)、PROPOSAL-024(README zh-TW + migration reframing)、PROPOSAL-025(Phase 術語拆解:Step Gate / Activity)、PROPOSAL-026(`/dflow:new-phase` 補 implementation / verification)、PROPOSAL-027(handoff lifecycle,dev-only)、PROPOSAL-028(`docs/*.md` 雙語化)、PROPOSAL-029(tutorial 檔名 align)。
|
package/README.en.md
CHANGED
|
@@ -113,15 +113,15 @@ Greenfield and Brownfield workflows with worked spec outputs, see the
|
|
|
113
113
|
| **Greenfield** | You are starting a new system or a new bounded area with room to shape architecture and domain model early. | Clean spec baseline, domain model ownership, feature-by-feature implementation through SDD. |
|
|
114
114
|
| **Brownfield** | You are adding or changing behavior in an existing codebase where business rules may already be scattered. | Progressive domain extraction, safer change planning, and migration-ready domain knowledge. |
|
|
115
115
|
|
|
116
|
-
These tracks
|
|
116
|
+
These tracks distinguish the project's starting state (new vs existing codebase), not a framework choice; the design makes no assumption about language or stack. Dflow should be read as a workflow system for software teams that want AI assistance without giving up domain clarity.
|
|
117
117
|
|
|
118
|
-
|
|
118
|
+
Filled-in examples for common stacks (.NET, Java/Spring, Node/TypeScript, Python, Go, PHP/Laravel) are in [`docs/examples-by-stack.md`](./docs/examples-by-stack.md).
|
|
119
119
|
|
|
120
|
-
|
|
120
|
+
### Track Choice and Migration
|
|
121
121
|
|
|
122
|
-
Track is fixed at `dflow init` time and **cannot be switched in-place** (there is no `/dflow:switch-to-greenfield` command). Brownfield is by design a preparation path toward Greenfield:
|
|
122
|
+
Track is fixed at `dflow init` time and **cannot be switched in-place** (there is no `/dflow:switch-to-greenfield` command). Brownfield is by design a preparation path toward Greenfield: domain code extracted into the project's domain layer (e.g., `src/Domain/`) and the domain documents under `dflow/specs/domain/` (glossary, rules, models, events) are all migration-ready assets — at the eventual rewrite (a new project + fresh `dflow init` with Greenfield track), they can be lifted directly. `dflow/specs/migration/tech-debt.md` is the brownfield-specific migration debt log.
|
|
123
123
|
|
|
124
|
-
Per-BC migration is also supported — once a Bounded Context's logic is fully extracted into
|
|
124
|
+
Per-BC migration is also supported — once a Bounded Context's business logic is fully extracted into the domain layer and the presentation layer is reduced to UI binding, that BC is already in a Clean Architecture state; the whole system doesn't have to switch in one go. The brownfield `/dflow:modify-existing`'s "assess presentation-layer business logic" step becomes a no-op for that BC naturally.
|
|
125
125
|
|
|
126
126
|
## Workflow Model
|
|
127
127
|
|
package/README.md
CHANGED
|
@@ -83,24 +83,24 @@ dflow doctor
|
|
|
83
83
|
|
|
84
84
|
第一次採用建議用 branch 或一次性試用專案,讓團隊先檢視產生的 `dflow/specs/` 工作區,再把流程引入正式程式碼。
|
|
85
85
|
|
|
86
|
-
完整評估流程(init 產生哪些檔案、AI
|
|
86
|
+
完整評估流程(init 產生哪些檔案、AI 工具支援、模式選擇、30 分鐘試用 playbook)見 [評估 Dflow](docs/evaluating-dflow.md)。Greenfield 與 Brownfield 端到端劇情走完與規格範例見 [`tutorial/`](tutorial/README.md) 索引。
|
|
87
87
|
|
|
88
|
-
##
|
|
88
|
+
## 專案模式
|
|
89
89
|
|
|
90
|
-
|
|
|
90
|
+
| 模式 | 何時用 | 主要產出 |
|
|
91
91
|
|---|---|---|
|
|
92
92
|
| **Greenfield** | 新系統或新 bounded area,有空間早期塑形架構與領域模型 | 乾淨的規格 baseline、領域模型歸屬、feature-by-feature SDD 實作 |
|
|
93
93
|
| **Brownfield** | 在既有 codebase 增加或修改行為,業務規則可能已散落各處 | 漸進的領域抽出、更安全的變更規劃、可遷移的領域知識 |
|
|
94
94
|
|
|
95
|
-
|
|
95
|
+
兩種模式區分的是專案起始狀態(新建 vs 既有 codebase),不是 framework 品牌;Dflow 對語言與 stack 不做假設,workflow、tier 制與文件模型可套用任何技術組合。本質是給「希望 AI 協助、又不願放棄領域清晰度」的軟體團隊使用的 workflow 系統。
|
|
96
96
|
|
|
97
|
-
|
|
97
|
+
各 stack(.NET / Java-Spring / Node-TS / Python / Go / PHP-Laravel)的填好範例見 [`docs/examples-by-stack.md`](./docs/examples-by-stack.md)。
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
### 模式選擇與遷移
|
|
100
100
|
|
|
101
|
-
|
|
101
|
+
模式在 `dflow init` 時選定、之後**不能 in-place 切換**(沒有 `/dflow:switch-to-greenfield` 之類的指令)。Brownfield 設計上是 Greenfield 的前置準備:抽出到專案 domain 層(例如 `src/Domain/`)的領域程式碼,與 `dflow/specs/domain/` 內的領域文件(術語、規則、模型、事件),都是 migration-ready 資產 — 未來要 rewrite 時(建新專案 + 新 `dflow init` 選 Greenfield),可以直接搬過去。`dflow/specs/migration/tech-debt.md` 是 brownfield 專用的遷移債紀錄。
|
|
102
102
|
|
|
103
|
-
也支援「逐 BC(Bounded Context)遷移」— 某個 BC
|
|
103
|
+
也支援「逐 BC(Bounded Context)遷移」— 某個 BC 的業務邏輯已純化到 domain 層、表現層只剩 UI 綁定後,這個 BC 就已是 Clean Architecture 狀態,不必整個 system 一次性切。Brownfield 的 `/dflow:modify-existing` 內「評估表現層業務邏輯」步驟對該 BC 自然會變 no-op。
|
|
104
104
|
|
|
105
105
|
## Workflow 模型
|
|
106
106
|
|
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
# Examples by Stack
|
|
2
|
+
|
|
3
|
+
> A per-stack appendix to Dflow templates. Dflow itself is language- and
|
|
4
|
+
> framework-agnostic; this document shows what canonical placeholders
|
|
5
|
+
> (`{Language}` / `{Framework}` / `{Framework version}` / `{ORM / persistence}`
|
|
6
|
+
> / `{ORM version}` / `{Mediator}` / `{Test framework}`) and stack-neutral
|
|
7
|
+
> prose look like once filled in for common stacks.
|
|
8
|
+
>
|
|
9
|
+
> **How to use this**: after running `dflow init`, open
|
|
10
|
+
> `dflow/specs/shared/_overview.md` and your root `CLAUDE.md`. Replace
|
|
11
|
+
> placeholder rows / directory trees / dependency lists with the snippets
|
|
12
|
+
> from your stack's section below. Adapt as needed — these are starting
|
|
13
|
+
> points, not prescriptions.
|
|
14
|
+
|
|
15
|
+
## Index
|
|
16
|
+
|
|
17
|
+
- [.NET / ASP.NET Core (Greenfield)](#net--aspnet-core-greenfield)
|
|
18
|
+
- [.NET / ASP.NET WebForms (Brownfield)](#net--aspnet-webforms-brownfield)
|
|
19
|
+
- [Java / Spring Boot (Greenfield)](#java--spring-boot-greenfield) — Kotlin/Spring same pattern
|
|
20
|
+
- [Node.js / TypeScript / NestJS (Greenfield)](#nodejs--typescript--nestjs-greenfield)
|
|
21
|
+
- [Python / FastAPI (Greenfield)](#python--fastapi-greenfield) — Django/Flask similar
|
|
22
|
+
- [Go / Gin (Greenfield)](#go--gin-greenfield)
|
|
23
|
+
- [PHP / Laravel (Brownfield migration)](#php--laravel-brownfield-migration)
|
|
24
|
+
|
|
25
|
+
Each section provides:
|
|
26
|
+
|
|
27
|
+
1. **`_overview.md` stack table** — substituted canonical placeholders
|
|
28
|
+
2. **Project layout** — idiomatic directory / package convention
|
|
29
|
+
3. **Domain purity check** — the stack's equivalent of "no delivery-framework references" (used in Git-principles checklist)
|
|
30
|
+
4. **Test framework** — what `{Test framework}` resolves to
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## .NET / ASP.NET Core (Greenfield)
|
|
35
|
+
|
|
36
|
+
### `_overview.md` stack table
|
|
37
|
+
|
|
38
|
+
```markdown
|
|
39
|
+
| Item | Choice |
|
|
40
|
+
|------|--------|
|
|
41
|
+
| Language | C# 12 |
|
|
42
|
+
| Framework | ASP.NET Core 9 |
|
|
43
|
+
| ORM / persistence | Entity Framework Core 9 |
|
|
44
|
+
| Mediator | MediatR 12 |
|
|
45
|
+
| Test framework | xUnit |
|
|
46
|
+
| Database | PostgreSQL 16 |
|
|
47
|
+
| Auth | JWT bearer / OIDC via Azure AD |
|
|
48
|
+
| Hosting | Azure App Service |
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Project layout
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
src/
|
|
55
|
+
├── {Project}.Domain/ # Aggregates, Value Objects, Domain Events
|
|
56
|
+
│ ├── Common/ # Entity, AggregateRoot, ValueObject base classes
|
|
57
|
+
│ ├── {BoundedContext}/
|
|
58
|
+
│ │ ├── Entities/
|
|
59
|
+
│ │ ├── ValueObjects/
|
|
60
|
+
│ │ ├── Events/
|
|
61
|
+
│ │ ├── Services/
|
|
62
|
+
│ │ └── Interfaces/ # Repository / external service interfaces
|
|
63
|
+
│ └── SharedKernel/
|
|
64
|
+
├── {Project}.Application/ # CQRS handlers, validators, DTOs
|
|
65
|
+
│ ├── Common/
|
|
66
|
+
│ └── {BoundedContext}/
|
|
67
|
+
│ ├── Commands/
|
|
68
|
+
│ ├── Queries/
|
|
69
|
+
│ ├── DTOs/
|
|
70
|
+
│ └── EventHandlers/
|
|
71
|
+
├── {Project}.Infrastructure/ # EF Core DbContext, repository impls
|
|
72
|
+
│ ├── Persistence/
|
|
73
|
+
│ ├── Repositories/
|
|
74
|
+
│ └── ExternalServices/
|
|
75
|
+
└── {Project}.WebAPI/ # Presentation: HTTP endpoints
|
|
76
|
+
|
|
77
|
+
tests/
|
|
78
|
+
├── {Project}.Domain.UnitTests/
|
|
79
|
+
├── {Project}.Application.UnitTests/
|
|
80
|
+
└── {Project}.Integration.Tests/
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Domain purity check
|
|
84
|
+
|
|
85
|
+
The Domain project's `.csproj` should have **zero** package references
|
|
86
|
+
outside the allow-list (typically just the `SharedKernel`). Specifically
|
|
87
|
+
forbid:
|
|
88
|
+
|
|
89
|
+
- `Microsoft.AspNetCore.*`, `System.Web`
|
|
90
|
+
- `Microsoft.EntityFrameworkCore.*` (Domain declares interfaces; Infrastructure references EF Core)
|
|
91
|
+
- `HttpContext`, `HttpRequest`, `ISession`
|
|
92
|
+
- `Newtonsoft.Json` / `System.Text.Json` attributes on Domain entities
|
|
93
|
+
|
|
94
|
+
CI check example: `dotnet list package` on Domain.csproj — fail if any
|
|
95
|
+
disallowed dependency appears.
|
|
96
|
+
|
|
97
|
+
### Test framework
|
|
98
|
+
|
|
99
|
+
`xUnit` + `FluentAssertions` + `NSubstitute`. Run with `dotnet test`.
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## .NET / ASP.NET WebForms (Brownfield)
|
|
104
|
+
|
|
105
|
+
### `_overview.md` stack table
|
|
106
|
+
|
|
107
|
+
```markdown
|
|
108
|
+
| Item | Current |
|
|
109
|
+
|------|---------|
|
|
110
|
+
| Language | C# 7 (.NET Framework 4.8) |
|
|
111
|
+
| Framework | ASP.NET WebForms 4.8 |
|
|
112
|
+
| ORM / persistence | Entity Framework 6 (or ADO.NET / Dapper) |
|
|
113
|
+
| Test framework | xUnit / NUnit |
|
|
114
|
+
| Database | SQL Server 2019 |
|
|
115
|
+
| UI | WebForms .aspx + Code-Behind + Bootstrap |
|
|
116
|
+
| Auth | Forms authentication / Windows auth |
|
|
117
|
+
| Hosting | IIS on-prem |
|
|
118
|
+
| Target architecture | ASP.NET Core 8 + Clean Architecture |
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Project layout
|
|
122
|
+
|
|
123
|
+
```
|
|
124
|
+
src/
|
|
125
|
+
├── Domain/ # Extracted domain logic (framework-pure)
|
|
126
|
+
│ ├── {BoundedContext}/
|
|
127
|
+
│ │ ├── Entities/
|
|
128
|
+
│ │ ├── ValueObjects/
|
|
129
|
+
│ │ └── Services/
|
|
130
|
+
│ └── SharedKernel/
|
|
131
|
+
└── WebForms/ # Existing .aspx + .aspx.cs (Code-Behind)
|
|
132
|
+
└── Pages/
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Domain purity check
|
|
136
|
+
|
|
137
|
+
The Domain assembly must not reference:
|
|
138
|
+
|
|
139
|
+
- `System.Web.*`, `System.Web.UI.*`
|
|
140
|
+
- `HttpContext.Current`, `Session[…]`, `ViewState[…]`
|
|
141
|
+
- Page lifecycle types (`Page`, `Control`)
|
|
142
|
+
- EF6 attributes on Domain entities (use Fluent API in a separate
|
|
143
|
+
configuration class outside Domain)
|
|
144
|
+
|
|
145
|
+
CI check: project reference analyzer on `src/Domain/Domain.csproj`.
|
|
146
|
+
|
|
147
|
+
### Test framework
|
|
148
|
+
|
|
149
|
+
`xUnit` or `NUnit`. Run with `dotnet test` or `nunit3-console`.
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Java / Spring Boot (Greenfield)
|
|
154
|
+
|
|
155
|
+
> Same pattern applies to Kotlin/Spring with minor syntax differences.
|
|
156
|
+
|
|
157
|
+
### `_overview.md` stack table
|
|
158
|
+
|
|
159
|
+
```markdown
|
|
160
|
+
| Item | Choice |
|
|
161
|
+
|------|--------|
|
|
162
|
+
| Language | Java 21 |
|
|
163
|
+
| Framework | Spring Boot 3.3 |
|
|
164
|
+
| ORM / persistence | Spring Data JPA / Hibernate 6.5 |
|
|
165
|
+
| Mediator | (none — direct service injection; optional: Spring Modulith) |
|
|
166
|
+
| Test framework | JUnit 5 + AssertJ + Mockito |
|
|
167
|
+
| Database | PostgreSQL 16 |
|
|
168
|
+
| Auth | Spring Security + OAuth2 / OIDC |
|
|
169
|
+
| Hosting | Kubernetes / managed app platform |
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Project layout
|
|
173
|
+
|
|
174
|
+
```
|
|
175
|
+
src/
|
|
176
|
+
├── main/
|
|
177
|
+
│ └── java/com/example/{system}/
|
|
178
|
+
│ ├── domain/ # Aggregates, Value Objects, Domain Events
|
|
179
|
+
│ │ ├── common/ # Base classes (Entity, AggregateRoot, ValueObject)
|
|
180
|
+
│ │ ├── {boundedcontext}/
|
|
181
|
+
│ │ │ ├── entity/
|
|
182
|
+
│ │ │ ├── valueobject/
|
|
183
|
+
│ │ │ ├── event/
|
|
184
|
+
│ │ │ ├── service/
|
|
185
|
+
│ │ │ └── repository/ # Repository interfaces (NOT implementations)
|
|
186
|
+
│ │ └── sharedkernel/
|
|
187
|
+
│ ├── application/ # Command/Query handlers, DTOs
|
|
188
|
+
│ │ ├── command/
|
|
189
|
+
│ │ ├── query/
|
|
190
|
+
│ │ └── dto/
|
|
191
|
+
│ ├── infrastructure/ # JPA repositories, external clients
|
|
192
|
+
│ │ ├── persistence/
|
|
193
|
+
│ │ └── client/
|
|
194
|
+
│ └── web/ # Controllers, REST endpoints
|
|
195
|
+
└── test/
|
|
196
|
+
└── java/com/example/{system}/
|
|
197
|
+
├── domain/ # Unit tests
|
|
198
|
+
├── application/
|
|
199
|
+
└── web/ # @SpringBootTest integration
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Multi-module Maven/Gradle layouts (one module per layer) are also common
|
|
203
|
+
and recommended for stricter dependency enforcement.
|
|
204
|
+
|
|
205
|
+
### Domain purity check
|
|
206
|
+
|
|
207
|
+
The Domain package must not import:
|
|
208
|
+
|
|
209
|
+
- `org.springframework.*` (no Spring annotations on Domain entities)
|
|
210
|
+
- `jakarta.persistence.*` / `javax.persistence.*` (no JPA annotations on
|
|
211
|
+
Domain entities; use a separate JPA mapping class in Infrastructure)
|
|
212
|
+
- `jakarta.servlet.*` (no HTTP types)
|
|
213
|
+
- `com.fasterxml.jackson.*` (no serialization annotations on Domain)
|
|
214
|
+
|
|
215
|
+
CI check: ArchUnit rule `noClasses().that().resideInAPackage("..domain..")
|
|
216
|
+
.should().dependOnClassesThat().resideInAnyPackage("org.springframework..", "jakarta.persistence..")`.
|
|
217
|
+
|
|
218
|
+
### Test framework
|
|
219
|
+
|
|
220
|
+
`JUnit 5` (`org.junit.jupiter`). Run with `./mvnw test` or `./gradlew test`.
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## Node.js / TypeScript / NestJS (Greenfield)
|
|
225
|
+
|
|
226
|
+
### `_overview.md` stack table
|
|
227
|
+
|
|
228
|
+
```markdown
|
|
229
|
+
| Item | Choice |
|
|
230
|
+
|------|--------|
|
|
231
|
+
| Language | TypeScript 5.4 |
|
|
232
|
+
| Framework | NestJS 10 |
|
|
233
|
+
| ORM / persistence | Prisma 5 (or TypeORM / MikroORM) |
|
|
234
|
+
| Mediator | (none — NestJS providers; optional: nestjs-cqrs module) |
|
|
235
|
+
| Test framework | Vitest (or Jest) + supertest |
|
|
236
|
+
| Database | PostgreSQL 16 |
|
|
237
|
+
| Auth | Passport (JWT, OAuth2) |
|
|
238
|
+
| Hosting | Containerized (Docker / Kubernetes / Fly.io) |
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Project layout
|
|
242
|
+
|
|
243
|
+
```
|
|
244
|
+
src/
|
|
245
|
+
├── domain/ # Aggregates, Value Objects, Domain Events
|
|
246
|
+
│ ├── common/ # Base classes
|
|
247
|
+
│ ├── {bounded-context}/
|
|
248
|
+
│ │ ├── entities/
|
|
249
|
+
│ │ ├── value-objects/
|
|
250
|
+
│ │ ├── events/
|
|
251
|
+
│ │ ├── services/
|
|
252
|
+
│ │ └── repositories/ # Interfaces only
|
|
253
|
+
│ └── shared-kernel/
|
|
254
|
+
├── application/ # Use cases, command/query handlers
|
|
255
|
+
│ ├── commands/
|
|
256
|
+
│ ├── queries/
|
|
257
|
+
│ └── dtos/
|
|
258
|
+
├── infrastructure/ # Prisma client, external API clients
|
|
259
|
+
│ ├── persistence/
|
|
260
|
+
│ └── external/
|
|
261
|
+
└── presentation/ # NestJS controllers, modules
|
|
262
|
+
└── http/
|
|
263
|
+
|
|
264
|
+
test/
|
|
265
|
+
├── domain/
|
|
266
|
+
├── application/
|
|
267
|
+
└── e2e/
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Domain purity check
|
|
271
|
+
|
|
272
|
+
`src/domain/` files must not import:
|
|
273
|
+
|
|
274
|
+
- `@nestjs/*` (no `@Injectable`, `@Module`, decorators on Domain)
|
|
275
|
+
- `@prisma/client` (Prisma generated client lives in Infrastructure only)
|
|
276
|
+
- `express` / `fastify` types
|
|
277
|
+
- `class-transformer` / `class-validator` (use plain TS types; validation
|
|
278
|
+
in Application layer)
|
|
279
|
+
|
|
280
|
+
CI check: ESLint rule `no-restricted-imports` scoped to `src/domain/**` or
|
|
281
|
+
`dependency-cruiser` rule forbidding cross-layer imports.
|
|
282
|
+
|
|
283
|
+
### Test framework
|
|
284
|
+
|
|
285
|
+
`Vitest` (or `Jest`). Run with `npm test` or `pnpm test`.
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## Python / FastAPI (Greenfield)
|
|
290
|
+
|
|
291
|
+
> Django and Flask follow the same layered pattern with framework-specific
|
|
292
|
+
> presentation conventions. Django: views/templates as presentation;
|
|
293
|
+
> Flask: blueprints; FastAPI: routers.
|
|
294
|
+
|
|
295
|
+
### `_overview.md` stack table
|
|
296
|
+
|
|
297
|
+
```markdown
|
|
298
|
+
| Item | Choice |
|
|
299
|
+
|------|--------|
|
|
300
|
+
| Language | Python 3.12 |
|
|
301
|
+
| Framework | FastAPI 0.115 |
|
|
302
|
+
| ORM / persistence | SQLAlchemy 2.0 + Alembic (or SQLModel) |
|
|
303
|
+
| Mediator | (none — typed dependency injection via FastAPI Depends) |
|
|
304
|
+
| Test framework | pytest + pytest-asyncio |
|
|
305
|
+
| Database | PostgreSQL 16 |
|
|
306
|
+
| Auth | OAuth2 via fastapi.security (PasswordBearer / JWT) |
|
|
307
|
+
| Hosting | Containerized (Docker / Kubernetes / Fly.io) |
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Project layout
|
|
311
|
+
|
|
312
|
+
```
|
|
313
|
+
src/
|
|
314
|
+
├── domain/ # Aggregates, Value Objects, Domain Events
|
|
315
|
+
│ ├── common/
|
|
316
|
+
│ ├── {bounded_context}/
|
|
317
|
+
│ │ ├── entities.py
|
|
318
|
+
│ │ ├── value_objects.py
|
|
319
|
+
│ │ ├── events.py
|
|
320
|
+
│ │ ├── services.py
|
|
321
|
+
│ │ └── repositories.py # Protocol / abstract base classes
|
|
322
|
+
│ └── shared_kernel/
|
|
323
|
+
├── application/ # Use cases, DTOs
|
|
324
|
+
│ ├── commands/
|
|
325
|
+
│ ├── queries/
|
|
326
|
+
│ └── dtos/
|
|
327
|
+
├── infrastructure/ # SQLAlchemy models, external clients
|
|
328
|
+
│ ├── persistence/
|
|
329
|
+
│ └── external/
|
|
330
|
+
└── presentation/ # FastAPI routers
|
|
331
|
+
└── api/
|
|
332
|
+
|
|
333
|
+
tests/
|
|
334
|
+
├── domain/
|
|
335
|
+
├── application/
|
|
336
|
+
└── integration/
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
### Domain purity check
|
|
340
|
+
|
|
341
|
+
`src/domain/` modules must not import:
|
|
342
|
+
|
|
343
|
+
- `fastapi` / `starlette`
|
|
344
|
+
- `sqlalchemy.*` (use Protocols / dataclasses in Domain; SQLAlchemy models
|
|
345
|
+
live in Infrastructure as separate mapping classes)
|
|
346
|
+
- `pydantic.BaseModel` for Domain entities (use `dataclass` or plain
|
|
347
|
+
classes; Pydantic models belong in DTO / API layer)
|
|
348
|
+
|
|
349
|
+
CI check: `import-linter` (`importlinter.org`) contract forbidding Domain
|
|
350
|
+
imports of presentation/infrastructure packages.
|
|
351
|
+
|
|
352
|
+
### Test framework
|
|
353
|
+
|
|
354
|
+
`pytest`. Run with `pytest` or `python -m pytest`.
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
## Go / Gin (Greenfield)
|
|
359
|
+
|
|
360
|
+
### `_overview.md` stack table
|
|
361
|
+
|
|
362
|
+
```markdown
|
|
363
|
+
| Item | Choice |
|
|
364
|
+
|------|--------|
|
|
365
|
+
| Language | Go 1.22 |
|
|
366
|
+
| Framework | Gin 1.10 (or Echo / Chi) |
|
|
367
|
+
| ORM / persistence | GORM v2 (or sqlx / pgx) |
|
|
368
|
+
| Mediator | (none — interface-based dispatch) |
|
|
369
|
+
| Test framework | go test + testify |
|
|
370
|
+
| Database | PostgreSQL 16 |
|
|
371
|
+
| Auth | JWT middleware / OAuth2 |
|
|
372
|
+
| Hosting | Containerized (Docker / Kubernetes / Fly.io) |
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### Project layout
|
|
376
|
+
|
|
377
|
+
```
|
|
378
|
+
{module-root}/
|
|
379
|
+
├── cmd/
|
|
380
|
+
│ └── api/main.go # Application entrypoint
|
|
381
|
+
├── internal/
|
|
382
|
+
│ ├── domain/ # Aggregates, Value Objects, Domain Events
|
|
383
|
+
│ │ ├── common/
|
|
384
|
+
│ │ ├── {boundedcontext}/
|
|
385
|
+
│ │ │ ├── entity.go
|
|
386
|
+
│ │ │ ├── valueobject.go
|
|
387
|
+
│ │ │ ├── event.go
|
|
388
|
+
│ │ │ ├── service.go
|
|
389
|
+
│ │ │ └── repository.go # Interface only
|
|
390
|
+
│ │ └── sharedkernel/
|
|
391
|
+
│ ├── application/ # Use cases
|
|
392
|
+
│ │ ├── command/
|
|
393
|
+
│ │ └── query/
|
|
394
|
+
│ ├── infrastructure/ # GORM repo impls, external clients
|
|
395
|
+
│ │ ├── persistence/
|
|
396
|
+
│ │ └── external/
|
|
397
|
+
│ └── handler/ # HTTP handlers (Gin routes)
|
|
398
|
+
└── go.mod
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
`internal/` enforces Go's import boundary — packages outside the module
|
|
402
|
+
cannot import from `internal/...`.
|
|
403
|
+
|
|
404
|
+
### Domain purity check
|
|
405
|
+
|
|
406
|
+
`internal/domain/` packages must not import:
|
|
407
|
+
|
|
408
|
+
- `github.com/gin-gonic/gin` or any HTTP router package
|
|
409
|
+
- `gorm.io/gorm` (Domain declares repository interfaces; GORM lives in
|
|
410
|
+
Infrastructure)
|
|
411
|
+
- `net/http` types (`http.Request`, `http.ResponseWriter`)
|
|
412
|
+
- Encoding tags (e.g. `gorm:"primaryKey"`, `json:"..."`) on Domain
|
|
413
|
+
entities — keep tag-free, do encoding via separate Infrastructure
|
|
414
|
+
models
|
|
415
|
+
|
|
416
|
+
CI check: `go vet` plus a custom `go-arch-lint` or `golangci-lint` rule
|
|
417
|
+
forbidding `internal/domain` from importing the above.
|
|
418
|
+
|
|
419
|
+
### Test framework
|
|
420
|
+
|
|
421
|
+
`go test` (standard library) + `stretchr/testify` for assertions.
|
|
422
|
+
Run with `go test ./...`.
|
|
423
|
+
|
|
424
|
+
---
|
|
425
|
+
|
|
426
|
+
## PHP / Laravel (Brownfield migration)
|
|
427
|
+
|
|
428
|
+
> Older PHP monoliths (Symfony 2/3, CodeIgniter, custom) follow similar
|
|
429
|
+
> extraction patterns; the snippets below are for a modern Laravel app
|
|
430
|
+
> already on Laravel 10+ that wants to introduce a Domain layer.
|
|
431
|
+
|
|
432
|
+
### `_overview.md` stack table
|
|
433
|
+
|
|
434
|
+
```markdown
|
|
435
|
+
| Item | Current |
|
|
436
|
+
|------|---------|
|
|
437
|
+
| Language | PHP 8.3 |
|
|
438
|
+
| Framework | Laravel 11 |
|
|
439
|
+
| ORM / persistence | Eloquent ORM (Active Record pattern — see Domain note) |
|
|
440
|
+
| Test framework | PHPUnit 11 (or Pest 2) |
|
|
441
|
+
| Database | MySQL 8 / MariaDB 10 |
|
|
442
|
+
| UI | Blade templates / Inertia + Vue / API-only |
|
|
443
|
+
| Auth | Laravel Breeze / Sanctum / Passport |
|
|
444
|
+
| Hosting | Laravel Forge / shared hosting / containerized |
|
|
445
|
+
| Target architecture | Same Laravel + extracted Domain layer + repository abstractions |
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
### Project layout
|
|
449
|
+
|
|
450
|
+
```
|
|
451
|
+
app/
|
|
452
|
+
├── Domain/ # Extracted domain logic (framework-pure)
|
|
453
|
+
│ ├── Common/
|
|
454
|
+
│ ├── {BoundedContext}/
|
|
455
|
+
│ │ ├── Entities/ # Plain PHP classes (NOT Eloquent models)
|
|
456
|
+
│ │ ├── ValueObjects/
|
|
457
|
+
│ │ ├── Events/
|
|
458
|
+
│ │ ├── Services/
|
|
459
|
+
│ │ └── Repositories/ # Interfaces only
|
|
460
|
+
│ └── SharedKernel/
|
|
461
|
+
├── Application/ # Use cases, command/query handlers
|
|
462
|
+
│ ├── Commands/
|
|
463
|
+
│ └── Queries/
|
|
464
|
+
├── Infrastructure/ # Eloquent repository implementations
|
|
465
|
+
│ ├── Persistence/
|
|
466
|
+
│ │ ├── Eloquent/ # Eloquent models live HERE, not in Domain
|
|
467
|
+
│ │ └── Repositories/
|
|
468
|
+
│ └── External/
|
|
469
|
+
├── Http/ # Controllers, middleware (Laravel-managed)
|
|
470
|
+
│ ├── Controllers/
|
|
471
|
+
│ └── Middleware/
|
|
472
|
+
└── Providers/
|
|
473
|
+
|
|
474
|
+
tests/
|
|
475
|
+
├── Unit/Domain/
|
|
476
|
+
├── Feature/Application/
|
|
477
|
+
└── Feature/Http/
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
### Domain purity check
|
|
481
|
+
|
|
482
|
+
`app/Domain/` classes must not extend / use:
|
|
483
|
+
|
|
484
|
+
- `Illuminate\Database\Eloquent\Model` (Eloquent models are Infrastructure)
|
|
485
|
+
- `Illuminate\Http\Request` / `Response`
|
|
486
|
+
- `Illuminate\Support\Facades\*` (DB, Auth, Cache, etc.)
|
|
487
|
+
- Laravel attribute / trait magic on Domain entities
|
|
488
|
+
|
|
489
|
+
CI check: a Laravel-aware static analysis rule (Larastan / PHPStan with a
|
|
490
|
+
custom architecture rule) forbidding `app/Domain/` from referencing the
|
|
491
|
+
above.
|
|
492
|
+
|
|
493
|
+
### Test framework
|
|
494
|
+
|
|
495
|
+
`PHPUnit` (`phpunit/phpunit`) or `Pest` (`pestphp/pest`). Run with
|
|
496
|
+
`./vendor/bin/phpunit` or `./vendor/bin/pest`.
|
|
497
|
+
|
|
498
|
+
---
|
|
499
|
+
|
|
500
|
+
## Adding your stack
|
|
501
|
+
|
|
502
|
+
If your stack isn't listed (Rust, Ruby/Rails, Kotlin native, Elixir, etc.),
|
|
503
|
+
the pattern is always the same:
|
|
504
|
+
|
|
505
|
+
1. Identify your stack's **domain layer convention** (folder / package /
|
|
506
|
+
namespace where pure business code lives)
|
|
507
|
+
2. Identify the **delivery / entrypoint layer** (controllers, handlers,
|
|
508
|
+
routes, CLI commands, message consumers, job runners)
|
|
509
|
+
3. Identify the **persistence layer** (ORM, query builder, raw driver)
|
|
510
|
+
4. Domain purity check = "no imports / usings / extends from delivery,
|
|
511
|
+
persistence, or framework runtime packages"
|
|
512
|
+
5. Express the layer separation in a CI-enforceable rule appropriate to
|
|
513
|
+
the stack (ArchUnit, ESLint, import-linter, go-arch-lint, Larastan,
|
|
514
|
+
etc.)
|
|
515
|
+
|
|
516
|
+
Contributions of additional stacks via PR are welcome.
|