@aicgen/aicgen 1.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agents/architecture-reviewer.md +88 -0
- package/.claude/agents/guideline-checker.md +73 -0
- package/.claude/agents/security-auditor.md +108 -0
- package/.claude/guidelines/api-design.md +645 -0
- package/.claude/guidelines/architecture.md +2503 -0
- package/.claude/guidelines/best-practices.md +618 -0
- package/.claude/guidelines/code-style.md +304 -0
- package/.claude/guidelines/design-patterns.md +573 -0
- package/.claude/guidelines/devops.md +226 -0
- package/.claude/guidelines/error-handling.md +413 -0
- package/.claude/guidelines/language.md +782 -0
- package/.claude/guidelines/performance.md +706 -0
- package/.claude/guidelines/security.md +583 -0
- package/.claude/guidelines/testing.md +568 -0
- package/.claude/settings.json +98 -0
- package/.claude/settings.local.json +8 -0
- package/.env.example +23 -0
- package/.eslintrc.json +28 -0
- package/.github/workflows/release.yml +180 -0
- package/.github/workflows/test.yml +81 -0
- package/.gitmodules +3 -0
- package/.vs/ProjectSettings.json +3 -0
- package/.vs/VSWorkspaceState.json +16 -0
- package/.vs/aicgen.slnx/FileContentIndex/5f0ce2a3-fd68-4863-9e23-e428cf1794e3.vsidx +0 -0
- package/.vs/aicgen.slnx/v18/.wsuo +0 -0
- package/.vs/aicgen.slnx/v18/DocumentLayout.json +54 -0
- package/.vs/slnx.sqlite +0 -0
- package/AGENTS.md +121 -0
- package/CLAUDE.md +36 -0
- package/CONTRIBUTING.md +821 -0
- package/LICENSE +21 -0
- package/README.md +199 -0
- package/assets/icon.svg +34 -0
- package/assets/logo.svg +41 -0
- package/bun.lock +848 -0
- package/data/LICENSE +21 -0
- package/data/README.md +203 -0
- package/data/api/basics.md +292 -0
- package/data/api/index.md +8 -0
- package/data/api/pagination.md +142 -0
- package/data/api/rest.md +137 -0
- package/data/api/versioning.md +60 -0
- package/data/architecture/clean-architecture/index.md +7 -0
- package/data/architecture/clean-architecture/layers.md +111 -0
- package/data/architecture/ddd/index.md +8 -0
- package/data/architecture/ddd/strategic.md +89 -0
- package/data/architecture/ddd/tactical.md +132 -0
- package/data/architecture/event-driven/index.md +7 -0
- package/data/architecture/event-driven/messaging.md +242 -0
- package/data/architecture/event-driven/patterns.md +129 -0
- package/data/architecture/feature-toggles/index.md +7 -0
- package/data/architecture/feature-toggles/patterns.md +73 -0
- package/data/architecture/gui/index.md +7 -0
- package/data/architecture/gui/patterns.md +132 -0
- package/data/architecture/hexagonal/ports-adapters.md +132 -0
- package/data/architecture/index.md +12 -0
- package/data/architecture/layered/index.md +7 -0
- package/data/architecture/layered/layers.md +100 -0
- package/data/architecture/microservices/api-gateway.md +56 -0
- package/data/architecture/microservices/boundaries.md +80 -0
- package/data/architecture/microservices/communication.md +97 -0
- package/data/architecture/microservices/data.md +92 -0
- package/data/architecture/microservices/index.md +11 -0
- package/data/architecture/microservices/resilience.md +111 -0
- package/data/architecture/modular-monolith/boundaries.md +133 -0
- package/data/architecture/modular-monolith/structure.md +131 -0
- package/data/architecture/serverless/best-practices.md +322 -0
- package/data/architecture/serverless/index.md +7 -0
- package/data/architecture/serverless/patterns.md +80 -0
- package/data/architecture/solid/index.md +7 -0
- package/data/architecture/solid/principles.md +187 -0
- package/data/database/basics.md +365 -0
- package/data/database/design-patterns.md +68 -0
- package/data/database/index.md +8 -0
- package/data/database/indexing.md +136 -0
- package/data/database/nosql.md +223 -0
- package/data/database/schema.md +137 -0
- package/data/devops/ci-cd.md +66 -0
- package/data/devops/index.md +8 -0
- package/data/devops/observability.md +73 -0
- package/data/devops/practices.md +77 -0
- package/data/error-handling/basics.md +222 -0
- package/data/error-handling/index.md +7 -0
- package/data/error-handling/strategy.md +185 -0
- package/data/guideline-mappings.yml +1077 -0
- package/data/index.md +3 -0
- package/data/language/csharp/basics.md +210 -0
- package/data/language/csharp/testing.md +252 -0
- package/data/language/go/basics.md +158 -0
- package/data/language/go/testing.md +192 -0
- package/data/language/index.md +14 -0
- package/data/language/java/basics.md +184 -0
- package/data/language/java/testing.md +273 -0
- package/data/language/javascript/basics.md +217 -0
- package/data/language/javascript/testing.md +269 -0
- package/data/language/python/async.md +100 -0
- package/data/language/python/basics.md +100 -0
- package/data/language/python/index.md +10 -0
- package/data/language/python/testing.md +125 -0
- package/data/language/python/types.md +99 -0
- package/data/language/ruby/basics.md +227 -0
- package/data/language/ruby/testing.md +267 -0
- package/data/language/rust/basics.md +175 -0
- package/data/language/rust/testing.md +219 -0
- package/data/language/typescript/async.md +103 -0
- package/data/language/typescript/basics.md +87 -0
- package/data/language/typescript/config.md +95 -0
- package/data/language/typescript/error-handling.md +98 -0
- package/data/language/typescript/generics.md +85 -0
- package/data/language/typescript/index.md +14 -0
- package/data/language/typescript/interfaces-types.md +83 -0
- package/data/language/typescript/performance.md +103 -0
- package/data/language/typescript/testing.md +98 -0
- package/data/patterns/base-patterns.md +105 -0
- package/data/patterns/concurrency.md +87 -0
- package/data/patterns/data-access.md +83 -0
- package/data/patterns/distribution.md +86 -0
- package/data/patterns/domain-logic.md +81 -0
- package/data/patterns/gof.md +109 -0
- package/data/patterns/index.md +12 -0
- package/data/performance/async.md +148 -0
- package/data/performance/basics.md +324 -0
- package/data/performance/caching-strategies.md +68 -0
- package/data/performance/caching.md +152 -0
- package/data/performance/index.md +8 -0
- package/data/practices/code-review.md +52 -0
- package/data/practices/documentation.md +260 -0
- package/data/practices/index.md +11 -0
- package/data/practices/planning.md +142 -0
- package/data/practices/refactoring.md +91 -0
- package/data/practices/version-control.md +55 -0
- package/data/security/auth-jwt.md +159 -0
- package/data/security/headers.md +143 -0
- package/data/security/index.md +10 -0
- package/data/security/injection.md +119 -0
- package/data/security/secrets.md +148 -0
- package/data/style/index.md +8 -0
- package/data/style/naming.md +136 -0
- package/data/style/organization.md +162 -0
- package/data/templates/agents/architecture-reviewer.md +88 -0
- package/data/templates/agents/guideline-checker.md +73 -0
- package/data/templates/agents/security-auditor.md +108 -0
- package/data/templates/antigravity/rules/architecture.md.hbs +5 -0
- package/data/templates/antigravity/rules/code-style.md.hbs +5 -0
- package/data/templates/antigravity/rules/language.md.hbs +5 -0
- package/data/templates/antigravity/rules/performance.md.hbs +5 -0
- package/data/templates/antigravity/rules/security.md.hbs +5 -0
- package/data/templates/antigravity/rules/testing.md.hbs +5 -0
- package/data/templates/antigravity/workflows/add-documentation.md.hbs +23 -0
- package/data/templates/antigravity/workflows/generate-integration-tests.md.hbs +17 -0
- package/data/templates/antigravity/workflows/generate-unit-tests.md.hbs +20 -0
- package/data/templates/antigravity/workflows/performance-audit.md.hbs +24 -0
- package/data/templates/antigravity/workflows/refactor-extract-module.md.hbs +17 -0
- package/data/templates/antigravity/workflows/security-audit.md.hbs +20 -0
- package/data/templates/hooks/formatting.json +26 -0
- package/data/templates/hooks/security.json +35 -0
- package/data/templates/hooks/testing.json +17 -0
- package/data/testing/basics.md +151 -0
- package/data/testing/index.md +9 -0
- package/data/testing/integration.md +159 -0
- package/data/testing/unit-fundamentals.md +128 -0
- package/data/testing/unit-mocking.md +116 -0
- package/data/version.json +49 -0
- package/dist/commands/init.d.ts +8 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +46 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/config/profiles.d.ts +4 -0
- package/dist/config/profiles.d.ts.map +1 -0
- package/dist/config/profiles.js +30 -0
- package/dist/config/profiles.js.map +1 -0
- package/dist/config/settings.d.ts +7 -0
- package/dist/config/settings.d.ts.map +1 -0
- package/dist/config/settings.js +7 -0
- package/dist/config/settings.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +58489 -0
- package/dist/index.js.map +1 -0
- package/dist/models/guideline.d.ts +15 -0
- package/dist/models/guideline.d.ts.map +1 -0
- package/dist/models/guideline.js +2 -0
- package/dist/models/guideline.js.map +1 -0
- package/dist/models/preference.d.ts +9 -0
- package/dist/models/preference.d.ts.map +1 -0
- package/dist/models/preference.js +2 -0
- package/dist/models/preference.js.map +1 -0
- package/dist/models/profile.d.ts +9 -0
- package/dist/models/profile.d.ts.map +1 -0
- package/dist/models/profile.js +2 -0
- package/dist/models/profile.js.map +1 -0
- package/dist/models/project.d.ts +13 -0
- package/dist/models/project.d.ts.map +1 -0
- package/dist/models/project.js +2 -0
- package/dist/models/project.js.map +1 -0
- package/dist/services/ai/anthropic.d.ts +7 -0
- package/dist/services/ai/anthropic.d.ts.map +1 -0
- package/dist/services/ai/anthropic.js +39 -0
- package/dist/services/ai/anthropic.js.map +1 -0
- package/dist/services/generator.d.ts +2 -0
- package/dist/services/generator.d.ts.map +1 -0
- package/dist/services/generator.js +4 -0
- package/dist/services/generator.js.map +1 -0
- package/dist/services/learner.d.ts +2 -0
- package/dist/services/learner.d.ts.map +1 -0
- package/dist/services/learner.js +4 -0
- package/dist/services/learner.js.map +1 -0
- package/dist/services/scanner.d.ts +3 -0
- package/dist/services/scanner.d.ts.map +1 -0
- package/dist/services/scanner.js +54 -0
- package/dist/services/scanner.js.map +1 -0
- package/dist/utils/errors.d.ts +15 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +27 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/file.d.ts +7 -0
- package/dist/utils/file.d.ts.map +1 -0
- package/dist/utils/file.js +32 -0
- package/dist/utils/file.js.map +1 -0
- package/dist/utils/logger.d.ts +6 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +17 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/path.d.ts +6 -0
- package/dist/utils/path.d.ts.map +1 -0
- package/dist/utils/path.js +14 -0
- package/dist/utils/path.js.map +1 -0
- package/docs/planning/memory-lane.md +83 -0
- package/package.json +64 -0
- package/packaging/linux/aicgen.spec +23 -0
- package/packaging/linux/control +9 -0
- package/packaging/macos/scripts/postinstall +12 -0
- package/packaging/windows/setup.nsi +92 -0
- package/planning/BRANDING-SUMMARY.md +194 -0
- package/planning/BRANDING.md +174 -0
- package/planning/BUILD.md +186 -0
- package/planning/CHUNK-IMPLEMENTATION-PLAN.md +87 -0
- package/planning/CHUNK-TAXONOMY.md +375 -0
- package/planning/CHUNKS-COMPLETE.md +382 -0
- package/planning/DESIGN.md +313 -0
- package/planning/DYNAMIC-GUIDELINES-DESIGN.md +265 -0
- package/planning/ENTERPRISE-UX-COMPLETE.md +281 -0
- package/planning/IMPLEMENTATION-PLAN.md +20 -0
- package/planning/PHASE1-COMPLETE.md +211 -0
- package/planning/PHASE2-COMPLETE.md +350 -0
- package/planning/PHASE3-COMPLETE.md +399 -0
- package/planning/PHASE4-COMPLETE.md +361 -0
- package/planning/PHASE4.5-CHUNKS.md +462 -0
- package/planning/STRUCTURE.md +170 -0
- package/scripts/add-categories.ts +87 -0
- package/scripts/build-binary.ts +46 -0
- package/scripts/embed-data.ts +105 -0
- package/scripts/generate-version.ts +150 -0
- package/scripts/test-decompress.ts +27 -0
- package/scripts/test-extract.ts +31 -0
- package/src/__tests__/services/assistant-file-writer.test.ts +400 -0
- package/src/__tests__/services/guideline-loader.test.ts +281 -0
- package/src/__tests__/services/tarball-extraction.test.ts +125 -0
- package/src/commands/add-guideline.ts +296 -0
- package/src/commands/clear.ts +61 -0
- package/src/commands/guideline-selector.ts +123 -0
- package/src/commands/init.ts +645 -0
- package/src/commands/quick-add.ts +586 -0
- package/src/commands/remove-guideline.ts +152 -0
- package/src/commands/stats.ts +49 -0
- package/src/commands/update.ts +240 -0
- package/src/config.ts +82 -0
- package/src/embedded-data.ts +1492 -0
- package/src/index.ts +67 -0
- package/src/models/profile.ts +24 -0
- package/src/models/project.ts +43 -0
- package/src/services/assistant-file-writer.ts +612 -0
- package/src/services/config-generator.ts +150 -0
- package/src/services/config-manager.ts +70 -0
- package/src/services/data-source.ts +248 -0
- package/src/services/first-run-init.ts +148 -0
- package/src/services/guideline-loader.ts +311 -0
- package/src/services/hook-generator.ts +178 -0
- package/src/services/subagent-generator.ts +310 -0
- package/src/utils/banner.ts +66 -0
- package/src/utils/errors.ts +27 -0
- package/src/utils/file.ts +67 -0
- package/src/utils/formatting.ts +172 -0
- package/src/utils/logger.ts +89 -0
- package/src/utils/path.ts +17 -0
- package/src/utils/wizard-state.ts +132 -0
- package/tsconfig.json +25 -0
package/data/api/rest.md
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# REST API Design
|
|
2
|
+
|
|
3
|
+
## Resource-Oriented URLs
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
✅ Good (nouns, plural)
|
|
7
|
+
GET /api/v1/books # List books
|
|
8
|
+
GET /api/v1/books/123 # Get book
|
|
9
|
+
POST /api/v1/books # Create book
|
|
10
|
+
PUT /api/v1/books/123 # Replace book
|
|
11
|
+
PATCH /api/v1/books/123 # Update book
|
|
12
|
+
DELETE /api/v1/books/123 # Delete book
|
|
13
|
+
|
|
14
|
+
❌ Bad (verbs, actions)
|
|
15
|
+
POST /api/v1/createBook
|
|
16
|
+
GET /api/v1/getBookById/123
|
|
17
|
+
POST /api/v1/updateBook/123
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## HTTP Methods
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
// GET - Read (safe, idempotent)
|
|
24
|
+
app.get('/api/v1/users/:id', async (req, res) => {
|
|
25
|
+
const user = await userService.findById(req.params.id);
|
|
26
|
+
res.json({ data: user });
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// POST - Create (not idempotent)
|
|
30
|
+
app.post('/api/v1/users', async (req, res) => {
|
|
31
|
+
const user = await userService.create(req.body);
|
|
32
|
+
res.status(201)
|
|
33
|
+
.location(`/api/v1/users/${user.id}`)
|
|
34
|
+
.json({ data: user });
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// PUT - Replace entire resource (idempotent)
|
|
38
|
+
app.put('/api/v1/users/:id', async (req, res) => {
|
|
39
|
+
const user = await userService.replace(req.params.id, req.body);
|
|
40
|
+
res.json({ data: user });
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// PATCH - Partial update (idempotent)
|
|
44
|
+
app.patch('/api/v1/users/:id', async (req, res) => {
|
|
45
|
+
const user = await userService.update(req.params.id, req.body);
|
|
46
|
+
res.json({ data: user });
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// DELETE - Remove (idempotent)
|
|
50
|
+
app.delete('/api/v1/users/:id', async (req, res) => {
|
|
51
|
+
await userService.delete(req.params.id);
|
|
52
|
+
res.status(204).end();
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Status Codes
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
// Success
|
|
60
|
+
200 OK // GET, PUT, PATCH succeeded
|
|
61
|
+
201 Created // POST succeeded
|
|
62
|
+
204 No Content // DELETE succeeded
|
|
63
|
+
|
|
64
|
+
// Client errors
|
|
65
|
+
400 Bad Request // Validation failed
|
|
66
|
+
401 Unauthorized // Not authenticated
|
|
67
|
+
403 Forbidden // Authenticated but not allowed
|
|
68
|
+
404 Not Found // Resource doesn't exist
|
|
69
|
+
409 Conflict // Duplicate, version conflict
|
|
70
|
+
422 Unprocessable // Business rule violation
|
|
71
|
+
|
|
72
|
+
// Server errors
|
|
73
|
+
500 Internal Server Error
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Response Format
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
// Single resource
|
|
80
|
+
{
|
|
81
|
+
"data": {
|
|
82
|
+
"id": 123,
|
|
83
|
+
"name": "John Doe",
|
|
84
|
+
"email": "john@example.com"
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Collection with pagination
|
|
89
|
+
{
|
|
90
|
+
"data": [
|
|
91
|
+
{ "id": 1, "name": "Item 1" },
|
|
92
|
+
{ "id": 2, "name": "Item 2" }
|
|
93
|
+
],
|
|
94
|
+
"pagination": {
|
|
95
|
+
"page": 1,
|
|
96
|
+
"limit": 20,
|
|
97
|
+
"total": 150,
|
|
98
|
+
"totalPages": 8
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Error response
|
|
103
|
+
{
|
|
104
|
+
"error": {
|
|
105
|
+
"code": "VALIDATION_ERROR",
|
|
106
|
+
"message": "The request contains invalid data",
|
|
107
|
+
"details": [
|
|
108
|
+
{ "field": "email", "message": "Invalid email format" }
|
|
109
|
+
]
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Hierarchical Resources
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
✅ Limit nesting to 2-3 levels
|
|
118
|
+
GET /api/v1/authors/456/books # Books by author
|
|
119
|
+
GET /api/v1/orders/789/items # Items in order
|
|
120
|
+
|
|
121
|
+
❌ Too deep
|
|
122
|
+
GET /api/v1/publishers/1/authors/2/books/3/reviews/4
|
|
123
|
+
|
|
124
|
+
✅ Use query parameters instead
|
|
125
|
+
GET /api/v1/reviews?bookId=3
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## API Versioning
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
✅ Always version from the start
|
|
132
|
+
/api/v1/books
|
|
133
|
+
/api/v2/books
|
|
134
|
+
|
|
135
|
+
❌ No version
|
|
136
|
+
/api/books
|
|
137
|
+
```
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# API Versioning
|
|
2
|
+
|
|
3
|
+
## Versioning Strategies
|
|
4
|
+
|
|
5
|
+
### URL Path Versioning
|
|
6
|
+
```
|
|
7
|
+
GET /api/v1/users
|
|
8
|
+
GET /api/v2/users
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
### Header Versioning
|
|
12
|
+
```
|
|
13
|
+
GET /api/users
|
|
14
|
+
Accept: application/vnd.api+json; version=2
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Query Parameter
|
|
18
|
+
```
|
|
19
|
+
GET /api/users?version=2
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Implementation
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
// URL path versioning
|
|
26
|
+
app.use('/api/v1', v1Router);
|
|
27
|
+
app.use('/api/v2', v2Router);
|
|
28
|
+
|
|
29
|
+
// Header versioning middleware
|
|
30
|
+
function versionMiddleware(req, res, next) {
|
|
31
|
+
const version = req.headers['api-version'] || '1';
|
|
32
|
+
req.apiVersion = parseInt(version);
|
|
33
|
+
next();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
app.get('/users', versionMiddleware, (req, res) => {
|
|
37
|
+
if (req.apiVersion >= 2) {
|
|
38
|
+
return handleV2(req, res);
|
|
39
|
+
}
|
|
40
|
+
return handleV1(req, res);
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Deprecation Strategy
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
// Add deprecation headers
|
|
48
|
+
res.setHeader('Deprecation', 'true');
|
|
49
|
+
res.setHeader('Sunset', 'Sat, 01 Jan 2025 00:00:00 GMT');
|
|
50
|
+
res.setHeader('Link', '</api/v2/users>; rel="successor-version"');
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Best Practices
|
|
54
|
+
|
|
55
|
+
- Version from the start
|
|
56
|
+
- Support at least N-1 versions
|
|
57
|
+
- Document deprecation timeline
|
|
58
|
+
- Provide migration guides
|
|
59
|
+
- Use semantic versioning for breaking changes
|
|
60
|
+
- Consider backwards-compatible changes first
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Clean Architecture
|
|
2
|
+
|
|
3
|
+
## Core Principle
|
|
4
|
+
|
|
5
|
+
Dependencies point inward. Inner layers know nothing about outer layers.
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
┌─────────────────────────────────────────────┐
|
|
9
|
+
│ Frameworks & Drivers │
|
|
10
|
+
│ ┌─────────────────────────────────────┐ │
|
|
11
|
+
│ │ Interface Adapters │ │
|
|
12
|
+
│ │ ┌─────────────────────────────┐ │ │
|
|
13
|
+
│ │ │ Application Business │ │ │
|
|
14
|
+
│ │ │ ┌─────────────────────┐ │ │ │
|
|
15
|
+
│ │ │ │ Enterprise Business│ │ │ │
|
|
16
|
+
│ │ │ │ (Entities) │ │ │ │
|
|
17
|
+
│ │ │ └─────────────────────┘ │ │ │
|
|
18
|
+
│ │ │ (Use Cases) │ │ │
|
|
19
|
+
│ │ └─────────────────────────────┘ │ │
|
|
20
|
+
│ │ (Controllers, Gateways) │ │
|
|
21
|
+
│ └─────────────────────────────────────┘ │
|
|
22
|
+
│ (Web, DB, External APIs) │
|
|
23
|
+
└─────────────────────────────────────────────┘
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## The Dependency Rule
|
|
27
|
+
|
|
28
|
+
Source code dependencies only point inward.
|
|
29
|
+
|
|
30
|
+
## Layer Structure
|
|
31
|
+
|
|
32
|
+
### Entities (Enterprise Business Rules)
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
class Order {
|
|
36
|
+
constructor(
|
|
37
|
+
public readonly id: string,
|
|
38
|
+
private items: OrderItem[],
|
|
39
|
+
private status: OrderStatus
|
|
40
|
+
) {}
|
|
41
|
+
|
|
42
|
+
calculateTotal(): Money {
|
|
43
|
+
return this.items.reduce(
|
|
44
|
+
(sum, item) => sum.add(item.subtotal()),
|
|
45
|
+
Money.zero()
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
canBeCancelled(): boolean {
|
|
50
|
+
return this.status === OrderStatus.Pending;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Use Cases (Application Business Rules)
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
class CreateOrderUseCase {
|
|
59
|
+
constructor(
|
|
60
|
+
private orderRepository: OrderRepository,
|
|
61
|
+
private productRepository: ProductRepository
|
|
62
|
+
) {}
|
|
63
|
+
|
|
64
|
+
async execute(request: CreateOrderRequest): Promise<CreateOrderResponse> {
|
|
65
|
+
const products = await this.productRepository.findByIds(request.productIds);
|
|
66
|
+
const order = new Order(generateId(), this.createItems(products));
|
|
67
|
+
await this.orderRepository.save(order);
|
|
68
|
+
return { orderId: order.id };
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Interface Adapters
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
// Controller (adapts HTTP to use case)
|
|
77
|
+
class OrderController {
|
|
78
|
+
constructor(private createOrder: CreateOrderUseCase) {}
|
|
79
|
+
|
|
80
|
+
async create(req: Request, res: Response) {
|
|
81
|
+
const result = await this.createOrder.execute(req.body);
|
|
82
|
+
res.json(result);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Repository Implementation (adapts use case to database)
|
|
87
|
+
class PostgreSQLOrderRepository implements OrderRepository {
|
|
88
|
+
async save(order: Order): Promise<void> {
|
|
89
|
+
await this.db.query('INSERT INTO orders...');
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Frameworks & Drivers
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
// Express setup
|
|
98
|
+
const app = express();
|
|
99
|
+
app.post('/orders', (req, res) => orderController.create(req, res));
|
|
100
|
+
|
|
101
|
+
// Database connection
|
|
102
|
+
const db = new Pool({ connectionString: process.env.DATABASE_URL });
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Best Practices
|
|
106
|
+
|
|
107
|
+
- Keep entities pure with no framework dependencies
|
|
108
|
+
- Use cases orchestrate domain logic
|
|
109
|
+
- Interfaces defined in inner layers, implemented in outer layers
|
|
110
|
+
- Cross boundaries with simple data structures
|
|
111
|
+
- Test use cases independently of frameworks
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# DDD Strategic Patterns
|
|
2
|
+
|
|
3
|
+
## Ubiquitous Language
|
|
4
|
+
|
|
5
|
+
Use the same terminology in code, documentation, and conversations.
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
// Domain experts say "place an order"
|
|
9
|
+
class Order {
|
|
10
|
+
place(): void { /* not submit(), not create() */ }
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Domain experts say "items are added to cart"
|
|
14
|
+
class ShoppingCart {
|
|
15
|
+
addItem(product: Product): void { /* not insert(), not push() */ }
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Bounded Contexts
|
|
20
|
+
|
|
21
|
+
Explicit boundaries where a model applies consistently.
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
┌─────────────────┐ ┌─────────────────┐
|
|
25
|
+
│ Sales │ │ Warehouse │
|
|
26
|
+
│ Context │ │ Context │
|
|
27
|
+
├─────────────────┤ ├─────────────────┤
|
|
28
|
+
│ Order │ │ Order │
|
|
29
|
+
│ - customerId │ │ - shipmentId │
|
|
30
|
+
│ - items[] │ │ - pickingList │
|
|
31
|
+
│ - total │ │ - status │
|
|
32
|
+
└─────────────────┘ └─────────────────┘
|
|
33
|
+
Same term, different model
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Context Mapping Patterns
|
|
37
|
+
|
|
38
|
+
### Shared Kernel
|
|
39
|
+
Two contexts share a subset of the model.
|
|
40
|
+
|
|
41
|
+
### Customer/Supplier
|
|
42
|
+
Upstream context provides what downstream needs.
|
|
43
|
+
|
|
44
|
+
### Conformist
|
|
45
|
+
Downstream adopts upstream's model entirely.
|
|
46
|
+
|
|
47
|
+
### Anti-Corruption Layer
|
|
48
|
+
Translate between contexts to protect domain model.
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
class InventoryAntiCorruptionLayer {
|
|
52
|
+
constructor(private legacyInventorySystem: LegacyInventory) {}
|
|
53
|
+
|
|
54
|
+
checkAvailability(productId: ProductId): Promise<boolean> {
|
|
55
|
+
// Translate from legacy format to domain model
|
|
56
|
+
const legacyResult = await this.legacyInventorySystem.getStock(
|
|
57
|
+
productId.toString()
|
|
58
|
+
);
|
|
59
|
+
return legacyResult.qty > 0;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Module Organization
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
src/
|
|
68
|
+
├── sales/ # Sales bounded context
|
|
69
|
+
│ ├── domain/
|
|
70
|
+
│ │ ├── order.ts
|
|
71
|
+
│ │ └── customer.ts
|
|
72
|
+
│ ├── application/
|
|
73
|
+
│ │ └── place-order.ts
|
|
74
|
+
│ └── infrastructure/
|
|
75
|
+
│ └── order-repository.ts
|
|
76
|
+
├── warehouse/ # Warehouse bounded context
|
|
77
|
+
│ ├── domain/
|
|
78
|
+
│ │ └── shipment.ts
|
|
79
|
+
│ └── ...
|
|
80
|
+
└── shared/ # Shared kernel
|
|
81
|
+
└── money.ts
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Best Practices
|
|
85
|
+
|
|
86
|
+
- Define context boundaries based on team structure and business capabilities
|
|
87
|
+
- Use ubiquitous language within each context
|
|
88
|
+
- Communicate between contexts via events or explicit APIs
|
|
89
|
+
- Protect domain model with anti-corruption layers when integrating legacy systems
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# DDD Tactical Patterns
|
|
2
|
+
|
|
3
|
+
## Entities
|
|
4
|
+
|
|
5
|
+
Objects with identity that persists through state changes.
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
class User {
|
|
9
|
+
constructor(
|
|
10
|
+
public readonly id: UserId,
|
|
11
|
+
private email: Email,
|
|
12
|
+
private name: string
|
|
13
|
+
) {}
|
|
14
|
+
|
|
15
|
+
changeEmail(newEmail: Email): void {
|
|
16
|
+
this.email = newEmail;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
equals(other: User): boolean {
|
|
20
|
+
return this.id.equals(other.id);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Value Objects
|
|
26
|
+
|
|
27
|
+
Immutable objects defined by their attributes.
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
class Email {
|
|
31
|
+
private readonly value: string;
|
|
32
|
+
|
|
33
|
+
constructor(email: string) {
|
|
34
|
+
if (!this.isValid(email)) {
|
|
35
|
+
throw new InvalidEmailError(email);
|
|
36
|
+
}
|
|
37
|
+
this.value = email.toLowerCase();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
equals(other: Email): boolean {
|
|
41
|
+
return this.value === other.value;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
class Money {
|
|
46
|
+
constructor(
|
|
47
|
+
public readonly amount: number,
|
|
48
|
+
public readonly currency: Currency
|
|
49
|
+
) {
|
|
50
|
+
Object.freeze(this);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
add(other: Money): Money {
|
|
54
|
+
this.assertSameCurrency(other);
|
|
55
|
+
return new Money(this.amount + other.amount, this.currency);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Aggregates
|
|
61
|
+
|
|
62
|
+
Cluster of entities and value objects with a root entity.
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
class Order {
|
|
66
|
+
private items: OrderItem[] = [];
|
|
67
|
+
|
|
68
|
+
constructor(
|
|
69
|
+
public readonly id: OrderId,
|
|
70
|
+
private customerId: CustomerId
|
|
71
|
+
) {}
|
|
72
|
+
|
|
73
|
+
addItem(product: Product, quantity: number): void {
|
|
74
|
+
const item = new OrderItem(product.id, product.price, quantity);
|
|
75
|
+
this.items.push(item);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// All modifications go through aggregate root
|
|
79
|
+
removeItem(productId: ProductId): void {
|
|
80
|
+
this.items = this.items.filter(item => !item.productId.equals(productId));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Domain Events
|
|
86
|
+
|
|
87
|
+
Capture something that happened in the domain.
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
class OrderPlaced implements DomainEvent {
|
|
91
|
+
constructor(
|
|
92
|
+
public readonly orderId: OrderId,
|
|
93
|
+
public readonly customerId: CustomerId,
|
|
94
|
+
public readonly occurredOn: Date = new Date()
|
|
95
|
+
) {}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
class Order {
|
|
99
|
+
private events: DomainEvent[] = [];
|
|
100
|
+
|
|
101
|
+
place(): void {
|
|
102
|
+
this.status = OrderStatus.Placed;
|
|
103
|
+
this.events.push(new OrderPlaced(this.id, this.customerId));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
pullEvents(): DomainEvent[] {
|
|
107
|
+
const events = [...this.events];
|
|
108
|
+
this.events = [];
|
|
109
|
+
return events;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Repositories
|
|
115
|
+
|
|
116
|
+
Abstract persistence for aggregates.
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
interface OrderRepository {
|
|
120
|
+
findById(id: OrderId): Promise<Order | null>;
|
|
121
|
+
save(order: Order): Promise<void>;
|
|
122
|
+
nextId(): OrderId;
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Best Practices
|
|
127
|
+
|
|
128
|
+
- One repository per aggregate root
|
|
129
|
+
- Aggregates should be small
|
|
130
|
+
- Reference other aggregates by ID
|
|
131
|
+
- Publish domain events for cross-aggregate communication
|
|
132
|
+
- Keep value objects immutable
|