@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
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# Modular Monolith Boundaries
|
|
2
|
+
|
|
3
|
+
## High Cohesion, Low Coupling
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
// ❌ Bad: Tight coupling - direct repository access
|
|
7
|
+
@Injectable()
|
|
8
|
+
export class OrderService {
|
|
9
|
+
constructor(private userRepo: UserRepository) {} // Crosses module boundary
|
|
10
|
+
|
|
11
|
+
async createOrder(userId: string) {
|
|
12
|
+
const user = await this.userRepo.findById(userId); // Direct access
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// ✅ Good: Loose coupling via service
|
|
17
|
+
@Injectable()
|
|
18
|
+
export class OrderService {
|
|
19
|
+
constructor(private userService: UserService) {} // Service dependency
|
|
20
|
+
|
|
21
|
+
async createOrder(userId: string) {
|
|
22
|
+
const user = await this.userService.findById(userId); // Through public API
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## No Direct Cross-Module Database Access
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
// ❌ Never query another module's tables directly
|
|
31
|
+
class BookingService {
|
|
32
|
+
async createBooking(data: CreateBookingDto) {
|
|
33
|
+
const user = await this.prisma.user.findUnique({ where: { id: data.userId } });
|
|
34
|
+
// This bypasses the User module!
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// ✅ Use the module's public service API
|
|
39
|
+
class BookingService {
|
|
40
|
+
constructor(private userService: UserService) {}
|
|
41
|
+
|
|
42
|
+
async createBooking(data: CreateBookingDto) {
|
|
43
|
+
const user = await this.userService.findById(data.userId);
|
|
44
|
+
// Properly goes through User module
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Separated Interface Pattern
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
// Define interface in consuming module
|
|
53
|
+
// modules/order/interfaces/user-provider.interface.ts
|
|
54
|
+
export interface UserProvider {
|
|
55
|
+
findById(id: string): Promise<User>;
|
|
56
|
+
validateUser(id: string): Promise<boolean>;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Implement in providing module
|
|
60
|
+
// modules/user/user.service.ts
|
|
61
|
+
@Injectable()
|
|
62
|
+
export class UserService implements UserProvider {
|
|
63
|
+
async findById(id: string): Promise<User> {
|
|
64
|
+
return this.userRepo.findById(id);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async validateUser(id: string): Promise<boolean> {
|
|
68
|
+
const user = await this.findById(id);
|
|
69
|
+
return user && user.isActive;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Domain Events for Loose Coupling
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
// ✅ Publish events instead of direct calls
|
|
78
|
+
@Injectable()
|
|
79
|
+
export class UserService {
|
|
80
|
+
constructor(private eventEmitter: EventEmitter2) {}
|
|
81
|
+
|
|
82
|
+
async createUser(dto: CreateUserDto): Promise<User> {
|
|
83
|
+
const user = await this.userRepo.create(dto);
|
|
84
|
+
|
|
85
|
+
this.eventEmitter.emit('user.created', new UserCreatedEvent(user.id, user.email));
|
|
86
|
+
|
|
87
|
+
return user;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Other modules subscribe to events
|
|
92
|
+
@Injectable()
|
|
93
|
+
export class NotificationListener {
|
|
94
|
+
@OnEvent('user.created')
|
|
95
|
+
async handleUserCreated(event: UserCreatedEvent) {
|
|
96
|
+
await this.notificationService.sendWelcomeEmail(event.email);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Handling Circular Dependencies
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
// Use forwardRef() when modules depend on each other
|
|
105
|
+
@Module({
|
|
106
|
+
imports: [
|
|
107
|
+
forwardRef(() => AuthModule), // Break circular dependency
|
|
108
|
+
UserModule,
|
|
109
|
+
],
|
|
110
|
+
})
|
|
111
|
+
export class UserModule {}
|
|
112
|
+
|
|
113
|
+
@Module({
|
|
114
|
+
imports: [
|
|
115
|
+
forwardRef(() => UserModule),
|
|
116
|
+
],
|
|
117
|
+
})
|
|
118
|
+
export class AuthModule {}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Export Only What's Necessary
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
@Module({
|
|
125
|
+
providers: [
|
|
126
|
+
UserService, // Public service
|
|
127
|
+
UserRepository, // Internal
|
|
128
|
+
PasswordHasher, // Internal
|
|
129
|
+
],
|
|
130
|
+
exports: [UserService], // Only export the service, not internals
|
|
131
|
+
})
|
|
132
|
+
export class UserModule {}
|
|
133
|
+
```
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Modular Monolith Structure
|
|
2
|
+
|
|
3
|
+
## Project Organization
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
project-root/
|
|
7
|
+
├── apps/
|
|
8
|
+
│ └── api/
|
|
9
|
+
│ ├── src/
|
|
10
|
+
│ │ ├── app/ # Application bootstrap
|
|
11
|
+
│ │ ├── modules/ # Business modules
|
|
12
|
+
│ │ │ ├── auth/
|
|
13
|
+
│ │ │ ├── user/
|
|
14
|
+
│ │ │ ├── booking/
|
|
15
|
+
│ │ │ ├── payment/
|
|
16
|
+
│ │ │ └── notification/
|
|
17
|
+
│ │ ├── common/ # Shared infrastructure
|
|
18
|
+
│ │ │ ├── decorators/
|
|
19
|
+
│ │ │ ├── guards/
|
|
20
|
+
│ │ │ └── interceptors/
|
|
21
|
+
│ │ └── prisma/ # Database service
|
|
22
|
+
│ └── main.ts
|
|
23
|
+
├── libs/ # Shared libraries
|
|
24
|
+
│ └── shared-types/
|
|
25
|
+
└── package.json
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Module Structure
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
modules/booking/
|
|
32
|
+
├── entities/ # Domain models and DTOs
|
|
33
|
+
│ ├── booking.entity.ts
|
|
34
|
+
│ ├── create-booking.dto.ts
|
|
35
|
+
│ └── booking-response.dto.ts
|
|
36
|
+
├── repositories/ # Data access layer
|
|
37
|
+
│ └── booking.repository.ts
|
|
38
|
+
├── services/ # Business logic
|
|
39
|
+
│ ├── booking.service.ts
|
|
40
|
+
│ └── availability.service.ts
|
|
41
|
+
├── controllers/ # HTTP/API layer
|
|
42
|
+
│ └── bookings.controller.ts
|
|
43
|
+
└── booking.module.ts # Module definition
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Module Definition
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
@Module({
|
|
50
|
+
imports: [
|
|
51
|
+
PrismaModule,
|
|
52
|
+
forwardRef(() => AuthModule),
|
|
53
|
+
NotificationsModule,
|
|
54
|
+
],
|
|
55
|
+
controllers: [BookingsController],
|
|
56
|
+
providers: [
|
|
57
|
+
BookingService,
|
|
58
|
+
AvailabilityService,
|
|
59
|
+
BookingRepository,
|
|
60
|
+
],
|
|
61
|
+
exports: [BookingService], // Only export public API
|
|
62
|
+
})
|
|
63
|
+
export class BookingsModule {}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Layered Architecture Within Modules
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
// Controller - HTTP layer
|
|
70
|
+
@Controller('api/v1/bookings')
|
|
71
|
+
export class BookingsController {
|
|
72
|
+
constructor(private bookingService: BookingService) {}
|
|
73
|
+
|
|
74
|
+
@Get('calendar')
|
|
75
|
+
async getCalendarBookings(@Query() dto: GetBookingsDto) {
|
|
76
|
+
return this.bookingService.getBookingsForCalendar(dto);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Service - Business logic
|
|
81
|
+
@Injectable()
|
|
82
|
+
export class BookingService {
|
|
83
|
+
constructor(
|
|
84
|
+
private bookingRepository: BookingRepository,
|
|
85
|
+
private availabilityService: AvailabilityService,
|
|
86
|
+
) {}
|
|
87
|
+
|
|
88
|
+
async getBookingsForCalendar(dto: GetBookingsDto) {
|
|
89
|
+
const bookings = await this.bookingRepository.findByDateRange(
|
|
90
|
+
dto.startDate,
|
|
91
|
+
dto.endDate
|
|
92
|
+
);
|
|
93
|
+
return bookings.map(this.mapToCalendarDto);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Repository - Data access
|
|
98
|
+
@Injectable()
|
|
99
|
+
export class BookingRepository {
|
|
100
|
+
constructor(private prisma: PrismaService) {}
|
|
101
|
+
|
|
102
|
+
async findByDateRange(start: Date, end: Date) {
|
|
103
|
+
return this.prisma.booking.findMany({
|
|
104
|
+
where: {
|
|
105
|
+
startTime: { gte: start },
|
|
106
|
+
endTime: { lte: end }
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Shared Infrastructure
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
// common/guards/jwt-auth.guard.ts
|
|
117
|
+
@Injectable()
|
|
118
|
+
export class JwtAuthGuard extends AuthGuard('jwt') {
|
|
119
|
+
canActivate(context: ExecutionContext) {
|
|
120
|
+
const isPublic = this.reflector.get<boolean>('isPublic', context.getHandler());
|
|
121
|
+
return isPublic ? true : super.canActivate(context);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// common/decorators/current-user.decorator.ts
|
|
126
|
+
export const CurrentUser = createParamDecorator(
|
|
127
|
+
(data: unknown, ctx: ExecutionContext) => {
|
|
128
|
+
return ctx.switchToHttp().getRequest().user;
|
|
129
|
+
}
|
|
130
|
+
);
|
|
131
|
+
```
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
# Serverless Best Practices
|
|
2
|
+
|
|
3
|
+
## Function Design
|
|
4
|
+
|
|
5
|
+
### Single Responsibility
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
// ❌ Bad: Multiple responsibilities
|
|
9
|
+
export const handler = async (event: APIGatewayEvent) => {
|
|
10
|
+
if (event.path === '/users') {
|
|
11
|
+
// Handle users
|
|
12
|
+
} else if (event.path === '/orders') {
|
|
13
|
+
// Handle orders
|
|
14
|
+
} else if (event.path === '/products') {
|
|
15
|
+
// Handle products
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// ✅ Good: One function per responsibility
|
|
20
|
+
// createUser.ts
|
|
21
|
+
export const handler = async (event: APIGatewayEvent) => {
|
|
22
|
+
const userData = JSON.parse(event.body);
|
|
23
|
+
const user = await userService.create(userData);
|
|
24
|
+
return { statusCode: 201, body: JSON.stringify(user) };
|
|
25
|
+
};
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Keep Functions Small
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
// ✅ Good: Small, focused function
|
|
32
|
+
export const handler = async (event: SNSEvent) => {
|
|
33
|
+
for (const record of event.Records) {
|
|
34
|
+
const message = JSON.parse(record.Sns.Message);
|
|
35
|
+
await processMessage(message);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// Extract business logic to separate module
|
|
40
|
+
async function processMessage(message: OrderMessage): Promise<void> {
|
|
41
|
+
const order = await orderService.process(message);
|
|
42
|
+
await notificationService.sendConfirmation(order);
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Cold Start Optimization
|
|
47
|
+
|
|
48
|
+
### Minimize Dependencies
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
// ❌ Bad: Heavy imports at top level
|
|
52
|
+
import * as AWS from 'aws-sdk';
|
|
53
|
+
import moment from 'moment';
|
|
54
|
+
import _ from 'lodash';
|
|
55
|
+
|
|
56
|
+
// ✅ Good: Import only what you need
|
|
57
|
+
import { DynamoDB } from '@aws-sdk/client-dynamodb';
|
|
58
|
+
|
|
59
|
+
// ✅ Good: Lazy load optional dependencies
|
|
60
|
+
let heavyLib: typeof import('heavy-lib') | undefined;
|
|
61
|
+
|
|
62
|
+
async function useHeavyFeature() {
|
|
63
|
+
if (!heavyLib) {
|
|
64
|
+
heavyLib = await import('heavy-lib');
|
|
65
|
+
}
|
|
66
|
+
return heavyLib.process();
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Initialize Outside Handler
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
// ✅ Good: Reuse connections across invocations
|
|
74
|
+
import { DynamoDB } from '@aws-sdk/client-dynamodb';
|
|
75
|
+
|
|
76
|
+
// Created once, reused
|
|
77
|
+
const dynamodb = new DynamoDB({});
|
|
78
|
+
let cachedConnection: Connection | undefined;
|
|
79
|
+
|
|
80
|
+
export const handler = async (event: Event) => {
|
|
81
|
+
// Reuse existing connection
|
|
82
|
+
if (!cachedConnection) {
|
|
83
|
+
cachedConnection = await createConnection();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return process(event, cachedConnection);
|
|
87
|
+
};
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Provisioned Concurrency
|
|
91
|
+
|
|
92
|
+
```yaml
|
|
93
|
+
# serverless.yml
|
|
94
|
+
functions:
|
|
95
|
+
api:
|
|
96
|
+
handler: handler.api
|
|
97
|
+
provisionedConcurrency: 5 # Keep 5 instances warm
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Error Handling
|
|
101
|
+
|
|
102
|
+
### Structured Error Responses
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
class LambdaError extends Error {
|
|
106
|
+
constructor(
|
|
107
|
+
message: string,
|
|
108
|
+
public statusCode: number,
|
|
109
|
+
public code: string
|
|
110
|
+
) {
|
|
111
|
+
super(message);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export const handler = async (event: APIGatewayEvent) => {
|
|
116
|
+
try {
|
|
117
|
+
const result = await processRequest(event);
|
|
118
|
+
return {
|
|
119
|
+
statusCode: 200,
|
|
120
|
+
body: JSON.stringify(result)
|
|
121
|
+
};
|
|
122
|
+
} catch (error) {
|
|
123
|
+
if (error instanceof LambdaError) {
|
|
124
|
+
return {
|
|
125
|
+
statusCode: error.statusCode,
|
|
126
|
+
body: JSON.stringify({
|
|
127
|
+
error: { code: error.code, message: error.message }
|
|
128
|
+
})
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
console.error('Unexpected error:', error);
|
|
133
|
+
return {
|
|
134
|
+
statusCode: 500,
|
|
135
|
+
body: JSON.stringify({
|
|
136
|
+
error: { code: 'INTERNAL_ERROR', message: 'Internal server error' }
|
|
137
|
+
})
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Retry and Dead Letter Queues
|
|
144
|
+
|
|
145
|
+
```yaml
|
|
146
|
+
# CloudFormation
|
|
147
|
+
Resources:
|
|
148
|
+
MyFunction:
|
|
149
|
+
Type: AWS::Lambda::Function
|
|
150
|
+
Properties:
|
|
151
|
+
DeadLetterConfig:
|
|
152
|
+
TargetArn: !GetAtt DeadLetterQueue.Arn
|
|
153
|
+
|
|
154
|
+
DeadLetterQueue:
|
|
155
|
+
Type: AWS::SQS::Queue
|
|
156
|
+
Properties:
|
|
157
|
+
QueueName: my-function-dlq
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## State Management
|
|
161
|
+
|
|
162
|
+
### Use External State Stores
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
// ❌ Bad: In-memory state (lost between invocations)
|
|
166
|
+
let requestCount = 0;
|
|
167
|
+
|
|
168
|
+
export const handler = async () => {
|
|
169
|
+
requestCount++; // Unreliable!
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
// ✅ Good: External state store
|
|
173
|
+
import { DynamoDB } from '@aws-sdk/client-dynamodb';
|
|
174
|
+
|
|
175
|
+
const dynamodb = new DynamoDB({});
|
|
176
|
+
|
|
177
|
+
export const handler = async (event: Event) => {
|
|
178
|
+
// Atomic counter in DynamoDB
|
|
179
|
+
await dynamodb.updateItem({
|
|
180
|
+
TableName: 'Counters',
|
|
181
|
+
Key: { id: { S: 'requests' } },
|
|
182
|
+
UpdateExpression: 'ADD #count :inc',
|
|
183
|
+
ExpressionAttributeNames: { '#count': 'count' },
|
|
184
|
+
ExpressionAttributeValues: { ':inc': { N: '1' } }
|
|
185
|
+
});
|
|
186
|
+
};
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Step Functions for Workflows
|
|
190
|
+
|
|
191
|
+
```yaml
|
|
192
|
+
# Step Functions state machine
|
|
193
|
+
StartAt: ValidateOrder
|
|
194
|
+
States:
|
|
195
|
+
ValidateOrder:
|
|
196
|
+
Type: Task
|
|
197
|
+
Resource: arn:aws:lambda:...:validateOrder
|
|
198
|
+
Next: ProcessPayment
|
|
199
|
+
|
|
200
|
+
ProcessPayment:
|
|
201
|
+
Type: Task
|
|
202
|
+
Resource: arn:aws:lambda:...:processPayment
|
|
203
|
+
Catch:
|
|
204
|
+
- ErrorEquals: [PaymentFailed]
|
|
205
|
+
Next: NotifyFailure
|
|
206
|
+
Next: FulfillOrder
|
|
207
|
+
|
|
208
|
+
FulfillOrder:
|
|
209
|
+
Type: Task
|
|
210
|
+
Resource: arn:aws:lambda:...:fulfillOrder
|
|
211
|
+
End: true
|
|
212
|
+
|
|
213
|
+
NotifyFailure:
|
|
214
|
+
Type: Task
|
|
215
|
+
Resource: arn:aws:lambda:...:notifyFailure
|
|
216
|
+
End: true
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Security
|
|
220
|
+
|
|
221
|
+
### Least Privilege IAM
|
|
222
|
+
|
|
223
|
+
```yaml
|
|
224
|
+
# serverless.yml
|
|
225
|
+
provider:
|
|
226
|
+
iam:
|
|
227
|
+
role:
|
|
228
|
+
statements:
|
|
229
|
+
# Only the permissions needed
|
|
230
|
+
- Effect: Allow
|
|
231
|
+
Action:
|
|
232
|
+
- dynamodb:GetItem
|
|
233
|
+
- dynamodb:PutItem
|
|
234
|
+
Resource: arn:aws:dynamodb:*:*:table/Users
|
|
235
|
+
|
|
236
|
+
- Effect: Allow
|
|
237
|
+
Action:
|
|
238
|
+
- s3:GetObject
|
|
239
|
+
Resource: arn:aws:s3:::my-bucket/*
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### Secrets Management
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
import { SecretsManager } from '@aws-sdk/client-secrets-manager';
|
|
246
|
+
|
|
247
|
+
const secretsManager = new SecretsManager({});
|
|
248
|
+
let cachedSecret: string | undefined;
|
|
249
|
+
|
|
250
|
+
async function getSecret(): Promise<string> {
|
|
251
|
+
if (!cachedSecret) {
|
|
252
|
+
const response = await secretsManager.getSecretValue({
|
|
253
|
+
SecretId: 'my-api-key'
|
|
254
|
+
});
|
|
255
|
+
cachedSecret = response.SecretString;
|
|
256
|
+
}
|
|
257
|
+
return cachedSecret!;
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## Monitoring and Observability
|
|
262
|
+
|
|
263
|
+
### Structured Logging
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
import { Logger } from '@aws-lambda-powertools/logger';
|
|
267
|
+
|
|
268
|
+
const logger = new Logger({
|
|
269
|
+
serviceName: 'order-service',
|
|
270
|
+
logLevel: 'INFO'
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
export const handler = async (event: Event, context: Context) => {
|
|
274
|
+
logger.addContext(context);
|
|
275
|
+
|
|
276
|
+
logger.info('Processing order', {
|
|
277
|
+
orderId: event.orderId,
|
|
278
|
+
customerId: event.customerId
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
try {
|
|
282
|
+
const result = await processOrder(event);
|
|
283
|
+
logger.info('Order processed', { orderId: event.orderId });
|
|
284
|
+
return result;
|
|
285
|
+
} catch (error) {
|
|
286
|
+
logger.error('Order processing failed', { error, event });
|
|
287
|
+
throw error;
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### Tracing
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
import { Tracer } from '@aws-lambda-powertools/tracer';
|
|
296
|
+
|
|
297
|
+
const tracer = new Tracer({ serviceName: 'order-service' });
|
|
298
|
+
|
|
299
|
+
export const handler = async (event: Event) => {
|
|
300
|
+
const segment = tracer.getSegment();
|
|
301
|
+
const subsegment = segment.addNewSubsegment('ProcessOrder');
|
|
302
|
+
|
|
303
|
+
try {
|
|
304
|
+
const result = await processOrder(event);
|
|
305
|
+
subsegment.close();
|
|
306
|
+
return result;
|
|
307
|
+
} catch (error) {
|
|
308
|
+
subsegment.addError(error);
|
|
309
|
+
subsegment.close();
|
|
310
|
+
throw error;
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
## Cost Optimization
|
|
316
|
+
|
|
317
|
+
- Set appropriate memory (more memory = faster CPU)
|
|
318
|
+
- Use ARM architecture when possible (cheaper)
|
|
319
|
+
- Batch operations to reduce invocations
|
|
320
|
+
- Use reserved concurrency to limit costs
|
|
321
|
+
- Monitor and alert on spending
|
|
322
|
+
- Clean up unused functions and versions
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Serverless Architecture
|
|
2
|
+
|
|
3
|
+
## Key Principles
|
|
4
|
+
|
|
5
|
+
- **Stateless functions**: Each invocation is independent
|
|
6
|
+
- **Event-driven**: Functions triggered by events
|
|
7
|
+
- **Auto-scaling**: Platform handles scaling
|
|
8
|
+
- **Pay-per-use**: Billed by execution
|
|
9
|
+
|
|
10
|
+
## Function Design
|
|
11
|
+
|
|
12
|
+
```typescript
|
|
13
|
+
// Handler pattern
|
|
14
|
+
export async function handler(
|
|
15
|
+
event: APIGatewayEvent,
|
|
16
|
+
context: Context
|
|
17
|
+
): Promise<APIGatewayProxyResult> {
|
|
18
|
+
try {
|
|
19
|
+
const body = JSON.parse(event.body || '{}');
|
|
20
|
+
const result = await processOrder(body);
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
statusCode: 200,
|
|
24
|
+
body: JSON.stringify(result)
|
|
25
|
+
};
|
|
26
|
+
} catch (error) {
|
|
27
|
+
return {
|
|
28
|
+
statusCode: 500,
|
|
29
|
+
body: JSON.stringify({ error: 'Internal error' })
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Cold Start Optimization
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
// Initialize outside handler (reused across invocations)
|
|
39
|
+
const dbPool = createPool(process.env.DATABASE_URL);
|
|
40
|
+
|
|
41
|
+
export async function handler(event: Event): Promise<Response> {
|
|
42
|
+
// Use cached connection
|
|
43
|
+
const result = await dbPool.query('SELECT * FROM orders');
|
|
44
|
+
return { statusCode: 200, body: JSON.stringify(result) };
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## State Management
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
// Use external state stores
|
|
52
|
+
class OrderService {
|
|
53
|
+
constructor(
|
|
54
|
+
private dynamodb: DynamoDB,
|
|
55
|
+
private redis: Redis
|
|
56
|
+
) {}
|
|
57
|
+
|
|
58
|
+
async getOrder(id: string): Promise<Order> {
|
|
59
|
+
// Check cache first
|
|
60
|
+
const cached = await this.redis.get(`order:${id}`);
|
|
61
|
+
if (cached) return JSON.parse(cached);
|
|
62
|
+
|
|
63
|
+
// Fall back to database
|
|
64
|
+
const result = await this.dynamodb.get({ Key: { id } });
|
|
65
|
+
await this.redis.set(`order:${id}`, JSON.stringify(result));
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Best Practices
|
|
72
|
+
|
|
73
|
+
- Keep functions small and focused
|
|
74
|
+
- Use environment variables for configuration
|
|
75
|
+
- Minimize dependencies to reduce cold start time
|
|
76
|
+
- Handle timeouts gracefully
|
|
77
|
+
- Use async/await for all I/O operations
|
|
78
|
+
- Implement idempotency for event handlers
|
|
79
|
+
- Log structured data for observability
|
|
80
|
+
- Set appropriate memory and timeout limits
|