@mytechtoday/augment-extensions 1.2.1 → 1.2.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.
- package/AGENTS.md +33 -1
- package/README.md +3 -3
- package/augment-extensions/domain-rules/software-architecture/README.md +143 -0
- package/augment-extensions/domain-rules/software-architecture/examples/banking-layered.md +961 -0
- package/augment-extensions/domain-rules/software-architecture/examples/ecommerce-microservices.md +990 -0
- package/augment-extensions/domain-rules/software-architecture/examples/iot-eventdriven.md +882 -0
- package/augment-extensions/domain-rules/software-architecture/examples/monolith-to-microservices-migration.md +703 -0
- package/augment-extensions/domain-rules/software-architecture/examples/serverless-imageprocessing.md +957 -0
- package/augment-extensions/domain-rules/software-architecture/examples/trading-eventdriven.md +747 -0
- package/augment-extensions/domain-rules/software-architecture/module.json +119 -0
- package/augment-extensions/domain-rules/software-architecture/rules/challenges-solutions.md +763 -0
- package/augment-extensions/domain-rules/software-architecture/rules/definitions-terminology.md +409 -0
- package/augment-extensions/domain-rules/software-architecture/rules/design-principles.md +684 -0
- package/augment-extensions/domain-rules/software-architecture/rules/evaluation-testing.md +1381 -0
- package/augment-extensions/domain-rules/software-architecture/rules/event-driven-architecture.md +616 -0
- package/augment-extensions/domain-rules/software-architecture/rules/fundamentals.md +306 -0
- package/augment-extensions/domain-rules/software-architecture/rules/industry-architectures.md +554 -0
- package/augment-extensions/domain-rules/software-architecture/rules/layered-architecture.md +776 -0
- package/augment-extensions/domain-rules/software-architecture/rules/microservices-architecture.md +503 -0
- package/augment-extensions/domain-rules/software-architecture/rules/modeling-documentation.md +1199 -0
- package/augment-extensions/domain-rules/software-architecture/rules/monolithic-architecture.md +351 -0
- package/augment-extensions/domain-rules/software-architecture/rules/principles.md +556 -0
- package/augment-extensions/domain-rules/software-architecture/rules/quality-attributes.md +797 -0
- package/augment-extensions/domain-rules/software-architecture/rules/scalability-performance.md +1345 -0
- package/augment-extensions/domain-rules/software-architecture/rules/security-architecture.md +1039 -0
- package/augment-extensions/domain-rules/software-architecture/rules/serverless-architecture.md +711 -0
- package/augment-extensions/domain-rules/software-architecture/rules/skills-development.md +568 -0
- package/augment-extensions/domain-rules/software-architecture/rules/tools-methodologies.md +961 -0
- package/augment-extensions/workflows/beads/rules/workflow.md +1 -1
- package/modules.md +40 -3
- package/package.json +1 -1
|
@@ -0,0 +1,684 @@
|
|
|
1
|
+
# Design Principles
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This document covers fundamental design principles that guide the creation of clean, maintainable, and extensible code. These principles complement architectural principles by focusing on code-level design decisions.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Knowledge
|
|
10
|
+
|
|
11
|
+
### SOLID Principles
|
|
12
|
+
|
|
13
|
+
#### Single Responsibility Principle (SRP)
|
|
14
|
+
|
|
15
|
+
**Definition**
|
|
16
|
+
- A class should have one, and only one, reason to change
|
|
17
|
+
- Each class should have a single, well-defined responsibility
|
|
18
|
+
- Separates concerns at the class level
|
|
19
|
+
- Reduces coupling and increases cohesion
|
|
20
|
+
|
|
21
|
+
**Benefits**
|
|
22
|
+
- Easier to understand and maintain
|
|
23
|
+
- Easier to test (focused unit tests)
|
|
24
|
+
- Reduces risk of breaking changes
|
|
25
|
+
- Enables better code organization
|
|
26
|
+
|
|
27
|
+
**Violations**
|
|
28
|
+
- God classes (do everything)
|
|
29
|
+
- Classes with multiple unrelated responsibilities
|
|
30
|
+
- Classes that change for multiple reasons
|
|
31
|
+
|
|
32
|
+
#### Open/Closed Principle (OCP)
|
|
33
|
+
|
|
34
|
+
**Definition**
|
|
35
|
+
- Software entities should be open for extension but closed for modification
|
|
36
|
+
- Add new functionality without changing existing code
|
|
37
|
+
- Use abstraction and polymorphism
|
|
38
|
+
- Protects existing code from breaking changes
|
|
39
|
+
|
|
40
|
+
**Benefits**
|
|
41
|
+
- Reduces regression risk
|
|
42
|
+
- Enables plugin architectures
|
|
43
|
+
- Supports versioning and backward compatibility
|
|
44
|
+
- Encourages abstraction
|
|
45
|
+
|
|
46
|
+
**Implementation Strategies**
|
|
47
|
+
- Inheritance and polymorphism
|
|
48
|
+
- Strategy pattern
|
|
49
|
+
- Decorator pattern
|
|
50
|
+
- Dependency injection
|
|
51
|
+
|
|
52
|
+
#### Liskov Substitution Principle (LSP)
|
|
53
|
+
|
|
54
|
+
**Definition**
|
|
55
|
+
- Subtypes must be substitutable for their base types
|
|
56
|
+
- Derived classes must honor the contract of base classes
|
|
57
|
+
- Behavioral compatibility, not just structural
|
|
58
|
+
- Preserves correctness when using polymorphism
|
|
59
|
+
|
|
60
|
+
**Benefits**
|
|
61
|
+
- Ensures reliable polymorphism
|
|
62
|
+
- Prevents unexpected behavior
|
|
63
|
+
- Maintains type safety
|
|
64
|
+
- Enables design by contract
|
|
65
|
+
|
|
66
|
+
**Violations**
|
|
67
|
+
- Strengthening preconditions in subtypes
|
|
68
|
+
- Weakening postconditions in subtypes
|
|
69
|
+
- Throwing new exceptions not in base contract
|
|
70
|
+
- Changing expected behavior
|
|
71
|
+
|
|
72
|
+
#### Interface Segregation Principle (ISP)
|
|
73
|
+
|
|
74
|
+
**Definition**
|
|
75
|
+
- Clients should not be forced to depend on interfaces they don't use
|
|
76
|
+
- Many specific interfaces are better than one general interface
|
|
77
|
+
- Prevents fat interfaces
|
|
78
|
+
- Reduces coupling between clients and implementations
|
|
79
|
+
|
|
80
|
+
**Benefits**
|
|
81
|
+
- Reduces unnecessary dependencies
|
|
82
|
+
- Improves code clarity
|
|
83
|
+
- Enables independent evolution
|
|
84
|
+
- Supports role-based design
|
|
85
|
+
|
|
86
|
+
**Implementation**
|
|
87
|
+
- Split large interfaces into smaller, focused ones
|
|
88
|
+
- Use role interfaces
|
|
89
|
+
- Apply composition over inheritance
|
|
90
|
+
- Design for specific client needs
|
|
91
|
+
|
|
92
|
+
#### Dependency Inversion Principle (DIP)
|
|
93
|
+
|
|
94
|
+
**Definition**
|
|
95
|
+
- High-level modules should not depend on low-level modules; both should depend on abstractions
|
|
96
|
+
- Abstractions should not depend on details; details should depend on abstractions
|
|
97
|
+
- Inverts traditional dependency flow
|
|
98
|
+
- Enables flexibility and testability
|
|
99
|
+
|
|
100
|
+
**Benefits**
|
|
101
|
+
- Decouples high-level and low-level code
|
|
102
|
+
- Enables dependency injection
|
|
103
|
+
- Improves testability (mock dependencies)
|
|
104
|
+
- Supports plugin architectures
|
|
105
|
+
|
|
106
|
+
**Implementation**
|
|
107
|
+
- Define interfaces for dependencies
|
|
108
|
+
- Inject dependencies through constructors or setters
|
|
109
|
+
- Use dependency injection containers
|
|
110
|
+
- Program to interfaces, not implementations
|
|
111
|
+
|
|
112
|
+
### DRY Principle (Don't Repeat Yourself)
|
|
113
|
+
|
|
114
|
+
**Definition**
|
|
115
|
+
- Every piece of knowledge should have a single, unambiguous representation
|
|
116
|
+
- Avoid code duplication
|
|
117
|
+
- Extract common functionality
|
|
118
|
+
- Applies to code, data, and documentation
|
|
119
|
+
|
|
120
|
+
**Benefits**
|
|
121
|
+
- Reduces maintenance burden
|
|
122
|
+
- Ensures consistency
|
|
123
|
+
- Simplifies changes (single point of modification)
|
|
124
|
+
- Reduces bugs from inconsistent copies
|
|
125
|
+
|
|
126
|
+
**Application**
|
|
127
|
+
- Extract methods/functions
|
|
128
|
+
- Create reusable libraries
|
|
129
|
+
- Use inheritance or composition
|
|
130
|
+
- Apply templates and code generation
|
|
131
|
+
|
|
132
|
+
**Caution**
|
|
133
|
+
- Don't over-abstract (premature abstraction)
|
|
134
|
+
- Consider context (similar code may have different reasons to change)
|
|
135
|
+
- Balance DRY with readability
|
|
136
|
+
|
|
137
|
+
### KISS Principle (Keep It Simple, Stupid)
|
|
138
|
+
|
|
139
|
+
**Definition**
|
|
140
|
+
- Simplicity should be a key goal in design
|
|
141
|
+
- Avoid unnecessary complexity
|
|
142
|
+
- Choose the simplest solution that works
|
|
143
|
+
- Complexity should be justified by requirements
|
|
144
|
+
|
|
145
|
+
**Benefits**
|
|
146
|
+
- Easier to understand and maintain
|
|
147
|
+
- Fewer bugs (less code, less complexity)
|
|
148
|
+
- Faster development
|
|
149
|
+
- Better performance (simpler code often runs faster)
|
|
150
|
+
|
|
151
|
+
**Guidelines**
|
|
152
|
+
- Avoid over-engineering
|
|
153
|
+
- Use clear, descriptive names
|
|
154
|
+
- Prefer composition over complex inheritance
|
|
155
|
+
- Avoid premature optimization
|
|
156
|
+
- Write code for humans first
|
|
157
|
+
|
|
158
|
+
**Violations**
|
|
159
|
+
- Unnecessary design patterns
|
|
160
|
+
- Over-abstraction
|
|
161
|
+
- Complex algorithms when simple ones suffice
|
|
162
|
+
- Feature creep
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Skills
|
|
167
|
+
|
|
168
|
+
### Applying SOLID Principles
|
|
169
|
+
|
|
170
|
+
**When to Apply SRP**
|
|
171
|
+
- During class design
|
|
172
|
+
- When a class has multiple reasons to change
|
|
173
|
+
- When refactoring large classes
|
|
174
|
+
- When writing new features
|
|
175
|
+
|
|
176
|
+
**When to Apply OCP**
|
|
177
|
+
- Designing plugin systems
|
|
178
|
+
- Creating extensible frameworks
|
|
179
|
+
- When anticipating future changes
|
|
180
|
+
- Building versioned APIs
|
|
181
|
+
|
|
182
|
+
**When to Apply LSP**
|
|
183
|
+
- Designing inheritance hierarchies
|
|
184
|
+
- Creating polymorphic interfaces
|
|
185
|
+
- Refactoring type systems
|
|
186
|
+
- Implementing design patterns
|
|
187
|
+
|
|
188
|
+
**When to Apply ISP**
|
|
189
|
+
- Designing public APIs
|
|
190
|
+
- Creating service interfaces
|
|
191
|
+
- Refactoring fat interfaces
|
|
192
|
+
- Building modular systems
|
|
193
|
+
|
|
194
|
+
**When to Apply DIP**
|
|
195
|
+
- Designing layered architectures
|
|
196
|
+
- Setting up dependency injection
|
|
197
|
+
- Creating testable code
|
|
198
|
+
- Building plugin architectures
|
|
199
|
+
|
|
200
|
+
### Balancing DRY and KISS
|
|
201
|
+
|
|
202
|
+
**Finding the Balance**
|
|
203
|
+
- Don't abstract too early (wait for third occurrence)
|
|
204
|
+
- Consider the cost of abstraction vs. duplication
|
|
205
|
+
- Evaluate readability impact
|
|
206
|
+
- Assess maintenance implications
|
|
207
|
+
|
|
208
|
+
**Red Flags**
|
|
209
|
+
- Abstractions with only one implementation
|
|
210
|
+
- Complex abstractions for simple problems
|
|
211
|
+
- Premature optimization
|
|
212
|
+
- Over-engineered solutions
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Examples
|
|
217
|
+
|
|
218
|
+
### SOLID Principles in TypeScript
|
|
219
|
+
|
|
220
|
+
#### Single Responsibility Principle
|
|
221
|
+
|
|
222
|
+
**Violation:**
|
|
223
|
+
```typescript
|
|
224
|
+
class UserManager {
|
|
225
|
+
createUser(data: UserData): User { /* ... */ }
|
|
226
|
+
deleteUser(id: string): void { /* ... */ }
|
|
227
|
+
sendWelcomeEmail(user: User): void { /* ... */ }
|
|
228
|
+
generateUserReport(user: User): Report { /* ... */ }
|
|
229
|
+
logUserActivity(user: User, action: string): void { /* ... */ }
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
**Correct:**
|
|
234
|
+
```typescript
|
|
235
|
+
class UserRepository {
|
|
236
|
+
create(data: UserData): User { /* ... */ }
|
|
237
|
+
delete(id: string): void { /* ... */ }
|
|
238
|
+
findById(id: string): User | null { /* ... */ }
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
class EmailService {
|
|
242
|
+
sendWelcomeEmail(user: User): void { /* ... */ }
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
class ReportGenerator {
|
|
246
|
+
generateUserReport(user: User): Report { /* ... */ }
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
class ActivityLogger {
|
|
250
|
+
logUserActivity(user: User, action: string): void { /* ... */ }
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
#### Open/Closed Principle
|
|
255
|
+
|
|
256
|
+
**Violation:**
|
|
257
|
+
```typescript
|
|
258
|
+
class PaymentProcessor {
|
|
259
|
+
processPayment(type: string, amount: number): void {
|
|
260
|
+
if (type === 'credit-card') {
|
|
261
|
+
// Process credit card
|
|
262
|
+
} else if (type === 'paypal') {
|
|
263
|
+
// Process PayPal
|
|
264
|
+
} else if (type === 'bitcoin') {
|
|
265
|
+
// Process Bitcoin
|
|
266
|
+
}
|
|
267
|
+
// Adding new payment method requires modifying this class
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
**Correct:**
|
|
273
|
+
```typescript
|
|
274
|
+
interface PaymentMethod {
|
|
275
|
+
process(amount: number): Promise<PaymentResult>;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
class CreditCardPayment implements PaymentMethod {
|
|
279
|
+
process(amount: number): Promise<PaymentResult> {
|
|
280
|
+
// Process credit card
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
class PayPalPayment implements PaymentMethod {
|
|
285
|
+
process(amount: number): Promise<PaymentResult> {
|
|
286
|
+
// Process PayPal
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
class PaymentProcessor {
|
|
291
|
+
constructor(private paymentMethod: PaymentMethod) {}
|
|
292
|
+
|
|
293
|
+
processPayment(amount: number): Promise<PaymentResult> {
|
|
294
|
+
return this.paymentMethod.process(amount);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
#### Liskov Substitution Principle
|
|
300
|
+
|
|
301
|
+
**Violation:**
|
|
302
|
+
```typescript
|
|
303
|
+
class Rectangle {
|
|
304
|
+
constructor(protected width: number, protected height: number) {}
|
|
305
|
+
|
|
306
|
+
setWidth(width: number): void {
|
|
307
|
+
this.width = width;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
setHeight(height: number): void {
|
|
311
|
+
this.height = height;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
getArea(): number {
|
|
315
|
+
return this.width * this.height;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
class Square extends Rectangle {
|
|
320
|
+
setWidth(width: number): void {
|
|
321
|
+
this.width = width;
|
|
322
|
+
this.height = width; // Violates LSP - unexpected behavior
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
setHeight(height: number): void {
|
|
326
|
+
this.width = height;
|
|
327
|
+
this.height = height; // Violates LSP - unexpected behavior
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
**Correct:**
|
|
333
|
+
```typescript
|
|
334
|
+
interface Shape {
|
|
335
|
+
getArea(): number;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
class Rectangle implements Shape {
|
|
339
|
+
constructor(private width: number, private height: number) {}
|
|
340
|
+
|
|
341
|
+
setWidth(width: number): void {
|
|
342
|
+
this.width = width;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
setHeight(height: number): void {
|
|
346
|
+
this.height = height;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
getArea(): number {
|
|
350
|
+
return this.width * this.height;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
class Square implements Shape {
|
|
355
|
+
constructor(private size: number) {}
|
|
356
|
+
|
|
357
|
+
setSize(size: number): void {
|
|
358
|
+
this.size = size;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
getArea(): number {
|
|
362
|
+
return this.size * this.size;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
#### Interface Segregation Principle
|
|
368
|
+
|
|
369
|
+
**Violation:**
|
|
370
|
+
```typescript
|
|
371
|
+
interface Worker {
|
|
372
|
+
work(): void;
|
|
373
|
+
eat(): void;
|
|
374
|
+
sleep(): void;
|
|
375
|
+
charge(): void; // Not all workers need this
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
class HumanWorker implements Worker {
|
|
379
|
+
work(): void { /* ... */ }
|
|
380
|
+
eat(): void { /* ... */ }
|
|
381
|
+
sleep(): void { /* ... */ }
|
|
382
|
+
charge(): void {
|
|
383
|
+
throw new Error('Humans cannot be charged'); // Forced to implement
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
class RobotWorker implements Worker {
|
|
388
|
+
work(): void { /* ... */ }
|
|
389
|
+
eat(): void {
|
|
390
|
+
throw new Error('Robots do not eat'); // Forced to implement
|
|
391
|
+
}
|
|
392
|
+
sleep(): void {
|
|
393
|
+
throw new Error('Robots do not sleep'); // Forced to implement
|
|
394
|
+
}
|
|
395
|
+
charge(): void { /* ... */ }
|
|
396
|
+
}
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
**Correct:**
|
|
400
|
+
```typescript
|
|
401
|
+
interface Workable {
|
|
402
|
+
work(): void;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
interface Eatable {
|
|
406
|
+
eat(): void;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
interface Sleepable {
|
|
410
|
+
sleep(): void;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
interface Chargeable {
|
|
414
|
+
charge(): void;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
class HumanWorker implements Workable, Eatable, Sleepable {
|
|
418
|
+
work(): void { /* ... */ }
|
|
419
|
+
eat(): void { /* ... */ }
|
|
420
|
+
sleep(): void { /* ... */ }
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
class RobotWorker implements Workable, Chargeable {
|
|
424
|
+
work(): void { /* ... */ }
|
|
425
|
+
charge(): void { /* ... */ }
|
|
426
|
+
}
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
#### Dependency Inversion Principle
|
|
430
|
+
|
|
431
|
+
**Violation:**
|
|
432
|
+
```typescript
|
|
433
|
+
class MySQLDatabase {
|
|
434
|
+
connect(): void { /* ... */ }
|
|
435
|
+
query(sql: string): any[] { /* ... */ }
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
class UserService {
|
|
439
|
+
private db: MySQLDatabase; // Depends on concrete implementation
|
|
440
|
+
|
|
441
|
+
constructor() {
|
|
442
|
+
this.db = new MySQLDatabase(); // Tight coupling
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
getUser(id: string): User {
|
|
446
|
+
return this.db.query(`SELECT * FROM users WHERE id = ${id}`)[0];
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
**Correct:**
|
|
452
|
+
```typescript
|
|
453
|
+
interface Database {
|
|
454
|
+
connect(): void;
|
|
455
|
+
query(sql: string): any[];
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
class MySQLDatabase implements Database {
|
|
459
|
+
connect(): void { /* ... */ }
|
|
460
|
+
query(sql: string): any[] { /* ... */ }
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
class PostgreSQLDatabase implements Database {
|
|
464
|
+
connect(): void { /* ... */ }
|
|
465
|
+
query(sql: string): any[] { /* ... */ }
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
class UserService {
|
|
469
|
+
constructor(private db: Database) {} // Depends on abstraction
|
|
470
|
+
|
|
471
|
+
getUser(id: string): User {
|
|
472
|
+
return this.db.query(`SELECT * FROM users WHERE id = ${id}`)[0];
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// Usage
|
|
477
|
+
const mysqlDb = new MySQLDatabase();
|
|
478
|
+
const userService = new UserService(mysqlDb); // Inject dependency
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
### DRY Principle
|
|
482
|
+
|
|
483
|
+
**Violation:**
|
|
484
|
+
```typescript
|
|
485
|
+
function calculateOrderTotal(order: Order): number {
|
|
486
|
+
let total = 0;
|
|
487
|
+
for (const item of order.items) {
|
|
488
|
+
total += item.price * item.quantity;
|
|
489
|
+
}
|
|
490
|
+
const tax = total * 0.08;
|
|
491
|
+
const shipping = total > 100 ? 0 : 10;
|
|
492
|
+
return total + tax + shipping;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
function calculateInvoiceTotal(invoice: Invoice): number {
|
|
496
|
+
let total = 0;
|
|
497
|
+
for (const item of invoice.items) {
|
|
498
|
+
total += item.price * item.quantity;
|
|
499
|
+
}
|
|
500
|
+
const tax = total * 0.08;
|
|
501
|
+
const shipping = total > 100 ? 0 : 10;
|
|
502
|
+
return total + tax + shipping;
|
|
503
|
+
}
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
**Correct:**
|
|
507
|
+
```typescript
|
|
508
|
+
interface LineItem {
|
|
509
|
+
price: number;
|
|
510
|
+
quantity: number;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
function calculateSubtotal(items: LineItem[]): number {
|
|
514
|
+
return items.reduce((sum, item) => sum + item.price * item.quantity, 0);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
function calculateTax(subtotal: number, rate: number = 0.08): number {
|
|
518
|
+
return subtotal * rate;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
function calculateShipping(subtotal: number, freeThreshold: number = 100): number {
|
|
522
|
+
return subtotal > freeThreshold ? 0 : 10;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
function calculateTotal(items: LineItem[]): number {
|
|
526
|
+
const subtotal = calculateSubtotal(items);
|
|
527
|
+
const tax = calculateTax(subtotal);
|
|
528
|
+
const shipping = calculateShipping(subtotal);
|
|
529
|
+
return subtotal + tax + shipping;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// Usage
|
|
533
|
+
const orderTotal = calculateTotal(order.items);
|
|
534
|
+
const invoiceTotal = calculateTotal(invoice.items);
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
### KISS Principle
|
|
538
|
+
|
|
539
|
+
**Violation (Over-engineered):**
|
|
540
|
+
```typescript
|
|
541
|
+
interface Strategy {
|
|
542
|
+
execute(value: number): number;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
class AdditionStrategy implements Strategy {
|
|
546
|
+
execute(value: number): number {
|
|
547
|
+
return value + 1;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
class SubtractionStrategy implements Strategy {
|
|
552
|
+
execute(value: number): number {
|
|
553
|
+
return value - 1;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
class StrategyFactory {
|
|
558
|
+
createStrategy(type: string): Strategy {
|
|
559
|
+
switch (type) {
|
|
560
|
+
case 'add': return new AdditionStrategy();
|
|
561
|
+
case 'subtract': return new SubtractionStrategy();
|
|
562
|
+
default: throw new Error('Unknown strategy');
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
class Counter {
|
|
568
|
+
constructor(
|
|
569
|
+
private value: number,
|
|
570
|
+
private strategyFactory: StrategyFactory
|
|
571
|
+
) {}
|
|
572
|
+
|
|
573
|
+
increment(): void {
|
|
574
|
+
const strategy = this.strategyFactory.createStrategy('add');
|
|
575
|
+
this.value = strategy.execute(this.value);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
decrement(): void {
|
|
579
|
+
const strategy = this.strategyFactory.createStrategy('subtract');
|
|
580
|
+
this.value = strategy.execute(this.value);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
**Correct (Simple):**
|
|
586
|
+
```typescript
|
|
587
|
+
class Counter {
|
|
588
|
+
constructor(private value: number = 0) {}
|
|
589
|
+
|
|
590
|
+
increment(): void {
|
|
591
|
+
this.value++;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
decrement(): void {
|
|
595
|
+
this.value--;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
getValue(): number {
|
|
599
|
+
return this.value;
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
---
|
|
605
|
+
|
|
606
|
+
## Understanding
|
|
607
|
+
|
|
608
|
+
### When to Apply Each Principle
|
|
609
|
+
|
|
610
|
+
**SOLID Principles**
|
|
611
|
+
- Use during initial design and refactoring
|
|
612
|
+
- Apply when code becomes hard to maintain
|
|
613
|
+
- Essential for large, long-lived systems
|
|
614
|
+
- Critical for team collaboration
|
|
615
|
+
|
|
616
|
+
**DRY Principle**
|
|
617
|
+
- Apply after identifying true duplication
|
|
618
|
+
- Wait for third occurrence (Rule of Three)
|
|
619
|
+
- Consider context and reasons for change
|
|
620
|
+
- Balance with readability
|
|
621
|
+
|
|
622
|
+
**KISS Principle**
|
|
623
|
+
- Apply always, especially at start
|
|
624
|
+
- Question every abstraction
|
|
625
|
+
- Prefer simple solutions
|
|
626
|
+
- Add complexity only when justified
|
|
627
|
+
|
|
628
|
+
### Common Pitfalls
|
|
629
|
+
|
|
630
|
+
**Over-applying SOLID**
|
|
631
|
+
- Too many small classes (class explosion)
|
|
632
|
+
- Over-abstraction (unnecessary interfaces)
|
|
633
|
+
- Premature optimization
|
|
634
|
+
- Reduced readability
|
|
635
|
+
|
|
636
|
+
**Over-applying DRY**
|
|
637
|
+
- Premature abstraction
|
|
638
|
+
- Coupling unrelated code
|
|
639
|
+
- Complex abstractions for simple duplication
|
|
640
|
+
- Reduced clarity
|
|
641
|
+
|
|
642
|
+
**Ignoring KISS**
|
|
643
|
+
- Over-engineering simple problems
|
|
644
|
+
- Unnecessary design patterns
|
|
645
|
+
- Complex frameworks for simple tasks
|
|
646
|
+
- Feature creep
|
|
647
|
+
|
|
648
|
+
### Best Practices
|
|
649
|
+
|
|
650
|
+
1. **Start Simple**: Begin with KISS, add complexity as needed
|
|
651
|
+
2. **Refactor Incrementally**: Apply SOLID during refactoring, not upfront
|
|
652
|
+
3. **Rule of Three**: Wait for third duplication before applying DRY
|
|
653
|
+
4. **Context Matters**: Consider team size, project lifetime, requirements
|
|
654
|
+
5. **Balance**: Trade-offs between principles are normal
|
|
655
|
+
6. **Readability First**: Code is read more than written
|
|
656
|
+
7. **Test-Driven**: Principles emerge naturally with TDD
|
|
657
|
+
8. **Review Regularly**: Principles guide code reviews
|
|
658
|
+
|
|
659
|
+
### Measuring Success
|
|
660
|
+
|
|
661
|
+
**Good Signs**
|
|
662
|
+
- Easy to add new features
|
|
663
|
+
- Easy to test
|
|
664
|
+
- Easy to understand
|
|
665
|
+
- Few bugs from changes
|
|
666
|
+
- Fast development velocity
|
|
667
|
+
|
|
668
|
+
**Warning Signs**
|
|
669
|
+
- Frequent breaking changes
|
|
670
|
+
- Difficult to test
|
|
671
|
+
- Hard to understand
|
|
672
|
+
- Many bugs from changes
|
|
673
|
+
- Slow development velocity
|
|
674
|
+
|
|
675
|
+
---
|
|
676
|
+
|
|
677
|
+
## References
|
|
678
|
+
|
|
679
|
+
- **SOLID Principles**: Robert C. Martin (Uncle Bob)
|
|
680
|
+
- **Design Patterns**: Gang of Four (GoF)
|
|
681
|
+
- **Clean Code**: Robert C. Martin
|
|
682
|
+
- **Refactoring**: Martin Fowler
|
|
683
|
+
- **The Pragmatic Programmer**: Andrew Hunt, David Thomas
|
|
684
|
+
|