@el-j/magic-helix-core 4.0.0-beta.2 → 4.0.0-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. package/dist/index-B88j4AyE.js +13 -0
  2. package/dist/index-B88j4AyE.js.map +1 -0
  3. package/dist/index-CY-pQbuu.cjs +2 -0
  4. package/dist/index-CY-pQbuu.cjs.map +1 -0
  5. package/dist/index.cjs +75 -1
  6. package/dist/index.cjs.map +1 -1
  7. package/dist/index.d.ts +0 -1
  8. package/dist/index.mjs +2214 -51
  9. package/dist/index.mjs.map +1 -1
  10. package/dist/pattern-combiner.d.ts +1 -1
  11. package/dist/plugin-loader.d.ts +2 -1
  12. package/package.json +4 -4
  13. package/dist/BasePlugin-6wv0hYJ9.js +0 -98
  14. package/dist/BasePlugin-6wv0hYJ9.js.map +0 -1
  15. package/dist/BasePlugin-odQJAKA-.cjs +0 -2
  16. package/dist/BasePlugin-odQJAKA-.cjs.map +0 -1
  17. package/dist/builtin-plugins/base/BasePlugin.d.ts +0 -69
  18. package/dist/builtin-plugins/cpp/index.d.ts +0 -46
  19. package/dist/builtin-plugins/csharp/index.d.ts +0 -20
  20. package/dist/builtin-plugins/go/index.d.ts +0 -23
  21. package/dist/builtin-plugins/index.d.ts +0 -16
  22. package/dist/builtin-plugins/java/index.d.ts +0 -22
  23. package/dist/builtin-plugins/nodejs/index.d.ts +0 -44
  24. package/dist/builtin-plugins/php/index.d.ts +0 -20
  25. package/dist/builtin-plugins/python/index.d.ts +0 -27
  26. package/dist/builtin-plugins/ruby/index.d.ts +0 -20
  27. package/dist/builtin-plugins/rust/index.d.ts +0 -53
  28. package/dist/builtin-plugins/swift/index.d.ts +0 -22
  29. package/dist/default_templates/angular/angular-core.md +0 -19
  30. package/dist/default_templates/architecture/codeowners.md +0 -123
  31. package/dist/default_templates/architecture/monorepo.md +0 -146
  32. package/dist/default_templates/architecture/nx.md +0 -122
  33. package/dist/default_templates/architecture/turborepo.md +0 -114
  34. package/dist/default_templates/ci/github-actions.md +0 -268
  35. package/dist/default_templates/ci/gitlab-ci.md +0 -330
  36. package/dist/default_templates/containers/docker-multistage.md +0 -120
  37. package/dist/default_templates/containers/kubernetes-deploy.md +0 -210
  38. package/dist/default_templates/devops/docker-compose.md +0 -111
  39. package/dist/default_templates/devops/docker-dockerfile.md +0 -94
  40. package/dist/default_templates/devops/github-actions.md +0 -160
  41. package/dist/default_templates/devops/gitlab-ci.md +0 -210
  42. package/dist/default_templates/dotnet/framework-aspnetcore.md +0 -205
  43. package/dist/default_templates/dotnet/framework-blazor.md +0 -271
  44. package/dist/default_templates/dotnet/lang-csharp.md +0 -162
  45. package/dist/default_templates/generic/lang-typescript.md +0 -57
  46. package/dist/default_templates/generic/state-redux.md +0 -21
  47. package/dist/default_templates/generic/state-rxjs.md +0 -6
  48. package/dist/default_templates/generic/style-mui.md +0 -23
  49. package/dist/default_templates/generic/style-tailwind.md +0 -76
  50. package/dist/default_templates/generic/test-cypress.md +0 -21
  51. package/dist/default_templates/generic/test-jest.md +0 -20
  52. package/dist/default_templates/generic/test-playwright.md +0 -21
  53. package/dist/default_templates/generic/test-vitest.md +0 -131
  54. package/dist/default_templates/go/lang-go.md +0 -571
  55. package/dist/default_templates/java/build-gradle.md +0 -102
  56. package/dist/default_templates/java/build-maven.md +0 -86
  57. package/dist/default_templates/java/framework-spring-boot.md +0 -179
  58. package/dist/default_templates/java/lang-java.md +0 -78
  59. package/dist/default_templates/java/lang-kotlin.md +0 -88
  60. package/dist/default_templates/meta/magic-helix-meta.md +0 -213
  61. package/dist/default_templates/meta/meta-debug.md +0 -459
  62. package/dist/default_templates/meta/meta-implement.md +0 -450
  63. package/dist/default_templates/meta/meta-roadmap.md +0 -265
  64. package/dist/default_templates/nestjs/nestjs-core.md +0 -7
  65. package/dist/default_templates/patterns/architecture/clean-architecture.md +0 -469
  66. package/dist/default_templates/patterns/architecture/dependency-injection.md +0 -517
  67. package/dist/default_templates/patterns/architecture/domain-driven-design.md +0 -621
  68. package/dist/default_templates/patterns/architecture/layered-architecture.md +0 -382
  69. package/dist/default_templates/patterns/architecture/repository-pattern.md +0 -408
  70. package/dist/default_templates/patterns/domain-expertise/nextjs-rules.md +0 -115
  71. package/dist/default_templates/patterns/domain-expertise/react-patterns.md +0 -181
  72. package/dist/default_templates/patterns/domain-expertise/server-components.md +0 -212
  73. package/dist/default_templates/patterns/domain-expertise/shadcn-ui.md +0 -52
  74. package/dist/default_templates/patterns/domain-expertise/tailwind-patterns.md +0 -52
  75. package/dist/default_templates/patterns/environment/container-awareness.md +0 -17
  76. package/dist/default_templates/patterns/environment/ide-features.md +0 -17
  77. package/dist/default_templates/patterns/environment/os-commands.md +0 -17
  78. package/dist/default_templates/patterns/organization/heading-hierarchy.md +0 -103
  79. package/dist/default_templates/patterns/organization/sequential-workflows.md +0 -102
  80. package/dist/default_templates/patterns/organization/xml-rule-groups.md +0 -64
  81. package/dist/default_templates/patterns/reasoning/agent-loop.md +0 -151
  82. package/dist/default_templates/patterns/reasoning/confirmation-gates.md +0 -141
  83. package/dist/default_templates/patterns/reasoning/dependency-analysis.md +0 -132
  84. package/dist/default_templates/patterns/reasoning/one-tool-per-iteration.md +0 -152
  85. package/dist/default_templates/patterns/reasoning/preview-before-action.md +0 -194
  86. package/dist/default_templates/patterns/reasoning/reflection-checkpoints.md +0 -166
  87. package/dist/default_templates/patterns/reasoning/result-verification.md +0 -157
  88. package/dist/default_templates/patterns/reasoning/subtask-breakdown.md +0 -131
  89. package/dist/default_templates/patterns/reasoning/thinking-tags.md +0 -100
  90. package/dist/default_templates/patterns/role-definition/capability-declarations.md +0 -72
  91. package/dist/default_templates/patterns/role-definition/expert-identity.md +0 -45
  92. package/dist/default_templates/patterns/role-definition/scope-boundaries.md +0 -61
  93. package/dist/default_templates/patterns/safety/code-safety-rules.md +0 -17
  94. package/dist/default_templates/patterns/safety/credential-handling.md +0 -17
  95. package/dist/default_templates/patterns/safety/destructive-warnings.md +0 -17
  96. package/dist/default_templates/patterns/safety/refusal-messages.md +0 -17
  97. package/dist/default_templates/patterns/tone/adaptive-tone.md +0 -17
  98. package/dist/default_templates/patterns/tone/concise-communication.md +0 -17
  99. package/dist/default_templates/patterns/tone/forbidden-phrases.md +0 -17
  100. package/dist/default_templates/patterns/tool-guidelines/function-schemas.md +0 -143
  101. package/dist/default_templates/patterns/tool-guidelines/parameter-examples.md +0 -137
  102. package/dist/default_templates/patterns/tool-guidelines/usage-policies.md +0 -105
  103. package/dist/default_templates/php/framework-laravel.md +0 -112
  104. package/dist/default_templates/php/lang-php.md +0 -94
  105. package/dist/default_templates/python/lang-python.md +0 -508
  106. package/dist/default_templates/react/react-core.md +0 -677
  107. package/dist/default_templates/react/react-zustand.md +0 -7
  108. package/dist/default_templates/ruby/framework-rails.md +0 -309
  109. package/dist/default_templates/ruby/framework-sinatra.md +0 -227
  110. package/dist/default_templates/ruby/lang-ruby.md +0 -216
  111. package/dist/default_templates/rust/lang-rust.md +0 -89
  112. package/dist/default_templates/swift/framework-vapor.md +0 -352
  113. package/dist/default_templates/swift/lang-swift.md +0 -291
  114. package/dist/default_templates/vue/style-primevue.md +0 -6
  115. package/dist/default_templates/vue/style-quasar.md +0 -22
  116. package/dist/default_templates/vue/vue-core.md +0 -108
  117. package/dist/default_templates/vue/vue-pinia.md +0 -5
  118. package/dist/index-0GK4RlUx.js +0 -1748
  119. package/dist/index-0GK4RlUx.js.map +0 -1
  120. package/dist/index-AkVwRl-r.js +0 -92
  121. package/dist/index-AkVwRl-r.js.map +0 -1
  122. package/dist/index-B6BeG1yT.cjs +0 -68
  123. package/dist/index-B6BeG1yT.cjs.map +0 -1
  124. package/dist/index-B8pyjKdF.js +0 -94
  125. package/dist/index-B8pyjKdF.js.map +0 -1
  126. package/dist/index-BQ6v041y.js +0 -13
  127. package/dist/index-BQ6v041y.js.map +0 -1
  128. package/dist/index-Baxb1vI_.js +0 -210
  129. package/dist/index-Baxb1vI_.js.map +0 -1
  130. package/dist/index-Bg8DD8ku.js +0 -216
  131. package/dist/index-Bg8DD8ku.js.map +0 -1
  132. package/dist/index-BqTqxCpG.cjs +0 -89
  133. package/dist/index-BqTqxCpG.cjs.map +0 -1
  134. package/dist/index-Bv4Q1Pr7.cjs +0 -33
  135. package/dist/index-Bv4Q1Pr7.cjs.map +0 -1
  136. package/dist/index-CN8J45Nc.cjs +0 -24
  137. package/dist/index-CN8J45Nc.cjs.map +0 -1
  138. package/dist/index-CPbv2Od1.js +0 -62
  139. package/dist/index-CPbv2Od1.js.map +0 -1
  140. package/dist/index-Cf-MC6Al.js +0 -63
  141. package/dist/index-Cf-MC6Al.js.map +0 -1
  142. package/dist/index-DDPXXXDy.cjs +0 -19
  143. package/dist/index-DDPXXXDy.cjs.map +0 -1
  144. package/dist/index-DO30AzDe.cjs +0 -19
  145. package/dist/index-DO30AzDe.cjs.map +0 -1
  146. package/dist/index-DkvW5yBY.js +0 -2249
  147. package/dist/index-DkvW5yBY.js.map +0 -1
  148. package/dist/index-Dn1ehjIj.cjs +0 -80
  149. package/dist/index-Dn1ehjIj.cjs.map +0 -1
  150. package/dist/index-DqHvgoXJ.cjs +0 -19
  151. package/dist/index-DqHvgoXJ.cjs.map +0 -1
  152. package/dist/index-K39pdw94.cjs +0 -31
  153. package/dist/index-K39pdw94.cjs.map +0 -1
  154. package/dist/index-OT2XAJkc.js +0 -117
  155. package/dist/index-OT2XAJkc.js.map +0 -1
  156. package/dist/index-TPAX4XKg.cjs +0 -30
  157. package/dist/index-TPAX4XKg.cjs.map +0 -1
  158. package/dist/index-WmVSB57y.js +0 -107
  159. package/dist/index-WmVSB57y.js.map +0 -1
  160. package/dist/index-mYXvc3Fs.js +0 -68
  161. package/dist/index-mYXvc3Fs.js.map +0 -1
  162. package/dist/index-nioXOg4m.cjs +0 -76
  163. package/dist/index-nioXOg4m.cjs.map +0 -1
  164. package/dist/index-okhY3fWD.cjs +0 -2
  165. package/dist/index-okhY3fWD.cjs.map +0 -1
@@ -1,469 +0,0 @@
1
- # Clean Architecture Pattern
2
-
3
- ## Purpose
4
- Enforce separation of concerns through concentric layers, keeping business logic independent of frameworks, UI, and external dependencies.
5
-
6
- ## Core Principles
7
-
8
- ### Layer Dependency Rule
9
- **Inner layers NEVER depend on outer layers. Dependencies point inward only.**
10
-
11
- ```
12
- ┌─────────────────────────────────────┐
13
- │ Frameworks & Drivers (UI, DB) │ ← Outermost layer
14
- ├─────────────────────────────────────┤
15
- │ Interface Adapters (Controllers)│
16
- ├─────────────────────────────────────┤
17
- │ Application Business Rules │
18
- │ (Use Cases / Interactors) │
19
- ├─────────────────────────────────────┤
20
- │ Enterprise Business Rules │
21
- │ (Entities / Domain Models) │ ← Innermost layer
22
- └─────────────────────────────────────┘
23
- ```
24
-
25
- ### Layer Responsibilities
26
-
27
- #### 1. Entities (Domain Models) - Innermost
28
- **Pure business logic. No framework dependencies. Use plain objects + pure functions.**
29
-
30
- ```typescript
31
- // ✅ Modern: Domain entity as immutable data + pure functions
32
- export type User = {
33
- readonly id: string;
34
- readonly email: string;
35
- readonly passwordHash: string;
36
- readonly createdAt: Date;
37
- };
38
-
39
- // Factory function to create user
40
- export function createUser(email: string, password: string): User {
41
- if (!isValidEmail(email)) {
42
- throw new Error('Invalid email format');
43
- }
44
-
45
- return {
46
- id: generateId(),
47
- email,
48
- passwordHash: hashPassword(password),
49
- createdAt: new Date(),
50
- };
51
- }
52
-
53
- // Pure functions for business logic
54
- export function validatePassword(user: User, plainPassword: string): boolean {
55
- return hashPassword(plainPassword) === user.passwordHash;
56
- }
57
-
58
- export function changeEmail(user: User, newEmail: string): User {
59
- if (!isValidEmail(newEmail)) {
60
- throw new Error('Invalid email format');
61
- }
62
- // Immutable update - return new object
63
- return { ...user, email: newEmail };
64
- }
65
-
66
- function isValidEmail(email: string): boolean {
67
- return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
68
- }
69
-
70
- function hashPassword(plain: string): string {
71
- // Business rule for password hashing
72
- return /* hashing logic */;
73
- }
74
- ```
75
-
76
- #### 2. Use Cases (Application Business Rules)
77
- **Orchestrate entities. Define application-specific business rules. Framework-independent.**
78
-
79
- ```typescript
80
- // ✅ Modern: Repository as plain interface (no class)
81
- export type UserRepository = {
82
- findById: (id: string) => Promise<User | null>;
83
- findByEmail: (email: string) => Promise<User | null>;
84
- save: (user: User) => Promise<void>;
85
- };
86
-
87
- export type EmailService = {
88
- sendWelcomeEmail: (email: string) => Promise<void>;
89
- };
90
-
91
- // ✅ Use case as pure function with dependencies injected as parameters
92
- export async function registerUser(
93
- email: string,
94
- password: string,
95
- deps: {
96
- userRepository: UserRepository;
97
- emailService: EmailService;
98
- }
99
- ): Promise<User> {
100
- // Application business rule
101
- const existingUser = await deps.userRepository.findByEmail(email);
102
- if (existingUser) {
103
- throw new Error('User already exists');
104
- }
105
-
106
- const user = createUser(email, password);
107
- await deps.userRepository.save(user);
108
- await deps.emailService.sendWelcomeEmail(email);
109
-
110
- return user;
111
- }
112
-
113
- // Alternative: Factory pattern for partial application
114
- export function makeRegisterUser(deps: {
115
- userRepository: UserRepository;
116
- emailService: EmailService;
117
- }) {
118
- return async (email: string, password: string) => {
119
- return registerUser(email, password, deps);
120
- };
121
- }
122
- ```
123
-
124
- #### 3. Interface Adapters (Controllers, Presenters, Gateways)
125
- **Convert data between use cases and external layers. Adapt external formats to internal formats.**
126
-
127
- ```typescript
128
- // ✅ Modern: Controller as pure function (no class)
129
- export async function registerUserHandler(
130
- req: Request,
131
- res: Response,
132
- deps: { registerUser: typeof registerUser }
133
- ): Promise<void> {
134
- try {
135
- const { email, password } = req.body;
136
-
137
- // Adapt HTTP input to use case input
138
- const user = await deps.registerUser(email, password, deps);
139
-
140
- // Adapt use case output to HTTP response
141
- res.status(201).json({
142
- id: user.id,
143
- email: user.email,
144
- });
145
- } catch (error) {
146
- res.status(400).json({ error: error.message });
147
- }
148
- }
149
-
150
- // ✅ Repository adapter implements type interface
151
- export function createPostgresUserRepository(db: Database): UserRepository {
152
- return {
153
- async findById(id: string): Promise<User | null> {
154
- const row = await db.query('SELECT * FROM users WHERE id = $1', [id]);
155
- if (!row) return null;
156
-
157
- // Adapt database row to domain entity
158
- return {
159
- id: row.id,
160
- email: row.email,
161
- passwordHash: row.password_hash,
162
- createdAt: new Date(row.created_at),
163
- };
164
- },
165
-
166
- async findByEmail(email: string): Promise<User | null> {
167
- const row = await db.query('SELECT * FROM users WHERE email = $1', [email]);
168
- if (!row) return null;
169
-
170
- return {
171
- id: row.id,
172
- email: row.email,
173
- passwordHash: row.password_hash,
174
- createdAt: new Date(row.created_at),
175
- };
176
- },
177
-
178
- async save(user: User): Promise<void> {
179
- // Adapt domain entity to database row
180
- await db.query(
181
- 'INSERT INTO users (id, email, password_hash, created_at) VALUES ($1, $2, $3, $4)',
182
- [user.id, user.email, user.passwordHash, user.createdAt]
183
- );
184
- },
185
- };
186
- }
187
- ```
188
-
189
- #### 4. Frameworks & Drivers (UI, Database, External Services)
190
- **Implementation details. Frameworks, libraries, databases.**
191
-
192
- ```typescript
193
- // ✅ Modern: Framework-specific setup with functional composition
194
- import express from 'express';
195
- import { createDatabase } from './infrastructure/database';
196
- import { createPostgresUserRepository } from './adapters/repositories/user-repository';
197
- import { createSendGridEmailService } from './adapters/services/email-service';
198
- import { makeRegisterUser } from './usecases/register-user';
199
- import { registerUserHandler } from './adapters/controllers/user-controller';
200
-
201
- const app = express();
202
- const db = createDatabase();
203
-
204
- // Wire up dependencies (Dependency Injection via composition)
205
- const userRepository = createPostgresUserRepository(db);
206
- const emailService = createSendGridEmailService(process.env.SENDGRID_KEY!);
207
-
208
- // Create use case with dependencies
209
- const registerUser = makeRegisterUser({ userRepository, emailService });
210
-
211
- // Register route with handler
212
- app.post('/api/users', (req, res) =>
213
- registerUserHandler(req, res, { registerUser })
214
- );
215
-
216
- app.listen(3000);
217
- ```
218
-
219
- ## File Organization
220
-
221
- ### Recommended Structure
222
-
223
- ```
224
- src/
225
- ├── domain/ # Entities layer (innermost)
226
- │ ├── entities/
227
- │ │ ├── User.ts
228
- │ │ ├── Order.ts
229
- │ │ └── Product.ts
230
- │ └── value-objects/
231
- │ ├── Email.ts
232
- │ └── Money.ts
233
-
234
- ├── usecases/ # Application business rules
235
- │ ├── user/
236
- │ │ ├── RegisterUser.ts
237
- │ │ ├── LoginUser.ts
238
- │ │ └── interfaces/ # Interfaces for repositories, services
239
- │ │ ├── IUserRepository.ts
240
- │ │ └── IEmailService.ts
241
- │ └── order/
242
- │ ├── CreateOrder.ts
243
- │ └── interfaces/
244
- │ └── IOrderRepository.ts
245
-
246
- ├── adapters/ # Interface adapters layer
247
- │ ├── controllers/ # HTTP/API controllers
248
- │ │ ├── UserController.ts
249
- │ │ └── OrderController.ts
250
- │ ├── presenters/ # Format output for UI
251
- │ │ └── UserPresenter.ts
252
- │ ├── repositories/ # Database implementations
253
- │ │ ├── PostgresUserRepository.ts
254
- │ │ └── PostgresOrderRepository.ts
255
- │ └── services/ # External service implementations
256
- │ └── SendGridEmailService.ts
257
-
258
- └── infrastructure/ # Frameworks & drivers (outermost)
259
- ├── database/
260
- │ └── postgres.ts
261
- ├── http/
262
- │ └── express-app.ts
263
- └── config/
264
- └── env.ts
265
- ```
266
-
267
- ## Testing Strategy
268
-
269
- ### Test Each Layer Independently
270
-
271
- ```typescript
272
- // ✅ Test domain entity (no mocks needed - pure logic)
273
- describe('User', () => {
274
- it('validates password correctly', () => {
275
- const user = new User('1', 'test@example.com', 'hashedPass');
276
- expect(user.validatePassword('wrongPass')).toBe(false);
277
- });
278
- });
279
-
280
- // ✅ Test use case (mock repository and service interfaces)
281
- describe('RegisterUserUseCase', () => {
282
- it('registers new user and sends welcome email', async () => {
283
- const mockRepo: IUserRepository = {
284
- findById: jest.fn().mockResolvedValue(null),
285
- save: jest.fn(),
286
- };
287
- const mockEmail: IEmailService = {
288
- sendWelcomeEmail: jest.fn(),
289
- };
290
-
291
- const useCase = new RegisterUserUseCase(mockRepo, mockEmail);
292
- await useCase.execute('new@example.com', 'password');
293
-
294
- expect(mockRepo.save).toHaveBeenCalled();
295
- expect(mockEmail.sendWelcomeEmail).toHaveBeenCalledWith('new@example.com');
296
- });
297
- });
298
-
299
- // ✅ Test controller (mock use case)
300
- describe('UserController', () => {
301
- it('returns 201 on successful registration', async () => {
302
- const mockUseCase = {
303
- execute: jest.fn().mockResolvedValue(new User('1', 'test@example.com', 'hash')),
304
- };
305
-
306
- const controller = new UserController(mockUseCase);
307
- const req = { body: { email: 'test@example.com', password: 'pass' } };
308
- const res = { status: jest.fn().mockReturnThis(), json: jest.fn() };
309
-
310
- await controller.register(req, res);
311
-
312
- expect(res.status).toHaveBeenCalledWith(201);
313
- });
314
- });
315
- ```
316
-
317
- ## Migration Strategy
318
-
319
- ### Step 1: Identify Current Layers
320
- - What's your business logic? (→ Entities)
321
- - What are your application operations? (→ Use Cases)
322
- - What talks to the outside world? (→ Adapters)
323
- - What are framework implementations? (→ Infrastructure)
324
-
325
- ### Step 2: Extract Entities First
326
- ```typescript
327
- // ❌ Before: Mixed concerns
328
- class UserService {
329
- async register(req: Request): Promise<Response> {
330
- const user = await db.query('SELECT * FROM users WHERE email = $1', [req.body.email]);
331
- if (user) throw new Error('Exists');
332
- await db.query('INSERT INTO users...');
333
- await sendEmail(req.body.email);
334
- return res.json({ success: true });
335
- }
336
- }
337
-
338
- // ✅ After: Extracted entity
339
- class User {
340
- constructor(public id: string, public email: string) {}
341
- // Business logic here
342
- }
343
- ```
344
-
345
- ### Step 3: Create Use Cases
346
- ```typescript
347
- // ✅ Extract application logic
348
- class RegisterUserUseCase {
349
- async execute(email: string, password: string): Promise<User> {
350
- // Application business rules
351
- }
352
- }
353
- ```
354
-
355
- ### Step 4: Create Adapters
356
- ```typescript
357
- // ✅ Separate database adapter
358
- class PostgresUserRepository implements IUserRepository {
359
- // Database-specific code
360
- }
361
- ```
362
-
363
- ## Test-Driven Development (TDD)
364
-
365
- **ALWAYS write tests before implementation. Follow the Red-Green-Refactor cycle.**
366
-
367
- ### TDD Workflow
368
-
369
- ```typescript
370
- // 1. RED: Write failing test first
371
- describe('createUser', () => {
372
- it('should create user with valid email and hashed password', () => {
373
- const user = createUser('test@example.com', 'password123');
374
-
375
- expect(user.email).toBe('test@example.com');
376
- expect(user.passwordHash).not.toBe('password123'); // Should be hashed
377
- expect(user.id).toBeDefined();
378
- expect(user.createdAt).toBeInstanceOf(Date);
379
- });
380
-
381
- it('should throw error for invalid email', () => {
382
- expect(() => createUser('invalid-email', 'password123'))
383
- .toThrow('Invalid email format');
384
- });
385
- });
386
-
387
- // 2. GREEN: Write minimal code to pass
388
- export function createUser(email: string, password: string): User {
389
- if (!isValidEmail(email)) {
390
- throw new Error('Invalid email format');
391
- }
392
-
393
- return {
394
- id: generateId(),
395
- email,
396
- passwordHash: hashPassword(password),
397
- createdAt: new Date(),
398
- };
399
- }
400
-
401
- // 3. REFACTOR: Clean up while keeping tests green
402
- ```
403
-
404
- ### Why Test-First?
405
-
406
- - **Design pressure**: Tests force you to think about interfaces before implementation
407
- - **Testable code**: Code written to be tested is naturally more modular and decoupled
408
- - **Living documentation**: Tests show how code is meant to be used
409
- - **Confidence**: Refactor freely knowing tests will catch breakage
410
- - **No test debt**: Tests are written, not "TODO"
411
-
412
- ### TDD Best Practices
413
-
414
- ```typescript
415
- // ✅ DO: Test behavior, not implementation
416
- it('should return active users', () => {
417
- const users = filterActiveUsers(allUsers);
418
- expect(users.every(u => u.isActive)).toBe(true);
419
- });
420
-
421
- // ❌ DON'T: Test internal implementation details
422
- it('should call isActive method', () => {
423
- expect(mockUser.isActive).toHaveBeenCalled(); // Fragile!
424
- });
425
-
426
- // ✅ DO: Use descriptive test names that explain business rules
427
- it('should reject orders when inventory is insufficient', () => {
428
- // ...
429
- });
430
-
431
- // ❌ DON'T: Use vague test names
432
- it('should work correctly', () => {
433
- // What does "correctly" mean?
434
- });
435
-
436
- // ✅ DO: Arrange-Act-Assert (AAA) pattern
437
- it('should calculate total with tax', () => {
438
- // Arrange
439
- const items = [{ price: 100 }, { price: 200 }];
440
- const taxRate = 0.1;
441
-
442
- // Act
443
- const total = calculateTotal(items, taxRate);
444
-
445
- // Assert
446
- expect(total).toBe(330); // (100 + 200) * 1.1
447
- });
448
- ```
449
-
450
- ### Test Coverage Goals
451
-
452
- - **Entities/Domain**: 100% coverage - critical business logic
453
- - **Use Cases**: 90%+ coverage - application logic paths
454
- - **Adapters**: 80%+ coverage - focus on error handling
455
- - **Integration**: Key user journeys and edge cases
456
-
457
- ## Rules Summary
458
-
459
- - **ALWAYS** write tests BEFORE writing implementation code (TDD)
460
- - **ALWAYS** keep entities framework-free
461
- - **ALWAYS** define interfaces in use cases, implement in adapters
462
- - **ALWAYS** point dependencies inward (outer layers depend on inner)
463
- - **ALWAYS** test each layer independently with appropriate coverage
464
- - **NEVER** import frameworks in entities or use cases
465
- - **NEVER** let inner layers know about outer layers
466
- - **NEVER** skip writing tests (no test debt)
467
- - **PREFER** dependency injection over direct instantiation
468
- - **PREFER** small, focused use cases over large service classes
469
- - **PREFER** testing behavior over implementation details