@ddd-tool/domain-designer-cli 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/bin/domain-designer-cli.cjs +19148 -19145
  2. package/package.json +4 -2
  3. package/packages/core/dist/README.md +43 -0
  4. package/packages/core/dist/actor.d.ts +2 -0
  5. package/packages/core/dist/agg.d.ts +2 -0
  6. package/packages/core/dist/command.d.ts +3 -0
  7. package/packages/core/dist/common.d.ts +158 -0
  8. package/packages/core/dist/event.d.ts +2 -0
  9. package/packages/core/dist/index.d.ts +33 -0
  10. package/packages/core/dist/index.js +1980 -0
  11. package/packages/core/dist/info.d.ts +2 -0
  12. package/packages/core/dist/note.d.ts +2 -0
  13. package/packages/core/dist/package.json +21 -0
  14. package/packages/core/dist/policy.d.ts +2 -0
  15. package/packages/core/dist/read-model.d.ts +2 -0
  16. package/packages/core/dist/service.d.ts +2 -0
  17. package/packages/core/dist/system.d.ts +2 -0
  18. package/packages/core/dist/types.d.ts +558 -0
  19. package/packages/playground/node_modules/.vite/deps/_metadata.json +7 -7
  20. package/packages/playground/package.json +19 -21
  21. package/packages/playground/src/views/Index.vue +16 -16
  22. package/packages/playground/src/views/complex-example-detail/book.ts +383 -383
  23. package/packages/playground/src/views/complex-example-detail/common.ts +4 -4
  24. package/packages/playground/src/views/complex-example-detail/user.ts +66 -66
  25. package/packages/playground/src/views/design-en.ts +110 -110
  26. package/packages/playground/src/views/design-zh.ts +97 -97
  27. package/packages/playground/src/views/index.ts +19 -19
  28. package/packages/playground/src/views/simple-example.ts +103 -103
  29. package/packages/playground/vite-env.d.ts +12 -12
  30. package/packages/playground/vite.config.ts +27 -21
  31. package/packages/ui-component/dist/LICENSE +201 -0
  32. package/packages/ui-component/dist/README.md +5 -0
  33. package/packages/ui-component/dist/UI.vue.d.ts +29 -0
  34. package/packages/ui-component/dist/common.d.ts +9 -0
  35. package/packages/ui-component/dist/components/drag-zoom/Index.vue.d.ts +37 -0
  36. package/packages/ui-component/dist/components/node-info/Index.vue.d.ts +29 -0
  37. package/packages/ui-component/dist/components/nomnoml/Index.vue.d.ts +24 -0
  38. package/packages/ui-component/dist/components/nomnoml/base-style.d.ts +2 -0
  39. package/packages/ui-component/dist/components/nomnoml/preprocess.d.ts +5 -0
  40. package/packages/ui-component/dist/components/story-bar/Index.vue.d.ts +28 -0
  41. package/packages/ui-component/dist/components/tool-bar/Index.vue.d.ts +29 -0
  42. package/packages/ui-component/dist/domain/common.d.ts +27 -0
  43. package/packages/ui-component/dist/domain/diagram-agg/gen-code.d.ts +64 -0
  44. package/packages/ui-component/dist/domain/diagram-agg/index.d.ts +8554 -0
  45. package/packages/ui-component/dist/domain/diagram-agg/plugins.d.ts +80 -0
  46. package/packages/ui-component/dist/domain/diagram-agg/types.d.ts +20 -0
  47. package/packages/ui-component/dist/domain/i18n-agg/index.d.ts +25 -0
  48. package/packages/ui-component/dist/domain/i18n-agg/locale/en-US.d.ts +3 -0
  49. package/packages/ui-component/dist/domain/i18n-agg/locale/zh-CN.d.ts +3 -0
  50. package/packages/ui-component/dist/domain/i18n-agg/message.d.ts +42 -0
  51. package/packages/ui-component/dist/domain/mount-plugin.d.ts +1 -0
  52. package/packages/ui-component/dist/favicon.ico +0 -0
  53. package/packages/ui-component/dist/index.css +144 -0
  54. package/packages/ui-component/dist/index.d.ts +6 -0
  55. package/packages/ui-component/dist/index.js +19776 -0
  56. package/packages/ui-component/dist/package.json +28 -0
  57. package/packages/ui-component/dist/ui.d.ts +8 -0
  58. package/scripts/build-ts.mjs +52 -52
  59. package/scripts/sync-version.mjs +22 -22
  60. package/templates/CLAUDE.md +273 -12
  61. package/templates/complex-example-detail/book.ts +386 -383
  62. package/templates/complex-example-detail/common.ts +4 -4
  63. package/templates/complex-example-detail/user.ts +66 -66
  64. package/templates/node_modules/@ddd-tool/domain-designer-core/actor.d.ts +2 -2
  65. package/templates/node_modules/@ddd-tool/domain-designer-core/agg.d.ts +2 -2
  66. package/templates/node_modules/@ddd-tool/domain-designer-core/command.d.ts +3 -3
  67. package/templates/node_modules/@ddd-tool/domain-designer-core/common.d.ts +79 -79
  68. package/templates/node_modules/@ddd-tool/domain-designer-core/event.d.ts +2 -2
  69. package/templates/node_modules/@ddd-tool/domain-designer-core/index.d.ts +4 -4
  70. package/templates/node_modules/@ddd-tool/domain-designer-core/info.d.ts +2 -2
  71. package/templates/node_modules/@ddd-tool/domain-designer-core/note.d.ts +2 -2
  72. package/templates/node_modules/@ddd-tool/domain-designer-core/policy.d.ts +2 -2
  73. package/templates/node_modules/@ddd-tool/domain-designer-core/read-model.d.ts +2 -2
  74. package/templates/node_modules/@ddd-tool/domain-designer-core/service.d.ts +2 -2
  75. package/templates/node_modules/@ddd-tool/domain-designer-core/system.d.ts +2 -2
  76. package/templates/node_modules/@ddd-tool/domain-designer-core/types.d.ts +326 -326
  77. package/templates/node_modules/version.txt +1 -1
  78. package/templates/simple-example.ts +103 -103
  79. package/templates//345/244/215/346/235/202/346/250/241/345/235/227/346/267/273/345/212/240-detail/345/220/216/347/274/200 +0 -2
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "@ddd-tool/domain-designer-ui-component",
3
+ "version": "0.3.2",
4
+ "private": false,
5
+ "type": "module",
6
+ "main": "index.umd.cjs",
7
+ "dependencies": {
8
+ "@ddd-tool/domain-designer-core": "file:../../core/dist",
9
+ "@primeuix/themes": "^2.0.3",
10
+ "graphre": "^0.1.3",
11
+ "nomnoml": "^1.7.0",
12
+ "primeicons": "^7.0.0",
13
+ "primevue": "^4.5.4",
14
+ "vue": "^3.5.27"
15
+ },
16
+ "devDependencies": {
17
+ "@types/jsdom": "^27.0.0",
18
+ "cypress": "^15.9.0",
19
+ "jsdom": "^27.4.0",
20
+ "read-pkg": "^9.0.1",
21
+ "start-server-and-test": "^2.1.3",
22
+ "vite-plugin-top-level-await": "^1.6.0"
23
+ },
24
+ "peerDependencies": {
25
+ "vue": ">=3.5.27"
26
+ },
27
+ "module": "index.js"
28
+ }
@@ -0,0 +1,8 @@
1
+ export type NodeDetail = {
2
+ rule: string
3
+ name: string
4
+ type: string
5
+ relatedTypes?: string
6
+ note?: string
7
+ }
8
+ export declare function parseNode(node?: object): NodeDetail
@@ -1,52 +1,52 @@
1
- import * as esbuild from 'esbuild'
2
- import fs from 'fs'
3
- import path from 'path'
4
-
5
- // 获取命令行参数
6
- const args = process.argv.slice(2) // 从第3个元素开始截取(跳过node和脚本路径)
7
- // 查找并解析 --source 参数
8
- let sourcePath = null
9
- for (const arg of args) {
10
- if (arg.startsWith('--source=')) {
11
- sourcePath = arg.split('=')[1] // 提取等号后面的值
12
- break
13
- }
14
- }
15
- if (!sourcePath) {
16
- throw new Error('missing --source=<path>')
17
- }
18
-
19
- if (
20
- !fs.existsSync(path.join(sourcePath, 'node_modules')) ||
21
- !fs.statSync(path.join(sourcePath, 'node_modules')).isDirectory() ||
22
- fs.existsSync(path.join(sourcePath, 'package.json'))
23
- ) {
24
- throw new Error('not a workspace: ' + sourcePath)
25
- }
26
- const esmPath = path.resolve(sourcePath, '.output', 'esm')
27
- if (fs.existsSync(esmPath)) {
28
- fs.rmSync(esmPath, { recursive: true, force: true })
29
- }
30
- fs.mkdirSync(esmPath, { recursive: true })
31
-
32
- const files = fs.readdirSync(sourcePath)
33
- for (const file of files) {
34
- if (file.endsWith('.ts')) {
35
- console.log('compile', `${sourcePath.replaceAll('\\', '/')}/${file}`)
36
- esbuild.build({
37
- bundle: true,
38
- entryPoints: [`${sourcePath.replaceAll('\\', '/')}/${file}`],
39
- drop: ['debugger'],
40
- minify: true,
41
- outfile: `${esmPath.replaceAll('\\', '/')}/${file.replace(/\.ts$/, '.mjs')}`,
42
- // sourcemap: true,
43
- format: 'esm',
44
- // format: 'cjs',
45
- platform: 'node',
46
- plugins: [],
47
- target: 'node18',
48
- // tsconfig: path.join(__dirname, '..', 'tsconfig.build-cli.json'),
49
- tsconfig: path.join(__dirname, '..', 'tsconfig.json'),
50
- })
51
- }
52
- }
1
+ import * as esbuild from 'esbuild'
2
+ import fs from 'fs'
3
+ import path from 'path'
4
+
5
+ // 获取命令行参数
6
+ const args = process.argv.slice(2) // 从第3个元素开始截取(跳过node和脚本路径)
7
+ // 查找并解析 --source 参数
8
+ let sourcePath = null
9
+ for (const arg of args) {
10
+ if (arg.startsWith('--source=')) {
11
+ sourcePath = arg.split('=')[1] // 提取等号后面的值
12
+ break
13
+ }
14
+ }
15
+ if (!sourcePath) {
16
+ throw new Error('missing --source=<path>')
17
+ }
18
+
19
+ if (
20
+ !fs.existsSync(path.join(sourcePath, 'node_modules')) ||
21
+ !fs.statSync(path.join(sourcePath, 'node_modules')).isDirectory() ||
22
+ fs.existsSync(path.join(sourcePath, 'package.json'))
23
+ ) {
24
+ throw new Error('not a workspace: ' + sourcePath)
25
+ }
26
+ const esmPath = path.resolve(sourcePath, '.output', 'esm')
27
+ if (fs.existsSync(esmPath)) {
28
+ fs.rmSync(esmPath, { recursive: true, force: true })
29
+ }
30
+ fs.mkdirSync(esmPath, { recursive: true })
31
+
32
+ const files = fs.readdirSync(sourcePath)
33
+ for (const file of files) {
34
+ if (file.endsWith('.ts')) {
35
+ console.log('compile', `${sourcePath.replaceAll('\\', '/')}/${file}`)
36
+ esbuild.build({
37
+ bundle: true,
38
+ entryPoints: [`${sourcePath.replaceAll('\\', '/')}/${file}`],
39
+ drop: ['debugger'],
40
+ minify: true,
41
+ outfile: `${esmPath.replaceAll('\\', '/')}/${file.replace(/\.ts$/, '.mjs')}`,
42
+ // sourcemap: true,
43
+ format: 'esm',
44
+ // format: 'cjs',
45
+ platform: 'node',
46
+ plugins: [],
47
+ target: 'node18',
48
+ // tsconfig: path.join(__dirname, '..', 'tsconfig.build-cli.json'),
49
+ tsconfig: path.join(__dirname, '..', 'tsconfig.json'),
50
+ })
51
+ }
52
+ }
@@ -1,22 +1,22 @@
1
- import fs from 'node:fs'
2
- import path from 'node:path'
3
-
4
- function syncVersion() {
5
- const rootPath = path.resolve(import.meta.dirname, '..')
6
- const packageInfo = JSON.parse(fs.readFileSync(path.join(rootPath, 'package.json'), 'utf-8'))
7
- const version = packageInfo.version
8
- if (!version) {
9
- console.error('version not found')
10
- process.exit(1)
11
- }
12
- fs.readdirSync(path.resolve(rootPath, 'packages')).forEach((pkgName) => {
13
- const pkgPath = path.resolve(rootPath, 'packages', pkgName)
14
- if (fs.existsSync(path.join(pkgPath, 'package.json'))) {
15
- const pkgInfo = JSON.parse(fs.readFileSync(path.join(pkgPath, 'package.json'), 'utf-8'))
16
- pkgInfo.version = version
17
- fs.writeFileSync(path.join(pkgPath, 'package.json'), JSON.stringify(pkgInfo, null, 2) + '\n', 'utf-8')
18
- }
19
- })
20
- }
21
-
22
- syncVersion()
1
+ import fs from 'node:fs'
2
+ import path from 'node:path'
3
+
4
+ function syncVersion() {
5
+ const rootPath = path.resolve(import.meta.dirname, '..')
6
+ const packageInfo = JSON.parse(fs.readFileSync(path.join(rootPath, 'package.json'), 'utf-8'))
7
+ const version = packageInfo.version
8
+ if (!version) {
9
+ console.error('version not found')
10
+ process.exit(1)
11
+ }
12
+ fs.readdirSync(path.resolve(rootPath, 'packages')).forEach((pkgName) => {
13
+ const pkgPath = path.resolve(rootPath, 'packages', pkgName)
14
+ if (fs.existsSync(path.join(pkgPath, 'package.json'))) {
15
+ const pkgInfo = JSON.parse(fs.readFileSync(path.join(pkgPath, 'package.json'), 'utf-8'))
16
+ pkgInfo.version = version
17
+ fs.writeFileSync(path.join(pkgPath, 'package.json'), JSON.stringify(pkgInfo, null, 2) + '\n', 'utf-8')
18
+ }
19
+ })
20
+ }
21
+
22
+ syncVersion()
@@ -18,6 +18,37 @@ import { createDomainDesigner } from '@ddd-tool/domain-designer-core'
18
18
  const d = createDomainDesigner()
19
19
  ```
20
20
 
21
+ ### Initialization Options
22
+
23
+ Optional parameters for `createDomainDesigner()`:
24
+
25
+ ```typescript
26
+ const d = createDomainDesigner({
27
+ moduleName: 'order', // Module name for code generation
28
+ ignoreValueObjects: ['time'], // Value object names to ignore during code generation
29
+ })
30
+ ```
31
+
32
+ | Parameter | Type | Description | Default Value |
33
+ | -------------------- | -------- | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
34
+ | `moduleName` | string | Module name for code bundling | File name |
35
+ | `ignoreValueObjects` | string[] | Value object names to skip during code generation | ['time', 'id', 'pid', 'name', 'state', 'status', 'version', 'code', 'message', 'type', 'result', 'data', 'payload', 'meta', 'context', 'sorting'] |
36
+
37
+ **Why `ignoreValueObjects`?** Generic names like `time` or `name` have weak business meaning. During code generation, they should be treated as primitive types rather than value objects.
38
+
39
+ ### Common Pattern: `const i = d.info`
40
+
41
+ It's common to alias `d.info` as `i` for brevity:
42
+
43
+ ```typescript
44
+ const d = createDomainDesigner()
45
+ const i = d.info
46
+
47
+ // Now you can use i instead of d.info
48
+ const userId = i.id('userId')
49
+ const userName = i.valueObj('userName', '用户名')
50
+ ```
51
+
21
52
  ### Available Methods
22
53
 
23
54
  | Method | Purpose |
@@ -47,6 +78,32 @@ const workflowName = d.startWorkflow('CreateOrder')
47
78
  actor.command(createOrder).agg(orderAgg).event(orderCreated)
48
79
  ```
49
80
 
81
+ ### Node Method Reference
82
+
83
+ Each type of node has specific methods that can be called in a workflow:
84
+
85
+ | Node Type | Available Methods | Description |
86
+ | :---------------- | :----------------------------------------------------- | :--------------------------------------- |
87
+ | **Actor** | `.command()`, `.facadeCmd()`, `.readModel()` | Initiates commands or reads data |
88
+ | **Command** | `.agg()` | Targets an aggregate |
89
+ | **FacadeCommand** | `.agg()`, `.service()` | Targets aggregate or calls service |
90
+ | **Aggregate** | `.event()`, `.command()` | Emits event or creates nested command |
91
+ | **Event** | `.policy()`, `.system()`, `.readModel()`, `.command()` | Triggers policy/system/readModel/command |
92
+ | **Policy** | `.command()`, `.facadeCmd()`, `.service()` | Issues commands or calls service |
93
+ | **Service** | `.command()`, `.facadeCmd()`, `.agg()` | Issues commands or targets aggregate |
94
+ | **System** | `.command()`, `.facadeCmd()`, `.event()` | Issues commands or emits events |
95
+
96
+ **Important**: Each method call in a workflow returns a reference to the target node, allowing you to continue chaining:
97
+
98
+ ```typescript
99
+ d.startWorkflow('Example')
100
+ // Actor → Command → Aggregate → Event
101
+ customer.command(createOrder).agg(orderAgg).event(orderCreated)
102
+ // ↓
103
+ // Event → Policy
104
+ // orderCreated.policy(autoConfirmPolicy)
105
+ ```
106
+
50
107
  ### Complete Example
51
108
 
52
109
  ```typescript
@@ -114,14 +171,15 @@ For larger domains, split into modules:
114
171
  // common.ts - Shared setup
115
172
  import { createDomainDesigner } from '@ddd-tool/domain-designer-core'
116
173
  export const d = createDomainDesigner({ moduleName: 'my-domain' })
174
+ export const i = d.info
117
175
 
118
176
  // user.ts - User domain
119
- import { d } from './common'
177
+ import { d, i } from './common'
120
178
  const user = d.actor('用户')
121
179
  // ... more definitions
122
180
 
123
181
  // book.ts - Book domain
124
- import { d } from './common'
182
+ import { d, i } from './common'
125
183
  const book = d.agg('图书')
126
184
  // ... more definitions
127
185
 
@@ -132,14 +190,193 @@ import './book'
132
190
  export default d
133
191
  ```
134
192
 
193
+ ### Value Pool Pattern ("常量池")
194
+
195
+ For domains with multiple related aggregates, organize reusable values into "value pools":
196
+
197
+ ```typescript
198
+ // book.ts
199
+ import { d, i } from './common'
200
+
201
+ export const bookValues = {
202
+ ISBN: i.id('isbn', '国际标准书号 - 一书一码'),
203
+ 图书名称: i.valueObj('bookName', '图书名称'),
204
+ 图书价格: i.valueObj('bookPrice', '图书价格'),
205
+ }
206
+
207
+ const 图书聚合 = d.agg('BookAgg', [bookValues.ISBN, bookValues.图书名称], '图书聚合')
208
+
209
+ // order.ts
210
+ import { d, i } from './common'
211
+ import { bookValues } from './book'
212
+
213
+ export const orderValues = {
214
+ 订单流水号: i.id('orderSequence', '订单流水号'),
215
+ 订购数量: i.valueObj('quantity', '订购数量'),
216
+ 最终价格: i.func(
217
+ '最终价格',
218
+ [bookValues.图书价格, orderValues.订购数量],
219
+ '最终价格 = 图书价格 * 订购数量',
220
+ ),
221
+ }
222
+
223
+ // Cross-reference values using d.note()
224
+ const 全局流水号 = i.valueObj('globalSequence', d.note`全局流水号由${上游系统}接口统一生成`)
225
+ ```
226
+
227
+ **Benefits of value pools:**
228
+
229
+ - Avoids repeated definitions
230
+ - Serves as a "glossary" for domain terms
231
+ - Makes cross-references explicit using `d.note()`
232
+
135
233
  ## Value Object Types
136
234
 
137
235
  ```typescript
138
236
  d.info.id('name') // ID type
139
- d.info.document('name') // Document type
140
- d.info.func('name') // Function type
237
+ d.info.document('name') // Document type (experimental)
238
+ d.info.func('name') // Function type (experimental)
141
239
  d.info.valueObj('name') // Value Object type
142
- d.info.version('name') // Version type
240
+ d.info.version('name') // Version type (experimental)
241
+ ```
242
+
243
+ ### Naming Conventions
244
+
245
+ **Variable names**: Use Chinese/native language for better domain understanding:
246
+
247
+ ```typescript
248
+ const 图书价格 = i.valueObj('bookPrice', '图书价格')
249
+ const 用户名 = i.valueObj('userName', '用户名')
250
+ ```
251
+
252
+ **Type/Node names**: Use English for the first parameter (actual code name), Chinese for documentation:
253
+
254
+ ```typescript
255
+ // First parameter = code name (English)
256
+ // Second parameter = documentation (Chinese)
257
+ const 图书价格 = i.valueObj('bookPrice', '图书价格')
258
+ d.command('CreateUser', [用户名, 邮箱], '创建用户')
259
+ d.agg('UserAgg', [用户id], '用户聚合')
260
+ d.event('UserCreated', [用户id], '用户已创建')
261
+ ```
262
+
263
+ ### Shorthand Syntax
264
+
265
+ All nodes accept plain strings as parameters. The shorthand `['fieldName', '备注']` syntax is equivalent to `i.valueObj('fieldName', '备注')`:
266
+
267
+ ```typescript
268
+ // Both are equivalent - use d.info functions for clarity
269
+ d.command('CreateUser', [i.valueObj('userName'), i.valueObj('email'), i.valueObj('gender', '性别')])
270
+
271
+ // Shorthand form
272
+ d.command('CreateUser', ['userName', 'email', ['gender', '性别']])
273
+ ```
274
+
275
+ **Recommendation**: Prefer `d.info` functions for explicit type marking, especially for value objects that have business meaning. Use shorthand only for generic fields like `time` or `id`.
276
+
277
+ ### Experimental Features
278
+
279
+ The following value types are experimental and their necessity is still being evaluated:
280
+
281
+ - **`document`** - For document or binary file values
282
+ - **`func`** - For values computed by functions
283
+ - **`version`** - For version numbers with business meaning
284
+
285
+ ## Adding Documentation (Notes)
286
+
287
+ Most node functions accept a **last parameter** for documentation:
288
+
289
+ ```typescript
290
+ // Simple documentation using a string
291
+ const orderCreated = d.event('OrderCreated', [orderId], '订单已创建')
292
+ const orderAgg = d.agg('OrderAgg', [orderId], '订单聚合')
293
+ const createOrder = d.command('CreateOrder', [orderId], '创建订单')
294
+ ```
295
+
296
+ ### Advanced Documentation with `d.note()`
297
+
298
+ `d.note()` is an advanced form of documentation that enables:
299
+
300
+ 1. **Referencing other nodes** using `${node}` syntax (creates traceability)
301
+ 2. **Multi-line business rules** with numbered lists
302
+ 3. **Cross-module references** between domain concepts
303
+
304
+ #### When to Use `d.note()`
305
+
306
+ **Use simple strings** for basic descriptions:
307
+
308
+ ```typescript
309
+ const addBook = d.command('AddToAvailableBooksCmd', [isbn, qrCode], '添加可预订书')
310
+ ```
311
+
312
+ **Use `d.note()` when you need to:**
313
+
314
+ - Reference other nodes/values in the domain
315
+ - Document complex business rules
316
+ - Explain domain-specific concepts
317
+
318
+ #### Referencing Other Nodes
319
+
320
+ Use the `${node}` syntax to create explicit links between concepts:
321
+
322
+ ```typescript
323
+ // In user.ts
324
+ export const userValues = {
325
+ 用户id: i.id('userId'),
326
+ 逾期次数: i.valueObj('overdueCount', '逾期次数'),
327
+ }
328
+
329
+ // In book.ts - reference using ${userValues.逾期次数}
330
+ const timeoutCommand = d.command(
331
+ 'TimeOutBorrowing',
332
+ ['借书id'],
333
+ d.note`逾期
334
+ 1. 书被借出,且1个月未还
335
+ 2. 增加借书会员的${userValues.逾期次数}`,
336
+ )
337
+
338
+ // Creates traceability - shows which value is being modified
339
+ const suspendPolicy = d.policy('SuspendAccountWhenTimeOut', d.note`增加账户${userValues.逾期次数}`)
340
+ ```
341
+
342
+ #### Business Rules with Lists
343
+
344
+ ```typescript
345
+ const reserveBook = d.command(
346
+ 'ReserveBook',
347
+ [isbn, 'userId'],
348
+ d.note`预定书
349
+ 1. 有可预定的书
350
+ 2. 会员账户没有被暂停
351
+ 3. 会员已预定或者借出的书小于3本`,
352
+ )
353
+
354
+ const borrowService = d.service(
355
+ 'BorrowBookService',
356
+ d.note`借书服务
357
+ 书可预定则可借出
358
+ 书已预定时借书人是预定则可借`,
359
+ )
360
+ ```
361
+
362
+ #### Complex Value Objects
363
+
364
+ For domain-specific or complex types:
365
+
366
+ ```typescript
367
+ const qrCodeSet = i.valueObj('二维码集合', d.note`${bookValues.二维码} // 一书一码,业务唯一标识`)
368
+
369
+ const isbn = i.valueObj('isbn', d.note`国际标准书号 (ISBN-13)`)
370
+ ```
371
+
372
+ **Key Point**: `d.note()` creates traceability. When a business rule mentions another concept, reference it explicitly:
373
+
374
+ ```typescript
375
+ // Good - Creates explicit link
376
+ d.note`增加${userValues.逾期次数}`
377
+
378
+ // Avoid - No traceability
379
+ d.note`增加逾期计数`
143
380
  ```
144
381
 
145
382
  ## Modeling Guidelines
@@ -191,20 +428,44 @@ const orderCreated = d.command('订单已创建') // Avoid - sounds like an even
191
428
  Policies automate business rules:
192
429
 
193
430
  ```typescript
194
- const paymentPolicy = d.policy('支付超时取消规则')
431
+ const paymentTimeoutPolicy = d.policy('支付超时取消规则')
195
432
  const cancelOrder = d.command('取消订单')
433
+ const orderCancelled = d.event('订单已取消')
434
+ const orderAgg = d.agg('订单聚合', [d.info.id('订单号')])
196
435
 
197
- // When payment timeout event occurs, trigger cancellation
198
- paymentTimeout.event(cancelOrder)
436
+ // When payment timeout event occurs, policy triggers cancellation
437
+ d.startWorkflow('支付超时取消流程')
438
+ paymentTimeout.policy(paymentTimeoutPolicy).command(cancelOrder).agg(orderAgg).event(orderCancelled)
199
439
  ```
200
440
 
201
- ### External Systems
441
+ ### Event to External Systems
202
442
 
203
- Integrate with external systems:
443
+ Events can trigger actions in external systems:
204
444
 
205
445
  ```typescript
206
446
  const emailSystem = d.system('邮件系统')
207
- orderCreated.event(emailSystem)
447
+ const smsSystem = d.system('短信系统')
448
+
449
+ // In a workflow, connect events to systems
450
+ d.startWorkflow('订单创建通知流程')
451
+ orderCreated.system(emailSystem)
452
+ orderCreated.system(smsSystem)
453
+ ```
454
+
455
+ ### Event to Read Models
456
+
457
+ Events update read models:
458
+
459
+ ```typescript
460
+ const orderSummary = d.readModel('订单汇总读模型', [
461
+ d.info.id('订单号'),
462
+ d.info.document('商品信息'),
463
+ d.info.version('订单状态'),
464
+ ])
465
+
466
+ // In a workflow, connect events to read models
467
+ d.startWorkflow('订单查询流程')
468
+ orderCreated.readModel(orderSummary)
208
469
  ```
209
470
 
210
471
  ### Read Models
@@ -265,7 +526,7 @@ When helping with domain modeling:
265
526
 
266
527
  If you see errors like:
267
528
 
268
- ```
529
+ ```text
269
530
  The requested module does not provide an export named 'X'
270
531
  ```
271
532