@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,242 @@
|
|
|
1
|
+
# Event-Driven Messaging
|
|
2
|
+
|
|
3
|
+
## Message Types
|
|
4
|
+
|
|
5
|
+
### Commands
|
|
6
|
+
Request to perform an action. Directed to a single handler.
|
|
7
|
+
|
|
8
|
+
```typescript
|
|
9
|
+
interface CreateOrderCommand {
|
|
10
|
+
type: 'CreateOrder';
|
|
11
|
+
orderId: string;
|
|
12
|
+
customerId: string;
|
|
13
|
+
items: OrderItem[];
|
|
14
|
+
timestamp: Date;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Single handler processes the command
|
|
18
|
+
class CreateOrderHandler {
|
|
19
|
+
async handle(command: CreateOrderCommand): Promise<void> {
|
|
20
|
+
const order = Order.create(command);
|
|
21
|
+
await this.repository.save(order);
|
|
22
|
+
await this.eventBus.publish(new OrderCreatedEvent(order));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Events
|
|
28
|
+
Notification that something happened. Published to multiple subscribers.
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
interface OrderCreatedEvent {
|
|
32
|
+
type: 'OrderCreated';
|
|
33
|
+
orderId: string;
|
|
34
|
+
customerId: string;
|
|
35
|
+
totalAmount: number;
|
|
36
|
+
occurredAt: Date;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Multiple handlers can subscribe
|
|
40
|
+
class InventoryService {
|
|
41
|
+
@Subscribe('OrderCreated')
|
|
42
|
+
async onOrderCreated(event: OrderCreatedEvent): Promise<void> {
|
|
43
|
+
await this.reserveInventory(event.orderId);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
class NotificationService {
|
|
48
|
+
@Subscribe('OrderCreated')
|
|
49
|
+
async onOrderCreated(event: OrderCreatedEvent): Promise<void> {
|
|
50
|
+
await this.sendConfirmation(event.customerId);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Queries
|
|
56
|
+
Request for data. Returns a response.
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
interface GetOrderQuery {
|
|
60
|
+
type: 'GetOrder';
|
|
61
|
+
orderId: string;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
class GetOrderHandler {
|
|
65
|
+
async handle(query: GetOrderQuery): Promise<Order> {
|
|
66
|
+
return this.repository.findById(query.orderId);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Message Bus Patterns
|
|
72
|
+
|
|
73
|
+
### In-Memory Bus
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
class EventBus {
|
|
77
|
+
private handlers = new Map<string, Function[]>();
|
|
78
|
+
|
|
79
|
+
subscribe(eventType: string, handler: Function): void {
|
|
80
|
+
const handlers = this.handlers.get(eventType) || [];
|
|
81
|
+
handlers.push(handler);
|
|
82
|
+
this.handlers.set(eventType, handlers);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async publish(event: Event): Promise<void> {
|
|
86
|
+
const handlers = this.handlers.get(event.type) || [];
|
|
87
|
+
await Promise.all(handlers.map(h => h(event)));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Message Queue Integration
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
// RabbitMQ example
|
|
96
|
+
class RabbitMQPublisher {
|
|
97
|
+
async publish(event: Event): Promise<void> {
|
|
98
|
+
const message = JSON.stringify({
|
|
99
|
+
type: event.type,
|
|
100
|
+
data: event,
|
|
101
|
+
metadata: {
|
|
102
|
+
correlationId: uuid(),
|
|
103
|
+
timestamp: new Date().toISOString()
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
await this.channel.publish(
|
|
108
|
+
'events',
|
|
109
|
+
event.type,
|
|
110
|
+
Buffer.from(message),
|
|
111
|
+
{ persistent: true }
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
class RabbitMQConsumer {
|
|
117
|
+
async consume(queue: string, handler: EventHandler): Promise<void> {
|
|
118
|
+
await this.channel.consume(queue, async (msg) => {
|
|
119
|
+
if (!msg) return;
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
const event = JSON.parse(msg.content.toString());
|
|
123
|
+
await handler.handle(event);
|
|
124
|
+
this.channel.ack(msg);
|
|
125
|
+
} catch (error) {
|
|
126
|
+
this.channel.nack(msg, false, true); // Requeue
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Delivery Guarantees
|
|
134
|
+
|
|
135
|
+
### At-Least-Once Delivery
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
// Producer: persist before publish
|
|
139
|
+
async function publishWithRetry(event: Event): Promise<void> {
|
|
140
|
+
// 1. Save to outbox
|
|
141
|
+
await db.insert('outbox', {
|
|
142
|
+
id: event.id,
|
|
143
|
+
type: event.type,
|
|
144
|
+
payload: JSON.stringify(event),
|
|
145
|
+
status: 'pending'
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// 2. Publish (may fail)
|
|
149
|
+
try {
|
|
150
|
+
await messageBus.publish(event);
|
|
151
|
+
await db.update('outbox', event.id, { status: 'sent' });
|
|
152
|
+
} catch {
|
|
153
|
+
// Retry worker will pick it up
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Consumer: idempotent handling
|
|
158
|
+
async function handleIdempotent(event: Event): Promise<void> {
|
|
159
|
+
const processed = await db.findOne('processed_events', event.id);
|
|
160
|
+
if (processed) return; // Already handled
|
|
161
|
+
|
|
162
|
+
await handleEvent(event);
|
|
163
|
+
await db.insert('processed_events', { id: event.id });
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Outbox Pattern
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
// Transaction includes outbox write
|
|
171
|
+
async function createOrder(data: OrderData): Promise<Order> {
|
|
172
|
+
return await db.transaction(async (tx) => {
|
|
173
|
+
// 1. Business logic
|
|
174
|
+
const order = Order.create(data);
|
|
175
|
+
await tx.insert('orders', order);
|
|
176
|
+
|
|
177
|
+
// 2. Outbox entry (same transaction)
|
|
178
|
+
await tx.insert('outbox', {
|
|
179
|
+
id: uuid(),
|
|
180
|
+
aggregateId: order.id,
|
|
181
|
+
type: 'OrderCreated',
|
|
182
|
+
payload: JSON.stringify(order)
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
return order;
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Separate process polls and publishes
|
|
190
|
+
async function processOutbox(): Promise<void> {
|
|
191
|
+
const pending = await db.query(
|
|
192
|
+
'SELECT * FROM outbox WHERE status = $1 ORDER BY created_at LIMIT 100',
|
|
193
|
+
['pending']
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
for (const entry of pending) {
|
|
197
|
+
await messageBus.publish(JSON.parse(entry.payload));
|
|
198
|
+
await db.update('outbox', entry.id, { status: 'sent' });
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Dead Letter Queues
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
class DeadLetterHandler {
|
|
207
|
+
maxRetries = 3;
|
|
208
|
+
|
|
209
|
+
async handleFailure(message: Message, error: Error): Promise<void> {
|
|
210
|
+
const retryCount = message.metadata.retryCount || 0;
|
|
211
|
+
|
|
212
|
+
if (retryCount < this.maxRetries) {
|
|
213
|
+
// Retry with backoff
|
|
214
|
+
await this.scheduleRetry(message, retryCount + 1);
|
|
215
|
+
} else {
|
|
216
|
+
// Move to DLQ
|
|
217
|
+
await this.moveToDLQ(message, error);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
async moveToDLQ(message: Message, error: Error): Promise<void> {
|
|
222
|
+
await this.dlqChannel.publish('dead-letter', {
|
|
223
|
+
originalMessage: message,
|
|
224
|
+
error: error.message,
|
|
225
|
+
failedAt: new Date()
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
// Alert operations
|
|
229
|
+
await this.alerting.notify('Message moved to DLQ', { message, error });
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## Best Practices
|
|
235
|
+
|
|
236
|
+
- Use correlation IDs to trace message flows
|
|
237
|
+
- Make consumers idempotent
|
|
238
|
+
- Use dead letter queues for failed messages
|
|
239
|
+
- Monitor queue depths and consumer lag
|
|
240
|
+
- Design for eventual consistency
|
|
241
|
+
- Version your message schemas
|
|
242
|
+
- Include metadata (timestamp, correlationId, causationId)
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# Event-Driven Architecture
|
|
2
|
+
|
|
3
|
+
## Event Sourcing
|
|
4
|
+
|
|
5
|
+
Store state as a sequence of events.
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
interface Event {
|
|
9
|
+
id: string;
|
|
10
|
+
aggregateId: string;
|
|
11
|
+
type: string;
|
|
12
|
+
data: unknown;
|
|
13
|
+
timestamp: Date;
|
|
14
|
+
version: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
class Account {
|
|
18
|
+
private balance = 0;
|
|
19
|
+
private version = 0;
|
|
20
|
+
|
|
21
|
+
static fromEvents(events: Event[]): Account {
|
|
22
|
+
const account = new Account();
|
|
23
|
+
events.forEach(event => account.apply(event));
|
|
24
|
+
return account;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
private apply(event: Event): void {
|
|
28
|
+
switch (event.type) {
|
|
29
|
+
case 'MoneyDeposited':
|
|
30
|
+
this.balance += (event.data as { amount: number }).amount;
|
|
31
|
+
break;
|
|
32
|
+
case 'MoneyWithdrawn':
|
|
33
|
+
this.balance -= (event.data as { amount: number }).amount;
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
this.version = event.version;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## CQRS (Command Query Responsibility Segregation)
|
|
42
|
+
|
|
43
|
+
Separate read and write models.
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
// Write Model (Commands)
|
|
47
|
+
class OrderCommandHandler {
|
|
48
|
+
async handle(cmd: PlaceOrderCommand): Promise<void> {
|
|
49
|
+
const order = new Order(cmd.orderId, cmd.items);
|
|
50
|
+
await this.eventStore.save(order.changes());
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Read Model (Queries)
|
|
55
|
+
class OrderQueryService {
|
|
56
|
+
async getOrderSummary(orderId: string): Promise<OrderSummaryDTO> {
|
|
57
|
+
return this.readDb.query('SELECT * FROM order_summaries WHERE id = $1', [orderId]);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Projection updates read model from events
|
|
62
|
+
class OrderProjection {
|
|
63
|
+
async handle(event: OrderPlaced): Promise<void> {
|
|
64
|
+
await this.readDb.insert('order_summaries', {
|
|
65
|
+
id: event.orderId,
|
|
66
|
+
status: 'placed',
|
|
67
|
+
total: event.total
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Saga Pattern
|
|
74
|
+
|
|
75
|
+
Manage long-running transactions across services.
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
class OrderSaga {
|
|
79
|
+
async execute(orderId: string): Promise<void> {
|
|
80
|
+
try {
|
|
81
|
+
await this.paymentService.charge(orderId);
|
|
82
|
+
await this.inventoryService.reserve(orderId);
|
|
83
|
+
await this.shippingService.schedule(orderId);
|
|
84
|
+
} catch (error) {
|
|
85
|
+
await this.compensate(orderId, error);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
private async compensate(orderId: string, error: Error): Promise<void> {
|
|
90
|
+
await this.shippingService.cancel(orderId);
|
|
91
|
+
await this.inventoryService.release(orderId);
|
|
92
|
+
await this.paymentService.refund(orderId);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Event Versioning
|
|
98
|
+
|
|
99
|
+
Handle schema changes gracefully.
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
interface EventUpgrader {
|
|
103
|
+
upgrade(event: Event): Event;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
class OrderPlacedV1ToV2 implements EventUpgrader {
|
|
107
|
+
upgrade(event: Event): Event {
|
|
108
|
+
const oldData = event.data as OrderPlacedV1Data;
|
|
109
|
+
return {
|
|
110
|
+
...event,
|
|
111
|
+
type: 'OrderPlaced',
|
|
112
|
+
version: 2,
|
|
113
|
+
data: {
|
|
114
|
+
...oldData,
|
|
115
|
+
currency: 'USD' // New field with default
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Best Practices
|
|
123
|
+
|
|
124
|
+
- Events are immutable facts
|
|
125
|
+
- Include enough context in events for consumers
|
|
126
|
+
- Version events from the start
|
|
127
|
+
- Use idempotent event handlers
|
|
128
|
+
- Design for eventual consistency
|
|
129
|
+
- Consider snapshots for aggregates with many events
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Feature Toggles
|
|
2
|
+
|
|
3
|
+
## Toggle Types
|
|
4
|
+
|
|
5
|
+
### Release Toggles
|
|
6
|
+
Hide incomplete features in production.
|
|
7
|
+
|
|
8
|
+
```typescript
|
|
9
|
+
if (featureFlags.isEnabled('new-checkout')) {
|
|
10
|
+
return <NewCheckout />;
|
|
11
|
+
}
|
|
12
|
+
return <LegacyCheckout />;
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### Experiment Toggles
|
|
16
|
+
A/B testing and gradual rollouts.
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
const variant = featureFlags.getVariant('pricing-experiment', userId);
|
|
20
|
+
if (variant === 'new-pricing') {
|
|
21
|
+
return calculateNewPricing(cart);
|
|
22
|
+
}
|
|
23
|
+
return calculateLegacyPricing(cart);
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Ops Toggles
|
|
27
|
+
Runtime operational control.
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
if (featureFlags.isEnabled('enable-caching')) {
|
|
31
|
+
return cache.get(key) || fetchFromDatabase(key);
|
|
32
|
+
}
|
|
33
|
+
return fetchFromDatabase(key);
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Implementation
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
interface FeatureFlags {
|
|
40
|
+
isEnabled(flag: string, context?: Context): boolean;
|
|
41
|
+
getVariant(flag: string, userId: string): string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
class FeatureFlagService implements FeatureFlags {
|
|
45
|
+
constructor(private config: Map<string, FlagConfig>) {}
|
|
46
|
+
|
|
47
|
+
isEnabled(flag: string, context?: Context): boolean {
|
|
48
|
+
const config = this.config.get(flag);
|
|
49
|
+
if (!config) return false;
|
|
50
|
+
|
|
51
|
+
if (config.percentage) {
|
|
52
|
+
return this.isInPercentage(context?.userId, config.percentage);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return config.enabled;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private isInPercentage(userId: string | undefined, percentage: number): boolean {
|
|
59
|
+
if (!userId) return false;
|
|
60
|
+
const hash = this.hashUserId(userId);
|
|
61
|
+
return (hash % 100) < percentage;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Best Practices
|
|
67
|
+
|
|
68
|
+
- Remove toggles after feature is stable
|
|
69
|
+
- Use clear naming conventions
|
|
70
|
+
- Log toggle decisions for debugging
|
|
71
|
+
- Test both toggle states
|
|
72
|
+
- Limit number of active toggles
|
|
73
|
+
- Document toggle purpose and expiration
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# GUI Architecture Patterns
|
|
2
|
+
|
|
3
|
+
## MVC (Model-View-Controller)
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
// Model - data and business logic
|
|
7
|
+
class UserModel {
|
|
8
|
+
private users: User[] = [];
|
|
9
|
+
|
|
10
|
+
getUsers(): User[] { return this.users; }
|
|
11
|
+
addUser(user: User): void { this.users.push(user); }
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// View - presentation
|
|
15
|
+
class UserView {
|
|
16
|
+
render(users: User[]): void {
|
|
17
|
+
console.log('Users:', users);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Controller - handles input, coordinates
|
|
22
|
+
class UserController {
|
|
23
|
+
constructor(
|
|
24
|
+
private model: UserModel,
|
|
25
|
+
private view: UserView
|
|
26
|
+
) {}
|
|
27
|
+
|
|
28
|
+
handleAddUser(userData: UserData): void {
|
|
29
|
+
const user = new User(userData);
|
|
30
|
+
this.model.addUser(user);
|
|
31
|
+
this.view.render(this.model.getUsers());
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## MVP (Model-View-Presenter)
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
// View interface - defines what presenter can call
|
|
40
|
+
interface UserView {
|
|
41
|
+
showUsers(users: User[]): void;
|
|
42
|
+
showError(message: string): void;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Presenter - all presentation logic
|
|
46
|
+
class UserPresenter {
|
|
47
|
+
constructor(
|
|
48
|
+
private view: UserView,
|
|
49
|
+
private model: UserModel
|
|
50
|
+
) {}
|
|
51
|
+
|
|
52
|
+
loadUsers(): void {
|
|
53
|
+
try {
|
|
54
|
+
const users = this.model.getUsers();
|
|
55
|
+
this.view.showUsers(users);
|
|
56
|
+
} catch (error) {
|
|
57
|
+
this.view.showError('Failed to load users');
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// View implementation - passive, no logic
|
|
63
|
+
class UserListView implements UserView {
|
|
64
|
+
showUsers(users: User[]): void { /* render list */ }
|
|
65
|
+
showError(message: string): void { /* show error */ }
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## MVVM (Model-View-ViewModel)
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
// ViewModel - exposes observable state
|
|
73
|
+
class UserViewModel {
|
|
74
|
+
users = observable<User[]>([]);
|
|
75
|
+
isLoading = observable(false);
|
|
76
|
+
|
|
77
|
+
async loadUsers(): Promise<void> {
|
|
78
|
+
this.isLoading.set(true);
|
|
79
|
+
const users = await this.userService.getUsers();
|
|
80
|
+
this.users.set(users);
|
|
81
|
+
this.isLoading.set(false);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// View binds to ViewModel
|
|
86
|
+
const UserList = observer(({ viewModel }: { viewModel: UserViewModel }) => (
|
|
87
|
+
<div>
|
|
88
|
+
{viewModel.isLoading.get() ? (
|
|
89
|
+
<Spinner />
|
|
90
|
+
) : (
|
|
91
|
+
viewModel.users.get().map(user => <UserItem key={user.id} user={user} />)
|
|
92
|
+
)}
|
|
93
|
+
</div>
|
|
94
|
+
));
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Component Architecture (React/Vue)
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
// Presentational component - no state, just props
|
|
101
|
+
const UserCard = ({ user, onDelete }: UserCardProps) => (
|
|
102
|
+
<div className="user-card">
|
|
103
|
+
<h3>{user.name}</h3>
|
|
104
|
+
<button onClick={() => onDelete(user.id)}>Delete</button>
|
|
105
|
+
</div>
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
// Container component - manages state
|
|
109
|
+
const UserListContainer = () => {
|
|
110
|
+
const [users, setUsers] = useState<User[]>([]);
|
|
111
|
+
|
|
112
|
+
useEffect(() => {
|
|
113
|
+
userService.getUsers().then(setUsers);
|
|
114
|
+
}, []);
|
|
115
|
+
|
|
116
|
+
const handleDelete = (id: string) => {
|
|
117
|
+
userService.deleteUser(id).then(() => {
|
|
118
|
+
setUsers(users.filter(u => u.id !== id));
|
|
119
|
+
});
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
return <UserList users={users} onDelete={handleDelete} />;
|
|
123
|
+
};
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Best Practices
|
|
127
|
+
|
|
128
|
+
- Separate UI logic from business logic
|
|
129
|
+
- Keep views as simple as possible
|
|
130
|
+
- Use unidirectional data flow when possible
|
|
131
|
+
- Make components reusable and testable
|
|
132
|
+
- Choose pattern based on framework and team familiarity
|