@el-j/magic-helix-core 4.0.0-beta.1 → 4.0.0-beta.3

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 (160) 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 +2234 -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 +7 -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/csharp/index.d.ts +0 -20
  19. package/dist/builtin-plugins/go/index.d.ts +0 -23
  20. package/dist/builtin-plugins/index.d.ts +0 -15
  21. package/dist/builtin-plugins/java/index.d.ts +0 -22
  22. package/dist/builtin-plugins/nodejs/index.d.ts +0 -44
  23. package/dist/builtin-plugins/php/index.d.ts +0 -20
  24. package/dist/builtin-plugins/python/index.d.ts +0 -27
  25. package/dist/builtin-plugins/ruby/index.d.ts +0 -20
  26. package/dist/builtin-plugins/rust/index.d.ts +0 -53
  27. package/dist/builtin-plugins/swift/index.d.ts +0 -22
  28. package/dist/default_templates/angular/angular-core.md +0 -19
  29. package/dist/default_templates/architecture/codeowners.md +0 -123
  30. package/dist/default_templates/architecture/monorepo.md +0 -146
  31. package/dist/default_templates/architecture/nx.md +0 -122
  32. package/dist/default_templates/architecture/turborepo.md +0 -114
  33. package/dist/default_templates/ci/github-actions.md +0 -268
  34. package/dist/default_templates/ci/gitlab-ci.md +0 -330
  35. package/dist/default_templates/containers/docker-multistage.md +0 -120
  36. package/dist/default_templates/containers/kubernetes-deploy.md +0 -210
  37. package/dist/default_templates/devops/docker-compose.md +0 -111
  38. package/dist/default_templates/devops/docker-dockerfile.md +0 -94
  39. package/dist/default_templates/devops/github-actions.md +0 -160
  40. package/dist/default_templates/devops/gitlab-ci.md +0 -210
  41. package/dist/default_templates/dotnet/framework-aspnetcore.md +0 -205
  42. package/dist/default_templates/dotnet/framework-blazor.md +0 -271
  43. package/dist/default_templates/dotnet/lang-csharp.md +0 -162
  44. package/dist/default_templates/generic/lang-typescript.md +0 -11
  45. package/dist/default_templates/generic/state-redux.md +0 -21
  46. package/dist/default_templates/generic/state-rxjs.md +0 -6
  47. package/dist/default_templates/generic/style-mui.md +0 -23
  48. package/dist/default_templates/generic/style-tailwind.md +0 -6
  49. package/dist/default_templates/generic/test-cypress.md +0 -21
  50. package/dist/default_templates/generic/test-jest.md +0 -20
  51. package/dist/default_templates/generic/test-playwright.md +0 -21
  52. package/dist/default_templates/generic/test-vitest.md +0 -6
  53. package/dist/default_templates/go/lang-go.md +0 -571
  54. package/dist/default_templates/java/build-gradle.md +0 -102
  55. package/dist/default_templates/java/build-maven.md +0 -86
  56. package/dist/default_templates/java/framework-spring-boot.md +0 -179
  57. package/dist/default_templates/java/lang-java.md +0 -78
  58. package/dist/default_templates/java/lang-kotlin.md +0 -88
  59. package/dist/default_templates/meta/magic-helix-meta.md +0 -213
  60. package/dist/default_templates/meta/meta-debug.md +0 -459
  61. package/dist/default_templates/meta/meta-implement.md +0 -450
  62. package/dist/default_templates/meta/meta-roadmap.md +0 -265
  63. package/dist/default_templates/nestjs/nestjs-core.md +0 -7
  64. package/dist/default_templates/patterns/architecture/clean-architecture.md +0 -469
  65. package/dist/default_templates/patterns/architecture/dependency-injection.md +0 -517
  66. package/dist/default_templates/patterns/architecture/domain-driven-design.md +0 -621
  67. package/dist/default_templates/patterns/architecture/layered-architecture.md +0 -382
  68. package/dist/default_templates/patterns/architecture/repository-pattern.md +0 -408
  69. package/dist/default_templates/patterns/domain-expertise/nextjs-rules.md +0 -115
  70. package/dist/default_templates/patterns/domain-expertise/react-patterns.md +0 -181
  71. package/dist/default_templates/patterns/domain-expertise/server-components.md +0 -212
  72. package/dist/default_templates/patterns/domain-expertise/shadcn-ui.md +0 -52
  73. package/dist/default_templates/patterns/domain-expertise/tailwind-patterns.md +0 -52
  74. package/dist/default_templates/patterns/environment/container-awareness.md +0 -17
  75. package/dist/default_templates/patterns/environment/ide-features.md +0 -17
  76. package/dist/default_templates/patterns/environment/os-commands.md +0 -17
  77. package/dist/default_templates/patterns/organization/heading-hierarchy.md +0 -103
  78. package/dist/default_templates/patterns/organization/sequential-workflows.md +0 -102
  79. package/dist/default_templates/patterns/organization/xml-rule-groups.md +0 -64
  80. package/dist/default_templates/patterns/reasoning/agent-loop.md +0 -151
  81. package/dist/default_templates/patterns/reasoning/confirmation-gates.md +0 -141
  82. package/dist/default_templates/patterns/reasoning/dependency-analysis.md +0 -132
  83. package/dist/default_templates/patterns/reasoning/one-tool-per-iteration.md +0 -152
  84. package/dist/default_templates/patterns/reasoning/preview-before-action.md +0 -194
  85. package/dist/default_templates/patterns/reasoning/reflection-checkpoints.md +0 -166
  86. package/dist/default_templates/patterns/reasoning/result-verification.md +0 -157
  87. package/dist/default_templates/patterns/reasoning/subtask-breakdown.md +0 -131
  88. package/dist/default_templates/patterns/reasoning/thinking-tags.md +0 -100
  89. package/dist/default_templates/patterns/role-definition/capability-declarations.md +0 -72
  90. package/dist/default_templates/patterns/role-definition/expert-identity.md +0 -45
  91. package/dist/default_templates/patterns/role-definition/scope-boundaries.md +0 -61
  92. package/dist/default_templates/patterns/safety/code-safety-rules.md +0 -17
  93. package/dist/default_templates/patterns/safety/credential-handling.md +0 -17
  94. package/dist/default_templates/patterns/safety/destructive-warnings.md +0 -17
  95. package/dist/default_templates/patterns/safety/refusal-messages.md +0 -17
  96. package/dist/default_templates/patterns/tone/adaptive-tone.md +0 -17
  97. package/dist/default_templates/patterns/tone/concise-communication.md +0 -17
  98. package/dist/default_templates/patterns/tone/forbidden-phrases.md +0 -17
  99. package/dist/default_templates/patterns/tool-guidelines/function-schemas.md +0 -143
  100. package/dist/default_templates/patterns/tool-guidelines/parameter-examples.md +0 -137
  101. package/dist/default_templates/patterns/tool-guidelines/usage-policies.md +0 -105
  102. package/dist/default_templates/php/framework-laravel.md +0 -112
  103. package/dist/default_templates/php/lang-php.md +0 -94
  104. package/dist/default_templates/python/lang-python.md +0 -508
  105. package/dist/default_templates/react/react-core.md +0 -677
  106. package/dist/default_templates/react/react-zustand.md +0 -7
  107. package/dist/default_templates/ruby/framework-rails.md +0 -309
  108. package/dist/default_templates/ruby/framework-sinatra.md +0 -227
  109. package/dist/default_templates/ruby/lang-ruby.md +0 -216
  110. package/dist/default_templates/rust/lang-rust.md +0 -89
  111. package/dist/default_templates/swift/framework-vapor.md +0 -352
  112. package/dist/default_templates/swift/lang-swift.md +0 -291
  113. package/dist/default_templates/vue/style-primevue.md +0 -6
  114. package/dist/default_templates/vue/style-quasar.md +0 -22
  115. package/dist/default_templates/vue/vue-core.md +0 -28
  116. package/dist/default_templates/vue/vue-pinia.md +0 -5
  117. package/dist/index-AkVwRl-r.js +0 -92
  118. package/dist/index-AkVwRl-r.js.map +0 -1
  119. package/dist/index-B6BeG1yT.cjs +0 -68
  120. package/dist/index-B6BeG1yT.cjs.map +0 -1
  121. package/dist/index-B8pyjKdF.js +0 -94
  122. package/dist/index-B8pyjKdF.js.map +0 -1
  123. package/dist/index-B_6W_RnJ.cjs +0 -76
  124. package/dist/index-B_6W_RnJ.cjs.map +0 -1
  125. package/dist/index-Bg8DD8ku.js +0 -216
  126. package/dist/index-Bg8DD8ku.js.map +0 -1
  127. package/dist/index-BkJhe5Af.js +0 -1748
  128. package/dist/index-BkJhe5Af.js.map +0 -1
  129. package/dist/index-Bv4Q1Pr7.cjs +0 -33
  130. package/dist/index-Bv4Q1Pr7.cjs.map +0 -1
  131. package/dist/index-CN8J45Nc.cjs +0 -24
  132. package/dist/index-CN8J45Nc.cjs.map +0 -1
  133. package/dist/index-CPbv2Od1.js +0 -62
  134. package/dist/index-CPbv2Od1.js.map +0 -1
  135. package/dist/index-Cf-MC6Al.js +0 -63
  136. package/dist/index-Cf-MC6Al.js.map +0 -1
  137. package/dist/index-DDPXXXDy.cjs +0 -19
  138. package/dist/index-DDPXXXDy.cjs.map +0 -1
  139. package/dist/index-DO30AzDe.cjs +0 -19
  140. package/dist/index-DO30AzDe.cjs.map +0 -1
  141. package/dist/index-Dm37u5ut.js +0 -2128
  142. package/dist/index-Dm37u5ut.js.map +0 -1
  143. package/dist/index-DqHvgoXJ.cjs +0 -19
  144. package/dist/index-DqHvgoXJ.cjs.map +0 -1
  145. package/dist/index-J1qAfsnO.cjs +0 -2
  146. package/dist/index-J1qAfsnO.cjs.map +0 -1
  147. package/dist/index-Jz0HYZ7B.js +0 -13
  148. package/dist/index-Jz0HYZ7B.js.map +0 -1
  149. package/dist/index-K39pdw94.cjs +0 -31
  150. package/dist/index-K39pdw94.cjs.map +0 -1
  151. package/dist/index-L3IVvhd1.cjs +0 -89
  152. package/dist/index-L3IVvhd1.cjs.map +0 -1
  153. package/dist/index-OT2XAJkc.js +0 -117
  154. package/dist/index-OT2XAJkc.js.map +0 -1
  155. package/dist/index-TPAX4XKg.cjs +0 -30
  156. package/dist/index-TPAX4XKg.cjs.map +0 -1
  157. package/dist/index-WmVSB57y.js +0 -107
  158. package/dist/index-WmVSB57y.js.map +0 -1
  159. package/dist/index-mYXvc3Fs.js +0 -68
  160. package/dist/index-mYXvc3Fs.js.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