specweave 0.23.18 → 0.24.0
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-plugin/marketplace.json +93 -49
- package/CLAUDE.md +137 -4
- package/dist/src/cli/helpers/ado-area-path-mapper.d.ts +89 -0
- package/dist/src/cli/helpers/ado-area-path-mapper.d.ts.map +1 -0
- package/dist/src/cli/helpers/ado-area-path-mapper.js +213 -0
- package/dist/src/cli/helpers/ado-area-path-mapper.js.map +1 -0
- package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.d.ts +29 -0
- package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.d.ts.map +1 -0
- package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.js +109 -0
- package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.js.map +1 -0
- package/dist/src/cli/helpers/issue-tracker/ado.d.ts +1 -0
- package/dist/src/cli/helpers/issue-tracker/ado.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/ado.js +2 -0
- package/dist/src/cli/helpers/issue-tracker/ado.js.map +1 -1
- package/dist/src/cli/helpers/smart-filter.d.ts +83 -0
- package/dist/src/cli/helpers/smart-filter.d.ts.map +1 -0
- package/dist/src/cli/helpers/smart-filter.js +265 -0
- package/dist/src/cli/helpers/smart-filter.js.map +1 -0
- package/dist/src/core/qa/quality-gate-decider.d.ts +1 -1
- package/dist/src/core/qa/quality-gate-decider.js +2 -2
- package/dist/src/core/qa/quality-gate-decider.js.map +1 -1
- package/dist/src/core/qa/risk-calculator.d.ts +2 -2
- package/dist/src/core/qa/risk-calculator.js +2 -2
- package/dist/src/core/validators/ac-presence-validator.d.ts +56 -0
- package/dist/src/core/validators/ac-presence-validator.d.ts.map +1 -0
- package/dist/src/core/validators/ac-presence-validator.js +149 -0
- package/dist/src/core/validators/ac-presence-validator.js.map +1 -0
- package/dist/src/integrations/ado/area-path-mapper.d.ts +137 -0
- package/dist/src/integrations/ado/area-path-mapper.d.ts.map +1 -0
- package/dist/src/integrations/ado/area-path-mapper.js +267 -0
- package/dist/src/integrations/ado/area-path-mapper.js.map +1 -0
- package/dist/src/integrations/jira/filter-processor.d.ts +126 -0
- package/dist/src/integrations/jira/filter-processor.d.ts.map +1 -0
- package/dist/src/integrations/jira/filter-processor.js +207 -0
- package/dist/src/integrations/jira/filter-processor.js.map +1 -0
- package/dist/src/integrations/jira/jira-client.d.ts +13 -0
- package/dist/src/integrations/jira/jira-client.d.ts.map +1 -1
- package/dist/src/integrations/jira/jira-client.js +33 -0
- package/dist/src/integrations/jira/jira-client.js.map +1 -1
- package/dist/src/utils/ac-embedder.d.ts +63 -0
- package/dist/src/utils/ac-embedder.d.ts.map +1 -0
- package/dist/src/utils/ac-embedder.js +217 -0
- package/dist/src/utils/ac-embedder.js.map +1 -0
- package/dist/src/utils/env-manager.d.ts +86 -0
- package/dist/src/utils/env-manager.d.ts.map +1 -0
- package/dist/src/utils/env-manager.js +188 -0
- package/dist/src/utils/env-manager.js.map +1 -0
- package/package.json +1 -1
- package/plugins/specweave/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave/agents/AGENTS-INDEX.md +1 -1
- package/plugins/specweave/agents/increment-quality-judge-v2/AGENT.md +9 -9
- package/plugins/specweave/commands/specweave-do.md +37 -0
- package/plugins/specweave/commands/specweave-done.md +159 -0
- package/plugins/specweave/commands/specweave-embed-acs.md +446 -0
- package/plugins/specweave/commands/specweave-next.md +148 -3
- package/plugins/specweave/commands/specweave-qa.md +2 -2
- package/plugins/specweave/hooks/pre-increment-start.sh +168 -0
- package/plugins/specweave/skills/SKILLS-INDEX.md +1 -1
- package/plugins/specweave-ado/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-ado/commands/specweave-ado-import-projects.md +331 -0
- package/plugins/specweave-alternatives/.claude-plugin/plugin.json +10 -0
- package/plugins/specweave-alternatives/commands/alternatives-analyze.md +336 -0
- package/plugins/specweave-alternatives/skills/architecture-alternatives/SKILL.md +651 -0
- package/plugins/specweave-alternatives/skills/bmad-method/SKILL.md +420 -0
- package/plugins/specweave-alternatives/skills/spec-kit-expert/SKILL.md +487 -0
- package/plugins/specweave-backend/commands/api-scaffold.md +80 -0
- package/plugins/specweave-backend/commands/crud-generate.md +109 -0
- package/plugins/specweave-backend/commands/migration-generate.md +139 -0
- package/plugins/specweave-confluent/commands/connector-deploy.md +154 -0
- package/plugins/specweave-confluent/commands/ksqldb-query.md +179 -0
- package/plugins/specweave-confluent/commands/schema-register.md +123 -0
- package/plugins/specweave-core/.claude-plugin/plugin.json +21 -0
- package/plugins/specweave-core/commands/architecture-review.md +288 -0
- package/plugins/specweave-core/commands/code-review.md +213 -0
- package/plugins/specweave-core/commands/refactor-plan.md +249 -0
- package/plugins/specweave-core/skills/code-quality/SKILL.md +157 -0
- package/plugins/specweave-core/skills/design-patterns/SKILL.md +244 -0
- package/plugins/specweave-core/skills/software-architecture/SKILL.md +83 -0
- package/plugins/specweave-cost-optimizer/.claude-plugin/plugin.json +22 -0
- package/plugins/specweave-cost-optimizer/commands/cost-analyze.md +360 -0
- package/plugins/specweave-cost-optimizer/commands/cost-optimize.md +480 -0
- package/plugins/specweave-cost-optimizer/skills/aws-cost-expert/SKILL.md +416 -0
- package/plugins/specweave-cost-optimizer/skills/cloud-pricing/SKILL.md +325 -0
- package/plugins/specweave-cost-optimizer/skills/cost-optimization/SKILL.md +337 -0
- package/plugins/specweave-diagrams/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-diagrams/commands/diagrams-generate.md +168 -0
- package/plugins/specweave-docs/.claude-plugin/plugin.json +10 -0
- package/plugins/specweave-docs/commands/docs-generate.md +441 -0
- package/plugins/specweave-docs/commands/docs-init.md +334 -0
- package/plugins/specweave-docs/skills/docusaurus/SKILL.md +581 -0
- package/plugins/specweave-docs/skills/spec-driven-brainstorming/SKILL.md +689 -0
- package/plugins/specweave-docs/skills/technical-writing/SKILL.md +1039 -0
- package/plugins/specweave-docs-preview/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-figma/.claude-plugin/plugin.json +23 -0
- package/plugins/specweave-figma/commands/figma-import.md +690 -0
- package/plugins/specweave-figma/commands/figma-to-react.md +834 -0
- package/plugins/specweave-figma/commands/figma-tokens.md +815 -0
- package/plugins/specweave-frontend/.claude-plugin/plugin.json +21 -0
- package/plugins/specweave-frontend/agents/frontend-architect/AGENT.md +387 -0
- package/plugins/specweave-frontend/agents/frontend-architect/README.md +385 -0
- package/plugins/specweave-frontend/agents/frontend-architect/examples.md +590 -0
- package/plugins/specweave-frontend/agents/frontend-architect/templates/component-template.tsx +152 -0
- package/plugins/specweave-frontend/agents/frontend-architect/templates/hook-template.ts +311 -0
- package/plugins/specweave-frontend/agents/frontend-architect/templates/page-template.tsx +228 -0
- package/plugins/specweave-frontend/commands/component-generate.md +510 -0
- package/plugins/specweave-frontend/commands/design-system-init.md +494 -0
- package/plugins/specweave-frontend/commands/frontend-scaffold.md +207 -0
- package/plugins/specweave-frontend/commands/nextjs-setup.md +396 -0
- package/plugins/specweave-frontend/skills/design-system-architect/SKILL.md +278 -0
- package/plugins/specweave-frontend/skills/frontend/SKILL.md +420 -0
- package/plugins/specweave-frontend/skills/nextjs/SKILL.md +546 -0
- package/plugins/specweave-github/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +194 -0
- package/plugins/specweave-infrastructure/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-jira/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-jira/commands/import-projects.js +183 -0
- package/plugins/specweave-jira/commands/import-projects.md +97 -0
- package/plugins/specweave-jira/commands/import-projects.ts +288 -0
- package/plugins/specweave-jira/commands/specweave-jira-import-projects.md +298 -0
- package/plugins/specweave-kafka/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-kafka-streams/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-kubernetes/commands/cluster-setup.md +262 -0
- package/plugins/specweave-kubernetes/commands/deployment-generate.md +242 -0
- package/plugins/specweave-kubernetes/commands/helm-scaffold.md +333 -0
- package/plugins/specweave-ml/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-mobile/commands/app-scaffold.md +233 -0
- package/plugins/specweave-mobile/commands/build-config.md +256 -0
- package/plugins/specweave-mobile/commands/screen-generate.md +289 -0
- package/plugins/specweave-n8n/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-plugin-dev/.claude-plugin/plugin.json +13 -12
- package/plugins/specweave-plugin-dev/commands/plugin-create.md +333 -0
- package/plugins/specweave-plugin-dev/commands/plugin-publish.md +339 -0
- package/plugins/specweave-plugin-dev/commands/plugin-test.md +293 -0
- package/plugins/specweave-plugin-dev/skills/claude-sdk/SKILL.md +162 -0
- package/plugins/specweave-plugin-dev/skills/marketplace-publishing/SKILL.md +263 -0
- package/plugins/specweave-plugin-dev/skills/plugin-development/SKILL.md +316 -0
- package/plugins/specweave-release/.claude-plugin/plugin.json +1 -1
- package/plugins/specweave-release/commands/specweave-release-npm.md +110 -0
- package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +168 -0
- package/plugins/specweave-testing/.claude-plugin/plugin.json +21 -0
- package/plugins/specweave-testing/agents/qa-engineer/AGENT.md +797 -0
- package/plugins/specweave-testing/agents/qa-engineer/README.md +443 -0
- package/plugins/specweave-testing/agents/qa-engineer/templates/playwright-e2e-test.ts +470 -0
- package/plugins/specweave-testing/agents/qa-engineer/templates/test-data-factory.ts +507 -0
- package/plugins/specweave-testing/agents/qa-engineer/templates/vitest-unit-test.ts +400 -0
- package/plugins/specweave-testing/agents/qa-engineer/test-strategies.md +726 -0
- package/plugins/specweave-testing/commands/e2e-setup.md +1081 -0
- package/plugins/specweave-testing/commands/test-coverage.md +979 -0
- package/plugins/specweave-testing/commands/test-generate.md +1156 -0
- package/plugins/specweave-testing/commands/test-init.md +409 -0
- package/plugins/specweave-testing/skills/e2e-playwright/SKILL.md +769 -0
- package/plugins/specweave-testing/skills/tdd-expert/SKILL.md +934 -0
- package/plugins/specweave-testing/skills/unit-testing-expert/SKILL.md +1011 -0
- package/plugins/specweave-tooling/.claude-plugin/plugin.json +22 -0
- package/plugins/specweave-tooling/commands/specweave-tooling-skill-create.md +691 -0
- package/plugins/specweave-tooling/commands/specweave-tooling-skill-package.md +751 -0
- package/plugins/specweave-tooling/commands/specweave-tooling-skill-validate.md +858 -0
- package/plugins/specweave-ui/.claude-plugin/plugin.json +10 -0
- package/plugins/specweave-ui/commands/ui-automate.md +199 -0
- package/plugins/specweave-ui/commands/ui-inspect.md +70 -0
- package/plugins/specweave-ui/skills/browser-automation/SKILL.md +314 -0
- package/plugins/specweave-ui/skills/ui-testing/SKILL.md +716 -0
- package/plugins/specweave-ui/skills/visual-regression/SKILL.md +728 -0
- package/plugins/specweave/commands/check-hooks.md +0 -257
- package/plugins/specweave/commands/specweave-archive-increments.md +0 -82
- package/plugins/specweave-plugin-dev/skills/plugin-expert/SKILL.md +0 -1231
- /package/plugins/specweave/{agents/code-reviewer.md → skills/code-reviewer/SKILL.md} +0 -0
|
@@ -0,0 +1,651 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: architecture-alternatives
|
|
3
|
+
description: Software architecture patterns and alternatives expert. Covers monolith vs microservices, serverless, event-driven, CQRS, hexagonal architecture, clean architecture, DDD, and architecture decision frameworks. Activates for architecture patterns, monolith, microservices, serverless, event-driven, CQRS, hexagonal architecture, clean architecture, DDD, architecture decisions, system design, scalability patterns.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Architecture Alternatives Expert Skill
|
|
7
|
+
|
|
8
|
+
Expert in software architecture patterns, their tradeoffs, and when to use each approach. Helps teams make informed architecture decisions based on context, scale, and organizational constraints.
|
|
9
|
+
|
|
10
|
+
## Core Architecture Patterns
|
|
11
|
+
|
|
12
|
+
### 1. Monolith vs Microservices
|
|
13
|
+
|
|
14
|
+
#### Monolithic Architecture
|
|
15
|
+
|
|
16
|
+
**Definition**: Single deployable unit containing all application logic.
|
|
17
|
+
|
|
18
|
+
**Strengths**:
|
|
19
|
+
- **Simplicity**: Single codebase, deployment, and database
|
|
20
|
+
- **Performance**: In-process communication (no network overhead)
|
|
21
|
+
- **Development Speed**: Easy local development and debugging
|
|
22
|
+
- **Data Consistency**: ACID transactions across entire system
|
|
23
|
+
- **Lower Operational Complexity**: One service to deploy, monitor, scale
|
|
24
|
+
|
|
25
|
+
**Weaknesses**:
|
|
26
|
+
- **Scaling**: Must scale entire application (can't scale components independently)
|
|
27
|
+
- **Deployment Risk**: Single point of failure, entire app redeploys for small changes
|
|
28
|
+
- **Team Coordination**: Large teams may conflict on shared codebase
|
|
29
|
+
- **Technology Lock-in**: Hard to adopt new languages/frameworks incrementally
|
|
30
|
+
|
|
31
|
+
**Best For**:
|
|
32
|
+
- Startups and MVPs (speed to market)
|
|
33
|
+
- Teams < 15 engineers
|
|
34
|
+
- Well-defined domains with low complexity
|
|
35
|
+
- Applications with predictable load patterns
|
|
36
|
+
|
|
37
|
+
**Example**:
|
|
38
|
+
```typescript
|
|
39
|
+
// Monolithic e-commerce app
|
|
40
|
+
class EcommerceApp {
|
|
41
|
+
constructor(
|
|
42
|
+
private userService: UserService,
|
|
43
|
+
private productService: ProductService,
|
|
44
|
+
private orderService: OrderService,
|
|
45
|
+
private paymentService: PaymentService
|
|
46
|
+
) {}
|
|
47
|
+
|
|
48
|
+
async checkout(userId: string, cartItems: CartItem[]) {
|
|
49
|
+
// All in-process, single transaction
|
|
50
|
+
const user = await this.userService.getUser(userId);
|
|
51
|
+
const products = await this.productService.validateStock(cartItems);
|
|
52
|
+
const order = await this.orderService.createOrder(user, products);
|
|
53
|
+
const payment = await this.paymentService.processPayment(order);
|
|
54
|
+
return { order, payment };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
#### Microservices Architecture
|
|
60
|
+
|
|
61
|
+
**Definition**: System composed of independently deployable services, each owning its domain.
|
|
62
|
+
|
|
63
|
+
**Strengths**:
|
|
64
|
+
- **Independent Scaling**: Scale services based on their specific load
|
|
65
|
+
- **Technology Diversity**: Use best tool for each service
|
|
66
|
+
- **Team Autonomy**: Teams own services end-to-end
|
|
67
|
+
- **Fault Isolation**: Failure in one service doesn't crash entire system
|
|
68
|
+
- **Faster Deployment**: Deploy services independently
|
|
69
|
+
|
|
70
|
+
**Weaknesses**:
|
|
71
|
+
- **Operational Complexity**: Distributed tracing, service mesh, monitoring
|
|
72
|
+
- **Data Consistency**: No ACID transactions across services (eventual consistency)
|
|
73
|
+
- **Network Overhead**: Inter-service communication latency
|
|
74
|
+
- **Testing Complexity**: End-to-end testing requires running multiple services
|
|
75
|
+
- **Organizational Overhead**: Requires mature DevOps culture
|
|
76
|
+
|
|
77
|
+
**Best For**:
|
|
78
|
+
- Large teams (50+ engineers)
|
|
79
|
+
- Complex domains requiring team specialization
|
|
80
|
+
- Independent scaling requirements
|
|
81
|
+
- Organizations with mature DevOps practices
|
|
82
|
+
|
|
83
|
+
**Example**:
|
|
84
|
+
```typescript
|
|
85
|
+
// Microservices e-commerce (order service)
|
|
86
|
+
class OrderService {
|
|
87
|
+
async createOrder(userId: string, items: CartItem[]) {
|
|
88
|
+
// 1. Call User Service via HTTP/gRPC
|
|
89
|
+
const user = await this.httpClient.get(`http://user-service/users/${userId}`);
|
|
90
|
+
|
|
91
|
+
// 2. Call Product Service to validate stock
|
|
92
|
+
const stockCheck = await this.httpClient.post('http://product-service/validate', { items });
|
|
93
|
+
|
|
94
|
+
// 3. Create order locally
|
|
95
|
+
const order = await this.orderRepo.create({ userId, items, total: stockCheck.total });
|
|
96
|
+
|
|
97
|
+
// 4. Publish OrderCreated event (eventual consistency)
|
|
98
|
+
await this.eventBus.publish('OrderCreated', { orderId: order.id, items });
|
|
99
|
+
|
|
100
|
+
return order;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**Decision Matrix**:
|
|
106
|
+
|
|
107
|
+
| Factor | Monolith | Microservices |
|
|
108
|
+
|--------|----------|---------------|
|
|
109
|
+
| Team Size | < 15 | 50+ |
|
|
110
|
+
| Domain Complexity | Low-Medium | High |
|
|
111
|
+
| Deployment Frequency | Weekly | Multiple/day |
|
|
112
|
+
| Scalability Needs | Uniform | Heterogeneous |
|
|
113
|
+
| Ops Maturity | Basic | Advanced |
|
|
114
|
+
|
|
115
|
+
### 2. Serverless Architecture
|
|
116
|
+
|
|
117
|
+
**Definition**: Application logic runs in managed, ephemeral compute environments (FaaS - Function as a Service).
|
|
118
|
+
|
|
119
|
+
**Patterns**:
|
|
120
|
+
|
|
121
|
+
#### Event-Driven Functions
|
|
122
|
+
```typescript
|
|
123
|
+
// AWS Lambda example
|
|
124
|
+
export const handler = async (event: S3Event) => {
|
|
125
|
+
for (const record of event.Records) {
|
|
126
|
+
const bucket = record.s3.bucket.name;
|
|
127
|
+
const key = record.s3.object.key;
|
|
128
|
+
|
|
129
|
+
// Process uploaded image
|
|
130
|
+
const image = await s3.getObject({ Bucket: bucket, Key: key });
|
|
131
|
+
const thumbnail = await generateThumbnail(image);
|
|
132
|
+
await s3.putObject({ Bucket: `${bucket}-thumbnails`, Key: key, Body: thumbnail });
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
#### API Gateway + Lambda
|
|
138
|
+
```typescript
|
|
139
|
+
// API Gateway Lambda handler
|
|
140
|
+
export const handler = async (event: APIGatewayProxyEvent) => {
|
|
141
|
+
const userId = event.pathParameters?.userId;
|
|
142
|
+
const user = await dynamoDB.get({ TableName: 'Users', Key: { id: userId } });
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
statusCode: 200,
|
|
146
|
+
body: JSON.stringify(user),
|
|
147
|
+
};
|
|
148
|
+
};
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**Strengths**:
|
|
152
|
+
- **Zero Server Management**: No infrastructure provisioning
|
|
153
|
+
- **Automatic Scaling**: Scales to zero, scales to millions
|
|
154
|
+
- **Pay-per-Use**: Only pay for actual execution time
|
|
155
|
+
- **Fast Iteration**: Deploy functions independently
|
|
156
|
+
|
|
157
|
+
**Weaknesses**:
|
|
158
|
+
- **Cold Starts**: 100ms-5s latency for infrequent functions
|
|
159
|
+
- **Vendor Lock-in**: AWS Lambda, Azure Functions, GCP Cloud Functions
|
|
160
|
+
- **Debugging Complexity**: Distributed logs, limited local testing
|
|
161
|
+
- **Statelessness**: No in-memory state (must use external storage)
|
|
162
|
+
- **Timeout Limits**: AWS Lambda max 15 minutes
|
|
163
|
+
|
|
164
|
+
**Best For**:
|
|
165
|
+
- Event-driven workloads (file processing, webhooks)
|
|
166
|
+
- Sporadic traffic patterns
|
|
167
|
+
- Rapid prototyping and MVPs
|
|
168
|
+
- Cost-sensitive projects with variable load
|
|
169
|
+
|
|
170
|
+
**Anti-Patterns**:
|
|
171
|
+
- Long-running processes (>15 min)
|
|
172
|
+
- Latency-sensitive APIs (<50ms required)
|
|
173
|
+
- Stateful applications (WebSocket servers)
|
|
174
|
+
|
|
175
|
+
### 3. Event-Driven Architecture
|
|
176
|
+
|
|
177
|
+
**Definition**: System components communicate through events rather than direct calls.
|
|
178
|
+
|
|
179
|
+
**Patterns**:
|
|
180
|
+
|
|
181
|
+
#### Event Sourcing
|
|
182
|
+
```typescript
|
|
183
|
+
// Store events, not current state
|
|
184
|
+
type AccountEvent =
|
|
185
|
+
| { type: 'AccountOpened'; accountId: string; initialBalance: number }
|
|
186
|
+
| { type: 'MoneyDeposited'; accountId: string; amount: number }
|
|
187
|
+
| { type: 'MoneyWithdrawn'; accountId: string; amount: number };
|
|
188
|
+
|
|
189
|
+
class BankAccount {
|
|
190
|
+
private balance = 0;
|
|
191
|
+
|
|
192
|
+
// Replay events to reconstruct state
|
|
193
|
+
applyEvent(event: AccountEvent) {
|
|
194
|
+
switch (event.type) {
|
|
195
|
+
case 'AccountOpened':
|
|
196
|
+
this.balance = event.initialBalance;
|
|
197
|
+
break;
|
|
198
|
+
case 'MoneyDeposited':
|
|
199
|
+
this.balance += event.amount;
|
|
200
|
+
break;
|
|
201
|
+
case 'MoneyWithdrawn':
|
|
202
|
+
this.balance -= event.amount;
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Commands produce events
|
|
208
|
+
withdraw(amount: number): AccountEvent[] {
|
|
209
|
+
if (this.balance < amount) throw new Error('Insufficient funds');
|
|
210
|
+
return [{ type: 'MoneyWithdrawn', accountId: this.id, amount }];
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
#### CQRS (Command Query Responsibility Segregation)
|
|
216
|
+
```typescript
|
|
217
|
+
// Separate read and write models
|
|
218
|
+
|
|
219
|
+
// WRITE MODEL (Commands)
|
|
220
|
+
class OrderCommandHandler {
|
|
221
|
+
async createOrder(command: CreateOrderCommand) {
|
|
222
|
+
const order = new Order(command.userId, command.items);
|
|
223
|
+
await this.orderWriteRepo.save(order);
|
|
224
|
+
await this.eventBus.publish('OrderCreated', order.toEvent());
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// READ MODEL (Queries - optimized for reads)
|
|
229
|
+
class OrderQueryHandler {
|
|
230
|
+
async getUserOrders(userId: string): Promise<OrderSummary[]> {
|
|
231
|
+
// Denormalized view, optimized for fast reads
|
|
232
|
+
return this.orderReadRepo.findByUserId(userId);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Event handler updates read model asynchronously
|
|
237
|
+
class OrderEventHandler {
|
|
238
|
+
async onOrderCreated(event: OrderCreatedEvent) {
|
|
239
|
+
// Update denormalized read model
|
|
240
|
+
await this.orderReadRepo.insertSummary({
|
|
241
|
+
orderId: event.orderId,
|
|
242
|
+
userId: event.userId,
|
|
243
|
+
total: event.total,
|
|
244
|
+
status: 'pending',
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
**Strengths**:
|
|
251
|
+
- **Decoupling**: Services don't need to know about each other
|
|
252
|
+
- **Scalability**: Asynchronous processing handles load spikes
|
|
253
|
+
- **Audit Trail**: Event log provides complete history
|
|
254
|
+
- **Flexibility**: Add new event consumers without changing producers
|
|
255
|
+
|
|
256
|
+
**Weaknesses**:
|
|
257
|
+
- **Eventual Consistency**: Read models lag behind writes
|
|
258
|
+
- **Complexity**: Debugging distributed event flows
|
|
259
|
+
- **Event Schema Evolution**: Managing event versioning
|
|
260
|
+
- **Ordering Guarantees**: Hard to maintain event order across partitions
|
|
261
|
+
|
|
262
|
+
**Best For**:
|
|
263
|
+
- Systems requiring audit trails (finance, healthcare)
|
|
264
|
+
- High-throughput systems (IoT, analytics)
|
|
265
|
+
- Complex business workflows
|
|
266
|
+
- Systems with multiple consumers of same events
|
|
267
|
+
|
|
268
|
+
### 4. Hexagonal Architecture (Ports & Adapters)
|
|
269
|
+
|
|
270
|
+
**Definition**: Application core is independent of external concerns (frameworks, databases, UI).
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
// DOMAIN (Core - no external dependencies)
|
|
274
|
+
interface OrderRepository {
|
|
275
|
+
save(order: Order): Promise<void>;
|
|
276
|
+
findById(id: string): Promise<Order | null>;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
class Order {
|
|
280
|
+
constructor(
|
|
281
|
+
public readonly id: string,
|
|
282
|
+
public readonly items: OrderItem[],
|
|
283
|
+
public status: OrderStatus
|
|
284
|
+
) {}
|
|
285
|
+
|
|
286
|
+
// Business logic here, independent of infrastructure
|
|
287
|
+
complete() {
|
|
288
|
+
if (this.status !== 'pending') throw new Error('Order already completed');
|
|
289
|
+
this.status = 'completed';
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// APPLICATION (Use Cases)
|
|
294
|
+
class CompleteOrderUseCase {
|
|
295
|
+
constructor(private orderRepo: OrderRepository) {}
|
|
296
|
+
|
|
297
|
+
async execute(orderId: string) {
|
|
298
|
+
const order = await this.orderRepo.findById(orderId);
|
|
299
|
+
if (!order) throw new Error('Order not found');
|
|
300
|
+
|
|
301
|
+
order.complete();
|
|
302
|
+
await this.orderRepo.save(order);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// INFRASTRUCTURE (Adapters)
|
|
307
|
+
class PostgresOrderRepository implements OrderRepository {
|
|
308
|
+
async save(order: Order) {
|
|
309
|
+
await this.db.query('UPDATE orders SET status = $1 WHERE id = $2', [order.status, order.id]);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
async findById(id: string): Promise<Order | null> {
|
|
313
|
+
const row = await this.db.query('SELECT * FROM orders WHERE id = $1', [id]);
|
|
314
|
+
if (!row) return null;
|
|
315
|
+
return new Order(row.id, row.items, row.status);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// UI (Adapter)
|
|
320
|
+
class ExpressOrderController {
|
|
321
|
+
constructor(private completeOrder: CompleteOrderUseCase) {}
|
|
322
|
+
|
|
323
|
+
async handleCompleteOrder(req: Request, res: Response) {
|
|
324
|
+
try {
|
|
325
|
+
await this.completeOrder.execute(req.params.orderId);
|
|
326
|
+
res.status(200).send({ message: 'Order completed' });
|
|
327
|
+
} catch (error) {
|
|
328
|
+
res.status(400).send({ error: error.message });
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
**Strengths**:
|
|
335
|
+
- **Testability**: Core business logic testable without infrastructure
|
|
336
|
+
- **Flexibility**: Swap databases, frameworks without changing core
|
|
337
|
+
- **Clear Separation**: Business rules isolated from technical concerns
|
|
338
|
+
- **Domain-Focused**: Encourages focus on business logic, not frameworks
|
|
339
|
+
|
|
340
|
+
**Weaknesses**:
|
|
341
|
+
- **Boilerplate**: More interfaces and abstraction layers
|
|
342
|
+
- **Learning Curve**: Requires understanding of dependency inversion
|
|
343
|
+
- **Overkill for Simple Apps**: CRUD apps may not need this complexity
|
|
344
|
+
|
|
345
|
+
**Best For**:
|
|
346
|
+
- Domain-rich applications (complex business rules)
|
|
347
|
+
- Long-lived projects (future-proofing)
|
|
348
|
+
- Teams practicing DDD (Domain-Driven Design)
|
|
349
|
+
|
|
350
|
+
### 5. Clean Architecture (Uncle Bob)
|
|
351
|
+
|
|
352
|
+
**Definition**: Layered architecture with dependency inversion - dependencies point inward.
|
|
353
|
+
|
|
354
|
+
**Layers** (outermost to innermost):
|
|
355
|
+
1. **Frameworks & Drivers**: UI, Database, External APIs
|
|
356
|
+
2. **Interface Adapters**: Controllers, Presenters, Gateways
|
|
357
|
+
3. **Application Business Rules**: Use Cases
|
|
358
|
+
4. **Enterprise Business Rules**: Entities (domain models)
|
|
359
|
+
|
|
360
|
+
**Dependency Rule**: Inner layers NEVER depend on outer layers.
|
|
361
|
+
|
|
362
|
+
```typescript
|
|
363
|
+
// ENTITIES (innermost layer)
|
|
364
|
+
class User {
|
|
365
|
+
constructor(public id: string, public email: string, public passwordHash: string) {}
|
|
366
|
+
|
|
367
|
+
validatePassword(plainPassword: string, hasher: PasswordHasher): boolean {
|
|
368
|
+
return hasher.compare(plainPassword, this.passwordHash);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// USE CASES (application layer)
|
|
373
|
+
interface UserRepository {
|
|
374
|
+
findByEmail(email: string): Promise<User | null>;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
class LoginUseCase {
|
|
378
|
+
constructor(
|
|
379
|
+
private userRepo: UserRepository,
|
|
380
|
+
private passwordHasher: PasswordHasher,
|
|
381
|
+
private tokenGenerator: TokenGenerator
|
|
382
|
+
) {}
|
|
383
|
+
|
|
384
|
+
async execute(email: string, password: string): Promise<{ token: string }> {
|
|
385
|
+
const user = await this.userRepo.findByEmail(email);
|
|
386
|
+
if (!user || !user.validatePassword(password, this.passwordHasher)) {
|
|
387
|
+
throw new Error('Invalid credentials');
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const token = this.tokenGenerator.generate(user.id);
|
|
391
|
+
return { token };
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// INTERFACE ADAPTERS (controllers)
|
|
396
|
+
class AuthController {
|
|
397
|
+
constructor(private loginUseCase: LoginUseCase) {}
|
|
398
|
+
|
|
399
|
+
async login(req: Request, res: Response) {
|
|
400
|
+
const { email, password } = req.body;
|
|
401
|
+
const result = await this.loginUseCase.execute(email, password);
|
|
402
|
+
res.json(result);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// FRAMEWORKS & DRIVERS (infrastructure)
|
|
407
|
+
class BcryptPasswordHasher implements PasswordHasher {
|
|
408
|
+
compare(plain: string, hash: string): boolean {
|
|
409
|
+
return bcrypt.compareSync(plain, hash);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
class PostgresUserRepository implements UserRepository {
|
|
414
|
+
async findByEmail(email: string): Promise<User | null> {
|
|
415
|
+
const row = await this.db.query('SELECT * FROM users WHERE email = $1', [email]);
|
|
416
|
+
if (!row) return null;
|
|
417
|
+
return new User(row.id, row.email, row.password_hash);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
**Strengths**:
|
|
423
|
+
- **Framework Independence**: Core business logic doesn't depend on Express, React, etc.
|
|
424
|
+
- **Database Independence**: Swap PostgreSQL for MongoDB without changing use cases
|
|
425
|
+
- **Testability**: Mock interfaces easily for unit testing
|
|
426
|
+
- **UI Independence**: Same use cases work with REST API, GraphQL, CLI
|
|
427
|
+
|
|
428
|
+
**Weaknesses**:
|
|
429
|
+
- **Complexity**: Many layers and interfaces
|
|
430
|
+
- **Indirection**: Tracing logic through layers can be difficult
|
|
431
|
+
- **Over-Engineering**: CRUD apps don't benefit from this structure
|
|
432
|
+
|
|
433
|
+
**Best For**:
|
|
434
|
+
- Enterprise applications with long lifespans
|
|
435
|
+
- Applications with multiple UIs (web, mobile, CLI)
|
|
436
|
+
- Teams with experienced architects
|
|
437
|
+
|
|
438
|
+
### 6. Domain-Driven Design (DDD)
|
|
439
|
+
|
|
440
|
+
**Strategic Design Patterns**:
|
|
441
|
+
|
|
442
|
+
#### Bounded Contexts
|
|
443
|
+
```typescript
|
|
444
|
+
// SALES CONTEXT
|
|
445
|
+
class Customer {
|
|
446
|
+
constructor(public id: string, public creditLimit: number) {}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// SUPPORT CONTEXT (different model for same entity!)
|
|
450
|
+
class Customer {
|
|
451
|
+
constructor(public id: string, public supportTier: 'basic' | 'premium') {}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// Each context has its own model, even for same real-world concept
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
#### Context Mapping
|
|
458
|
+
```
|
|
459
|
+
Sales Context → Customer Context (Shared Kernel)
|
|
460
|
+
- Share: CustomerId, CustomerName
|
|
461
|
+
- Separate: CreditLimit (Sales only), SupportTickets (Support only)
|
|
462
|
+
|
|
463
|
+
Inventory Context → Sales Context (Upstream/Downstream)
|
|
464
|
+
- Inventory publishes StockUpdated events
|
|
465
|
+
- Sales consumes events to update product availability
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
**Tactical Design Patterns**:
|
|
469
|
+
|
|
470
|
+
#### Aggregates
|
|
471
|
+
```typescript
|
|
472
|
+
// Order is the Aggregate Root
|
|
473
|
+
class Order {
|
|
474
|
+
private items: OrderItem[] = [];
|
|
475
|
+
|
|
476
|
+
// Enforce business invariants
|
|
477
|
+
addItem(product: Product, quantity: number) {
|
|
478
|
+
if (quantity <= 0) throw new Error('Quantity must be positive');
|
|
479
|
+
|
|
480
|
+
// Business rule: max 10 items per order
|
|
481
|
+
if (this.items.length >= 10) throw new Error('Order cannot exceed 10 items');
|
|
482
|
+
|
|
483
|
+
this.items.push(new OrderItem(product, quantity));
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// Aggregate ensures consistency
|
|
487
|
+
get total(): number {
|
|
488
|
+
return this.items.reduce((sum, item) => sum + item.subtotal, 0);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// OrderItem is NOT an Aggregate Root, only accessible through Order
|
|
493
|
+
class OrderItem {
|
|
494
|
+
constructor(public product: Product, public quantity: number) {}
|
|
495
|
+
|
|
496
|
+
get subtotal(): number {
|
|
497
|
+
return this.product.price * this.quantity;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
#### Value Objects
|
|
503
|
+
```typescript
|
|
504
|
+
class Money {
|
|
505
|
+
constructor(public readonly amount: number, public readonly currency: string) {
|
|
506
|
+
if (amount < 0) throw new Error('Amount cannot be negative');
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
add(other: Money): Money {
|
|
510
|
+
if (this.currency !== other.currency) {
|
|
511
|
+
throw new Error('Cannot add different currencies');
|
|
512
|
+
}
|
|
513
|
+
return new Money(this.amount + other.amount, this.currency);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
equals(other: Money): boolean {
|
|
517
|
+
return this.amount === other.amount && this.currency === other.currency;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// Immutable, defined by value (not identity)
|
|
522
|
+
const price1 = new Money(100, 'USD');
|
|
523
|
+
const price2 = new Money(100, 'USD');
|
|
524
|
+
console.log(price1.equals(price2)); // true (same value)
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
#### Domain Events
|
|
528
|
+
```typescript
|
|
529
|
+
class OrderPlaced {
|
|
530
|
+
constructor(
|
|
531
|
+
public readonly orderId: string,
|
|
532
|
+
public readonly userId: string,
|
|
533
|
+
public readonly total: Money,
|
|
534
|
+
public readonly occurredAt: Date
|
|
535
|
+
) {}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
class Order {
|
|
539
|
+
private events: DomainEvent[] = [];
|
|
540
|
+
|
|
541
|
+
place() {
|
|
542
|
+
if (this.status !== 'draft') throw new Error('Order already placed');
|
|
543
|
+
this.status = 'placed';
|
|
544
|
+
this.events.push(new OrderPlaced(this.id, this.userId, this.total, new Date()));
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
getEvents(): DomainEvent[] {
|
|
548
|
+
return this.events;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
**Best For**:
|
|
554
|
+
- Complex business domains (finance, insurance, logistics)
|
|
555
|
+
- Large teams needing clear boundaries
|
|
556
|
+
- Long-lived systems with evolving requirements
|
|
557
|
+
|
|
558
|
+
## Architecture Decision Framework
|
|
559
|
+
|
|
560
|
+
### 1. Context Analysis
|
|
561
|
+
|
|
562
|
+
**Team Context**:
|
|
563
|
+
- Team size: 5-10 (Monolith), 20-50 (Modular Monolith), 50+ (Microservices)
|
|
564
|
+
- Experience: Junior (simpler patterns), Senior (can handle complexity)
|
|
565
|
+
- Org structure: Conway's Law - architecture mirrors communication
|
|
566
|
+
|
|
567
|
+
**Technical Context**:
|
|
568
|
+
- Current scale: 1k users (Monolith), 100k users (Modular Monolith), 1M+ (Microservices)
|
|
569
|
+
- Growth rate: Steady (simpler), Exponential (plan for scale)
|
|
570
|
+
- Performance requirements: <100ms (consider caching, CDN), <10ms (in-memory, edge)
|
|
571
|
+
|
|
572
|
+
**Business Context**:
|
|
573
|
+
- Time to market: Fast (Monolith, Serverless), Can wait (Microservices)
|
|
574
|
+
- Budget: Limited (Serverless, Monolith), Generous (custom infrastructure)
|
|
575
|
+
- Risk tolerance: Low (proven patterns), High (bleeding edge)
|
|
576
|
+
|
|
577
|
+
### 2. Decision Matrix
|
|
578
|
+
|
|
579
|
+
| Architecture | Complexity | Scalability | Ops Overhead | Best For |
|
|
580
|
+
|--------------|------------|-------------|--------------|----------|
|
|
581
|
+
| Monolith | Low | Medium | Low | Startups, MVPs |
|
|
582
|
+
| Modular Monolith | Medium | Medium | Low | Growing startups |
|
|
583
|
+
| Microservices | High | High | High | Large orgs |
|
|
584
|
+
| Serverless | Low | Very High | Very Low | Event-driven, variable load |
|
|
585
|
+
| Event-Driven | High | Very High | High | High throughput, audit trails |
|
|
586
|
+
|
|
587
|
+
### 3. Migration Paths
|
|
588
|
+
|
|
589
|
+
**Monolith → Modular Monolith**:
|
|
590
|
+
1. Identify bounded contexts
|
|
591
|
+
2. Extract modules with clear interfaces
|
|
592
|
+
3. Enforce module boundaries (linting, architecture tests)
|
|
593
|
+
4. Keep single deployment unit
|
|
594
|
+
|
|
595
|
+
**Modular Monolith → Microservices**:
|
|
596
|
+
1. Start with most independent module
|
|
597
|
+
2. Extract as service with API
|
|
598
|
+
3. Introduce message bus for events
|
|
599
|
+
4. Gradually extract remaining modules
|
|
600
|
+
|
|
601
|
+
**Monolith → Serverless**:
|
|
602
|
+
1. Extract background jobs to Lambda functions
|
|
603
|
+
2. Move APIs to API Gateway + Lambda
|
|
604
|
+
3. Migrate to managed databases (RDS, DynamoDB)
|
|
605
|
+
4. Decompose monolith incrementally
|
|
606
|
+
|
|
607
|
+
## Anti-Patterns
|
|
608
|
+
|
|
609
|
+
### 1. Distributed Monolith
|
|
610
|
+
Microservices with tight coupling - worst of both worlds.
|
|
611
|
+
|
|
612
|
+
❌ **Symptom**: Services can't deploy independently (require coordinated releases)
|
|
613
|
+
✅ **Fix**: Introduce message bus, versioned APIs, backward compatibility
|
|
614
|
+
|
|
615
|
+
### 2. Premature Microservices
|
|
616
|
+
Starting with microservices before understanding domain.
|
|
617
|
+
|
|
618
|
+
❌ **Symptom**: Constantly moving logic between services
|
|
619
|
+
✅ **Fix**: Start with modular monolith, extract services when boundaries are clear
|
|
620
|
+
|
|
621
|
+
### 3. Anemic Domain Model
|
|
622
|
+
Entities with only getters/setters, all logic in services.
|
|
623
|
+
|
|
624
|
+
❌ **Symptom**: Entities are just data containers
|
|
625
|
+
✅ **Fix**: Move business logic into domain entities
|
|
626
|
+
|
|
627
|
+
### 4. God Aggregate
|
|
628
|
+
Single aggregate managing entire system state.
|
|
629
|
+
|
|
630
|
+
❌ **Symptom**: All commands touch the same aggregate
|
|
631
|
+
✅ **Fix**: Split into smaller aggregates with clear boundaries
|
|
632
|
+
|
|
633
|
+
## Resources
|
|
634
|
+
|
|
635
|
+
- [Martin Fowler - Microservices](https://martinfowler.com/articles/microservices.html)
|
|
636
|
+
- [Hexagonal Architecture - Alistair Cockburn](https://alistair.cockburn.us/hexagonal-architecture/)
|
|
637
|
+
- [Clean Architecture - Robert C. Martin](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)
|
|
638
|
+
- [Domain-Driven Design - Eric Evans](https://www.domainlanguage.com/ddd/)
|
|
639
|
+
|
|
640
|
+
## Activation Keywords
|
|
641
|
+
|
|
642
|
+
Ask me about:
|
|
643
|
+
- "Monolith vs microservices tradeoffs"
|
|
644
|
+
- "When to use serverless architecture"
|
|
645
|
+
- "Event-driven architecture patterns"
|
|
646
|
+
- "CQRS and event sourcing"
|
|
647
|
+
- "Hexagonal architecture examples"
|
|
648
|
+
- "Clean architecture in practice"
|
|
649
|
+
- "Domain-Driven Design patterns"
|
|
650
|
+
- "How to migrate from monolith to microservices"
|
|
651
|
+
- "Architecture decision frameworks"
|