cortex-agents 1.0.1 → 2.1.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/.opencode/agents/build.md +34 -3
- package/.opencode/agents/debug.md +24 -2
- package/.opencode/agents/devops.md +1 -2
- package/.opencode/agents/fullstack.md +1 -2
- package/.opencode/agents/plan.md +5 -3
- package/.opencode/agents/security.md +1 -2
- package/.opencode/agents/testing.md +1 -2
- package/.opencode/skills/api-design/SKILL.md +348 -0
- package/.opencode/skills/architecture-patterns/SKILL.md +323 -0
- package/.opencode/skills/backend-development/SKILL.md +329 -0
- package/.opencode/skills/code-quality/SKILL.md +12 -0
- package/.opencode/skills/database-design/SKILL.md +347 -0
- package/.opencode/skills/deployment-automation/SKILL.md +7 -0
- package/.opencode/skills/design-patterns/SKILL.md +295 -0
- package/.opencode/skills/desktop-development/SKILL.md +295 -0
- package/.opencode/skills/frontend-development/SKILL.md +210 -0
- package/.opencode/skills/mobile-development/SKILL.md +407 -0
- package/.opencode/skills/performance-optimization/SKILL.md +330 -0
- package/.opencode/skills/testing-strategies/SKILL.md +33 -0
- package/README.md +390 -76
- package/dist/cli.js +355 -68
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +38 -0
- package/dist/plugin.js +3 -2
- package/dist/registry.d.ts +45 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +140 -0
- package/dist/tools/cortex.d.ts.map +1 -1
- package/dist/tools/cortex.js +2 -3
- package/dist/tools/docs.d.ts +52 -0
- package/dist/tools/docs.d.ts.map +1 -0
- package/dist/tools/docs.js +328 -0
- package/package.json +11 -4
- package/.opencode/skills/web-development/SKILL.md +0 -122
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: database-design
|
|
3
|
+
description: Schema design, normalization, indexing, SQL and NoSQL patterns, ORM strategies, migrations, and caching layers
|
|
4
|
+
license: Apache-2.0
|
|
5
|
+
compatibility: opencode
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Database Design Skill
|
|
9
|
+
|
|
10
|
+
This skill provides patterns and best practices for designing, optimizing, and managing databases.
|
|
11
|
+
|
|
12
|
+
## When to Use
|
|
13
|
+
|
|
14
|
+
Use this skill when:
|
|
15
|
+
- Designing database schemas for new applications
|
|
16
|
+
- Optimizing slow queries or improving performance
|
|
17
|
+
- Choosing between SQL and NoSQL databases
|
|
18
|
+
- Setting up ORM patterns and migration strategies
|
|
19
|
+
- Implementing caching layers
|
|
20
|
+
|
|
21
|
+
## Database Selection Guide
|
|
22
|
+
|
|
23
|
+
| Database | Type | Best For |
|
|
24
|
+
|----------|------|----------|
|
|
25
|
+
| PostgreSQL | Relational | Complex queries, ACID, JSON support, full-text search |
|
|
26
|
+
| MySQL | Relational | Web applications, read-heavy workloads |
|
|
27
|
+
| SQLite | Relational | Embedded, mobile, local development, single-writer |
|
|
28
|
+
| MongoDB | Document | Flexible schemas, rapid prototyping, content management |
|
|
29
|
+
| Redis | Key-Value | Caching, sessions, rate limiting, pub/sub, leaderboards |
|
|
30
|
+
| Cassandra | Wide-Column | High write throughput, time-series, globally distributed |
|
|
31
|
+
| Neo4j | Graph | Social networks, recommendations, knowledge graphs |
|
|
32
|
+
| Elasticsearch | Search | Full-text search, log analytics, geo-spatial queries |
|
|
33
|
+
| DynamoDB | Key-Value/Doc | Serverless, predictable performance, AWS-native |
|
|
34
|
+
| ClickHouse | Columnar | Analytics, OLAP, real-time reporting |
|
|
35
|
+
|
|
36
|
+
## Relational Design
|
|
37
|
+
|
|
38
|
+
### Normalization
|
|
39
|
+
|
|
40
|
+
| Form | Rule | Example |
|
|
41
|
+
|------|------|---------|
|
|
42
|
+
| 1NF | Atomic values, no repeating groups | Split `tags: "a,b,c"` into separate rows |
|
|
43
|
+
| 2NF | 1NF + no partial dependencies | Move non-key attributes that depend only on part of composite key |
|
|
44
|
+
| 3NF | 2NF + no transitive dependencies | Move `city → state` to a separate table |
|
|
45
|
+
|
|
46
|
+
### When to Denormalize
|
|
47
|
+
- Read-heavy workloads where joins are expensive
|
|
48
|
+
- Reporting/analytics tables (materialized views)
|
|
49
|
+
- Caching computed values that change infrequently
|
|
50
|
+
- Always measure first — premature denormalization adds complexity
|
|
51
|
+
|
|
52
|
+
### Schema Design Best Practices
|
|
53
|
+
```sql
|
|
54
|
+
-- Good: clear naming, constraints, timestamps
|
|
55
|
+
CREATE TABLE users (
|
|
56
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
57
|
+
email VARCHAR(255) NOT NULL UNIQUE,
|
|
58
|
+
name VARCHAR(100) NOT NULL,
|
|
59
|
+
role VARCHAR(20) NOT NULL DEFAULT 'user'
|
|
60
|
+
CHECK (role IN ('user', 'admin', 'moderator')),
|
|
61
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
62
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
-- Add index for common queries
|
|
66
|
+
CREATE INDEX idx_users_email ON users(email);
|
|
67
|
+
CREATE INDEX idx_users_role ON users(role) WHERE role != 'user';
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Naming Conventions
|
|
71
|
+
- Tables: plural, snake_case (`order_items`, `user_roles`)
|
|
72
|
+
- Columns: snake_case, descriptive (`created_at`, not `ca`)
|
|
73
|
+
- Primary keys: `id` (or `{table}_id` for clarity)
|
|
74
|
+
- Foreign keys: `{referenced_table}_id` (`user_id`, `order_id`)
|
|
75
|
+
- Indexes: `idx_{table}_{columns}` (`idx_users_email`)
|
|
76
|
+
- Constraints: `chk_{table}_{column}`, `uq_{table}_{column}`
|
|
77
|
+
|
|
78
|
+
## Indexing
|
|
79
|
+
|
|
80
|
+
### Index Types (PostgreSQL)
|
|
81
|
+
| Type | Use Case |
|
|
82
|
+
|------|----------|
|
|
83
|
+
| B-tree (default) | Equality, range, sorting, LIKE 'prefix%' |
|
|
84
|
+
| Hash | Equality only (rarely needed, B-tree covers this) |
|
|
85
|
+
| GIN | Arrays, JSONB, full-text search |
|
|
86
|
+
| GiST | Geometry, ranges, full-text search |
|
|
87
|
+
| BRIN | Large sequential tables (time-series, logs) |
|
|
88
|
+
|
|
89
|
+
### Indexing Best Practices
|
|
90
|
+
- Index columns used in WHERE, JOIN, ORDER BY
|
|
91
|
+
- Composite indexes: put high-selectivity columns first
|
|
92
|
+
- Partial indexes to index only relevant rows
|
|
93
|
+
- Covering indexes to avoid table lookups
|
|
94
|
+
- Don't over-index — each index slows writes and uses storage
|
|
95
|
+
|
|
96
|
+
```sql
|
|
97
|
+
-- Composite index for common query pattern
|
|
98
|
+
CREATE INDEX idx_orders_user_status
|
|
99
|
+
ON orders(user_id, status);
|
|
100
|
+
|
|
101
|
+
-- Partial index for active records only
|
|
102
|
+
CREATE INDEX idx_users_active_email
|
|
103
|
+
ON users(email) WHERE deleted_at IS NULL;
|
|
104
|
+
|
|
105
|
+
-- Covering index (includes all needed columns)
|
|
106
|
+
CREATE INDEX idx_products_category_price
|
|
107
|
+
ON products(category_id, price)
|
|
108
|
+
INCLUDE (name, image_url);
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Index Analysis
|
|
112
|
+
```sql
|
|
113
|
+
-- Check if queries use indexes
|
|
114
|
+
EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'test@example.com';
|
|
115
|
+
|
|
116
|
+
-- Find unused indexes
|
|
117
|
+
SELECT indexrelname, idx_scan, idx_tup_read
|
|
118
|
+
FROM pg_stat_user_indexes
|
|
119
|
+
WHERE idx_scan = 0 AND indexrelname NOT LIKE 'pg_%';
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Query Optimization
|
|
123
|
+
|
|
124
|
+
### Common Performance Issues
|
|
125
|
+
| Issue | Symptom | Solution |
|
|
126
|
+
|-------|---------|----------|
|
|
127
|
+
| Missing index | Sequential scan on large table | Add appropriate index |
|
|
128
|
+
| N+1 queries | Hundreds of queries for one page | Eager load relations |
|
|
129
|
+
| SELECT * | Fetching unused columns | Select only needed columns |
|
|
130
|
+
| Large offset | Slow pagination (`OFFSET 10000`) | Use cursor-based pagination |
|
|
131
|
+
| Cartesian join | Exploding row count | Check JOIN conditions |
|
|
132
|
+
| Missing WHERE | Full table scan | Add filter conditions |
|
|
133
|
+
|
|
134
|
+
### Query Patterns
|
|
135
|
+
```sql
|
|
136
|
+
-- Cursor-based pagination (fast, stable)
|
|
137
|
+
SELECT * FROM orders
|
|
138
|
+
WHERE created_at < :last_cursor
|
|
139
|
+
ORDER BY created_at DESC
|
|
140
|
+
LIMIT 25;
|
|
141
|
+
|
|
142
|
+
-- Avoiding N+1 with JOIN
|
|
143
|
+
SELECT u.*, COUNT(o.id) as order_count
|
|
144
|
+
FROM users u
|
|
145
|
+
LEFT JOIN orders o ON o.user_id = u.id
|
|
146
|
+
GROUP BY u.id;
|
|
147
|
+
|
|
148
|
+
-- Upsert (INSERT or UPDATE)
|
|
149
|
+
INSERT INTO user_settings (user_id, key, value)
|
|
150
|
+
VALUES (:user_id, :key, :value)
|
|
151
|
+
ON CONFLICT (user_id, key)
|
|
152
|
+
DO UPDATE SET value = EXCLUDED.value, updated_at = NOW();
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## NoSQL Patterns
|
|
156
|
+
|
|
157
|
+
### MongoDB (Document)
|
|
158
|
+
- **Embed** when data is accessed together and doesn't grow unbounded
|
|
159
|
+
- **Reference** when data is accessed independently or is large
|
|
160
|
+
- Schema validation with JSON Schema for data integrity
|
|
161
|
+
- Use compound indexes for common query patterns
|
|
162
|
+
```javascript
|
|
163
|
+
// Embed: address always accessed with user
|
|
164
|
+
{
|
|
165
|
+
_id: ObjectId("..."),
|
|
166
|
+
name: "John",
|
|
167
|
+
address: { street: "123 Main", city: "NYC" } // embedded
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Reference: orders queried independently
|
|
171
|
+
{
|
|
172
|
+
_id: ObjectId("..."),
|
|
173
|
+
user_id: ObjectId("..."), // reference to users collection
|
|
174
|
+
items: [...]
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Redis Patterns
|
|
179
|
+
| Pattern | Structure | Use Case |
|
|
180
|
+
|---------|-----------|----------|
|
|
181
|
+
| Cache | `cache:user:{id}` → JSON | Query result caching |
|
|
182
|
+
| Session | `session:{token}` → hash | User session data |
|
|
183
|
+
| Rate limit | `ratelimit:{ip}:{window}` → counter | API rate limiting |
|
|
184
|
+
| Queue | List with LPUSH/BRPOP | Simple job queue |
|
|
185
|
+
| Leaderboard | Sorted set | Rankings, top-N queries |
|
|
186
|
+
| Pub/Sub | Channel subscription | Real-time notifications |
|
|
187
|
+
|
|
188
|
+
### Redis Best Practices
|
|
189
|
+
- Set TTL on all cache keys — avoid stale data and memory bloat
|
|
190
|
+
- Use pipelining for multiple operations — reduce round trips
|
|
191
|
+
- Choose data structures wisely — sorted sets for ranking, hashes for objects
|
|
192
|
+
- Monitor memory usage — configure maxmemory and eviction policy
|
|
193
|
+
|
|
194
|
+
## ORM Patterns
|
|
195
|
+
|
|
196
|
+
### Active Record vs Data Mapper
|
|
197
|
+
|
|
198
|
+
| Pattern | How It Works | Best For |
|
|
199
|
+
|---------|-------------|----------|
|
|
200
|
+
| Active Record | Model class handles its own persistence | Simple CRUD, rapid development |
|
|
201
|
+
| Data Mapper | Separate mapper handles persistence | Complex domains, testability |
|
|
202
|
+
|
|
203
|
+
### ORM Best Practices
|
|
204
|
+
- Define relations explicitly — avoid lazy loading in production
|
|
205
|
+
- Use eager loading for known access patterns
|
|
206
|
+
- Raw queries for complex analytics — ORMs aren't for everything
|
|
207
|
+
- Database-level constraints — don't rely solely on ORM validation
|
|
208
|
+
|
|
209
|
+
### Recommended ORMs
|
|
210
|
+
| Language | ORM | Style |
|
|
211
|
+
|----------|-----|-------|
|
|
212
|
+
| PHP | Eloquent | Active Record, expressive API, Laravel-native |
|
|
213
|
+
| PHP | Doctrine | Data Mapper, enterprise, Symfony-native |
|
|
214
|
+
| TypeScript | Prisma | Schema-first, generated client |
|
|
215
|
+
| TypeScript | Drizzle | SQL-like, lightweight, type-safe |
|
|
216
|
+
| TypeScript | TypeORM | Decorator-based, Active Record/Data Mapper |
|
|
217
|
+
| Python | SQLAlchemy | Data Mapper (or Active Record with ORM) |
|
|
218
|
+
| Python | Django ORM | Active Record, tightly integrated |
|
|
219
|
+
| Go | GORM | Convention-based, struct tags |
|
|
220
|
+
| Go | sqlc | SQL-first, generates Go code |
|
|
221
|
+
| Rust | Diesel | Compile-time checked queries |
|
|
222
|
+
| Rust | SQLx | Async, compile-time checked SQL |
|
|
223
|
+
|
|
224
|
+
### Eloquent ORM Patterns (Laravel)
|
|
225
|
+
```php
|
|
226
|
+
// Expressive relationships
|
|
227
|
+
class User extends Model {
|
|
228
|
+
public function orders(): HasMany {
|
|
229
|
+
return $this->hasMany(Order::class);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
public function roles(): BelongsToMany {
|
|
233
|
+
return $this->belongsToMany(Role::class);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Scopes for reusable query logic
|
|
237
|
+
public function scopeActive(Builder $query): Builder {
|
|
238
|
+
return $query->where('status', 'active');
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Eager loading to prevent N+1
|
|
243
|
+
$users = User::with(['orders', 'roles'])->active()->paginate(25);
|
|
244
|
+
|
|
245
|
+
// Query scopes, accessors, casts for clean models
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
- Use `with()` for eager loading — always avoid N+1
|
|
249
|
+
- Use scopes for reusable query constraints
|
|
250
|
+
- Use casts for attribute type conversion (JSON, dates, enums)
|
|
251
|
+
- Use observers or model events for side effects
|
|
252
|
+
- Use `chunk()` or `lazy()` for large dataset processing
|
|
253
|
+
|
|
254
|
+
## Migration Strategies
|
|
255
|
+
|
|
256
|
+
### Best Practices
|
|
257
|
+
- One migration per change — small, focused, reversible
|
|
258
|
+
- Never edit a deployed migration — create a new one
|
|
259
|
+
- Test migrations against production-like data
|
|
260
|
+
- Separate schema changes from data migrations
|
|
261
|
+
|
|
262
|
+
### Zero-Downtime Migrations
|
|
263
|
+
```
|
|
264
|
+
1. ADD new column (nullable or with default)
|
|
265
|
+
2. DEPLOY code that writes to both old and new columns
|
|
266
|
+
3. BACKFILL data from old column to new column
|
|
267
|
+
4. DEPLOY code that reads from new column only
|
|
268
|
+
5. DROP old column
|
|
269
|
+
|
|
270
|
+
Never: Rename column, change type, or drop column in one step
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Migration Tools
|
|
274
|
+
| Tool | Language | Features |
|
|
275
|
+
|------|----------|----------|
|
|
276
|
+
| Laravel Migrations | PHP | Schema builder, rollback, seeding, `artisan migrate` |
|
|
277
|
+
| Doctrine Migrations | PHP | Diff-based, Symfony integration |
|
|
278
|
+
| Prisma Migrate | TypeScript | Schema-diff based, auto-generated |
|
|
279
|
+
| Drizzle Kit | TypeScript | Schema-diff, push/pull |
|
|
280
|
+
| Alembic | Python | Revision-based, auto-detect |
|
|
281
|
+
| golang-migrate | Go | SQL files, multiple DB drivers |
|
|
282
|
+
| Diesel CLI | Rust | Schema-diff, reversible |
|
|
283
|
+
| Flyway | Java/Any | SQL-based, versioned, widely used |
|
|
284
|
+
|
|
285
|
+
## Caching Layer
|
|
286
|
+
|
|
287
|
+
### Caching Strategies
|
|
288
|
+
| Strategy | Description | Consistency |
|
|
289
|
+
|----------|-------------|-------------|
|
|
290
|
+
| Cache-aside | App checks cache, loads from DB on miss | Application-managed |
|
|
291
|
+
| Read-through | Cache loads from DB transparently | Cache-managed |
|
|
292
|
+
| Write-through | Write to cache and DB simultaneously | Strong |
|
|
293
|
+
| Write-behind | Write to cache, async write to DB | Eventual |
|
|
294
|
+
|
|
295
|
+
### Cache Invalidation
|
|
296
|
+
```typescript
|
|
297
|
+
// Pattern: Cache-aside with TTL + event invalidation
|
|
298
|
+
async function getUser(id: string): Promise<User> {
|
|
299
|
+
const cacheKey = `user:${id}`;
|
|
300
|
+
|
|
301
|
+
// Check cache first
|
|
302
|
+
const cached = await redis.get(cacheKey);
|
|
303
|
+
if (cached) return JSON.parse(cached);
|
|
304
|
+
|
|
305
|
+
// Load from DB
|
|
306
|
+
const user = await db.user.findUnique({ where: { id } });
|
|
307
|
+
if (!user) throw new NotFoundError("User");
|
|
308
|
+
|
|
309
|
+
// Cache with TTL
|
|
310
|
+
await redis.set(cacheKey, JSON.stringify(user), "EX", 3600);
|
|
311
|
+
return user;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Invalidate on write
|
|
315
|
+
async function updateUser(id: string, data: UpdateUserDTO) {
|
|
316
|
+
const user = await db.user.update({ where: { id }, data });
|
|
317
|
+
await redis.del(`user:${id}`); // Invalidate cache
|
|
318
|
+
return user;
|
|
319
|
+
}
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### Cache Key Design
|
|
323
|
+
- Include version: `v1:user:{id}`
|
|
324
|
+
- Include context: `user:{id}:orders:page:{page}`
|
|
325
|
+
- Use hash tags for Redis Cluster: `{user:123}:profile`
|
|
326
|
+
- Set appropriate TTL based on data freshness requirements
|
|
327
|
+
|
|
328
|
+
## Connection Management
|
|
329
|
+
|
|
330
|
+
### Connection Pooling
|
|
331
|
+
- Always use connection pools — never open/close per query
|
|
332
|
+
- Size pool based on: `pool_size = (core_count * 2) + spindle_count`
|
|
333
|
+
- Monitor active/idle connections
|
|
334
|
+
- Set connection timeout and idle timeout
|
|
335
|
+
|
|
336
|
+
### Connection Pool Configuration
|
|
337
|
+
```typescript
|
|
338
|
+
// Prisma — connection pool in connection string
|
|
339
|
+
// postgresql://user:pass@host:5432/db?connection_limit=20&pool_timeout=10
|
|
340
|
+
|
|
341
|
+
// Node.js pg pool
|
|
342
|
+
const pool = new Pool({
|
|
343
|
+
max: 20, // Maximum connections
|
|
344
|
+
idleTimeoutMillis: 30000,
|
|
345
|
+
connectionTimeoutMillis: 2000,
|
|
346
|
+
});
|
|
347
|
+
```
|
|
@@ -117,6 +117,13 @@ CMD ["node", "dist/index.js"]
|
|
|
117
117
|
|
|
118
118
|
### Platform-Specific
|
|
119
119
|
|
|
120
|
+
#### Laravel-Specific Deployment
|
|
121
|
+
- **Laravel Forge** — Server provisioning and deployment for PHP (Nginx, MySQL, Redis, SSL)
|
|
122
|
+
- **Laravel Vapor** — Serverless deployment on AWS Lambda (auto-scaling, zero maintenance)
|
|
123
|
+
- **Laravel Envoyer** — Zero-downtime deployment with rollback
|
|
124
|
+
- **Laravel Cloud** — Managed Laravel hosting (PaaS)
|
|
125
|
+
- Traditional: Nginx + PHP-FPM + Supervisor (queues) + Redis + MySQL/PostgreSQL
|
|
126
|
+
|
|
120
127
|
#### AWS
|
|
121
128
|
- ECS (Elastic Container Service)
|
|
122
129
|
- EKS (Elastic Kubernetes Service)
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: design-patterns
|
|
3
|
+
description: Gang of Four patterns, enterprise patterns, functional patterns, domain-driven design, and anti-patterns
|
|
4
|
+
license: Apache-2.0
|
|
5
|
+
compatibility: opencode
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Design Patterns Skill
|
|
9
|
+
|
|
10
|
+
This skill provides guidance on applying proven software design patterns to solve recurring problems.
|
|
11
|
+
|
|
12
|
+
## When to Use
|
|
13
|
+
|
|
14
|
+
Use this skill when:
|
|
15
|
+
- Solving recurring design problems in code
|
|
16
|
+
- Refactoring code for better maintainability
|
|
17
|
+
- Reviewing code architecture and structure
|
|
18
|
+
- Implementing domain-driven design
|
|
19
|
+
- Identifying and eliminating anti-patterns
|
|
20
|
+
|
|
21
|
+
## Creational Patterns
|
|
22
|
+
|
|
23
|
+
### Singleton
|
|
24
|
+
- **Intent**: Ensure a class has only one instance with global access
|
|
25
|
+
- **Use when**: Database connections, configuration, logging
|
|
26
|
+
- **Avoid when**: It creates hidden global state or hinders testing
|
|
27
|
+
```typescript
|
|
28
|
+
class Database {
|
|
29
|
+
private static instance: Database;
|
|
30
|
+
private constructor(private connection: Connection) {}
|
|
31
|
+
|
|
32
|
+
static getInstance(): Database {
|
|
33
|
+
if (!Database.instance) {
|
|
34
|
+
Database.instance = new Database(createConnection());
|
|
35
|
+
}
|
|
36
|
+
return Database.instance;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Factory Method
|
|
42
|
+
- **Intent**: Define an interface for creating objects, let subclasses decide which class
|
|
43
|
+
- **Use when**: Object creation logic varies by context
|
|
44
|
+
- **Example**: `createNotifier("email")` returns EmailNotifier, `createNotifier("sms")` returns SMSNotifier
|
|
45
|
+
|
|
46
|
+
### Abstract Factory
|
|
47
|
+
- **Intent**: Create families of related objects without specifying concrete classes
|
|
48
|
+
- **Use when**: Multiple related objects need consistent creation (e.g., UI themes)
|
|
49
|
+
|
|
50
|
+
### Builder
|
|
51
|
+
- **Intent**: Construct complex objects step by step
|
|
52
|
+
- **Use when**: Object has many optional parameters or complex construction
|
|
53
|
+
```typescript
|
|
54
|
+
const query = new QueryBuilder()
|
|
55
|
+
.select("name", "email")
|
|
56
|
+
.from("users")
|
|
57
|
+
.where("active", true)
|
|
58
|
+
.orderBy("name")
|
|
59
|
+
.limit(10)
|
|
60
|
+
.build();
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Prototype
|
|
64
|
+
- **Intent**: Create new objects by cloning existing ones
|
|
65
|
+
- **Use when**: Object creation is expensive, need copies with slight variations
|
|
66
|
+
|
|
67
|
+
## Structural Patterns
|
|
68
|
+
|
|
69
|
+
### Adapter
|
|
70
|
+
- **Intent**: Convert one interface to another that clients expect
|
|
71
|
+
- **Use when**: Integrating third-party libraries, legacy code migration
|
|
72
|
+
```typescript
|
|
73
|
+
// Adapt old payment API to new interface
|
|
74
|
+
class StripeAdapter implements PaymentGateway {
|
|
75
|
+
constructor(private stripe: StripeSDK) {}
|
|
76
|
+
|
|
77
|
+
async charge(amount: number, currency: string): Promise<PaymentResult> {
|
|
78
|
+
const result = await this.stripe.paymentIntents.create({
|
|
79
|
+
amount: amount * 100, // Stripe uses cents
|
|
80
|
+
currency,
|
|
81
|
+
});
|
|
82
|
+
return { id: result.id, status: result.status };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Decorator
|
|
88
|
+
- **Intent**: Attach additional behavior dynamically without altering the class
|
|
89
|
+
- **Use when**: Adding logging, caching, validation to existing objects
|
|
90
|
+
- **Common in**: Middleware chains, TypeScript decorators, Python decorators
|
|
91
|
+
|
|
92
|
+
### Facade
|
|
93
|
+
- **Intent**: Provide a simplified interface to a complex subsystem
|
|
94
|
+
- **Use when**: Hiding complexity behind a clean API
|
|
95
|
+
- **Example**: A `PaymentService` that orchestrates cart, pricing, gateway, and receipt subsystems
|
|
96
|
+
|
|
97
|
+
### Proxy
|
|
98
|
+
- **Intent**: Control access to an object (lazy loading, access control, logging)
|
|
99
|
+
- **Use when**: Adding cross-cutting concerns transparently
|
|
100
|
+
|
|
101
|
+
### Composite
|
|
102
|
+
- **Intent**: Treat individual objects and compositions uniformly
|
|
103
|
+
- **Use when**: Tree structures (file systems, UI components, org charts)
|
|
104
|
+
|
|
105
|
+
## Behavioral Patterns
|
|
106
|
+
|
|
107
|
+
### Observer
|
|
108
|
+
- **Intent**: Define a one-to-many dependency — when one changes, dependents are notified
|
|
109
|
+
- **Use when**: Event systems, reactive state, pub/sub
|
|
110
|
+
```typescript
|
|
111
|
+
class EventEmitter<T extends Record<string, unknown[]>> {
|
|
112
|
+
private listeners = new Map<keyof T, Set<Function>>();
|
|
113
|
+
|
|
114
|
+
on<K extends keyof T>(event: K, handler: (...args: T[K]) => void) {
|
|
115
|
+
if (!this.listeners.has(event)) this.listeners.set(event, new Set());
|
|
116
|
+
this.listeners.get(event)!.add(handler);
|
|
117
|
+
return () => this.listeners.get(event)?.delete(handler);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
emit<K extends keyof T>(event: K, ...args: T[K]) {
|
|
121
|
+
this.listeners.get(event)?.forEach((handler) => handler(...args));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Strategy
|
|
127
|
+
- **Intent**: Define a family of algorithms, make them interchangeable
|
|
128
|
+
- **Use when**: Multiple ways to accomplish something (sorting, validation, pricing)
|
|
129
|
+
```typescript
|
|
130
|
+
interface CompressionStrategy {
|
|
131
|
+
compress(data: Buffer): Buffer;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
class FileProcessor {
|
|
135
|
+
constructor(private strategy: CompressionStrategy) {}
|
|
136
|
+
process(file: Buffer): Buffer {
|
|
137
|
+
return this.strategy.compress(file);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Command
|
|
143
|
+
- **Intent**: Encapsulate a request as an object for queuing, logging, undo
|
|
144
|
+
- **Use when**: Undo/redo, task queues, macro recording
|
|
145
|
+
|
|
146
|
+
### State
|
|
147
|
+
- **Intent**: Object behavior changes based on internal state
|
|
148
|
+
- **Use when**: Complex state machines (order status, UI modes, workflows)
|
|
149
|
+
|
|
150
|
+
### Template Method
|
|
151
|
+
- **Intent**: Define skeleton of algorithm, let subclasses fill in steps
|
|
152
|
+
- **Use when**: Common workflow with varying steps (report generation, ETL)
|
|
153
|
+
|
|
154
|
+
### Chain of Responsibility
|
|
155
|
+
- **Intent**: Pass request along a chain of handlers until one processes it
|
|
156
|
+
- **Use when**: Middleware pipelines, event bubbling, validation chains
|
|
157
|
+
|
|
158
|
+
### Iterator
|
|
159
|
+
- **Intent**: Access elements sequentially without exposing representation
|
|
160
|
+
- **Use when**: Custom collections, lazy evaluation, streaming data
|
|
161
|
+
|
|
162
|
+
## Enterprise Patterns
|
|
163
|
+
|
|
164
|
+
### Repository
|
|
165
|
+
- **Intent**: Mediate between domain and data mapping layers
|
|
166
|
+
- **Use when**: Abstracting data access from business logic
|
|
167
|
+
- **Benefit**: Testable — swap real DB for in-memory implementation
|
|
168
|
+
|
|
169
|
+
### Unit of Work
|
|
170
|
+
- **Intent**: Track changes and coordinate writing to DB as a single transaction
|
|
171
|
+
- **Use when**: Complex operations that must succeed or fail together
|
|
172
|
+
|
|
173
|
+
### Service Layer
|
|
174
|
+
- **Intent**: Define application boundary with a layer of services
|
|
175
|
+
- **Use when**: Orchestrating business operations across multiple domain objects
|
|
176
|
+
|
|
177
|
+
### Domain Model
|
|
178
|
+
- **Intent**: Object model of the domain that incorporates behavior and data
|
|
179
|
+
- **Use when**: Complex business logic that belongs in domain objects, not services
|
|
180
|
+
|
|
181
|
+
### Data Mapper
|
|
182
|
+
- **Intent**: Move data between objects and database while keeping them independent
|
|
183
|
+
- **Use when**: Domain model should be free of persistence concerns
|
|
184
|
+
|
|
185
|
+
### Data Transfer Object (DTO)
|
|
186
|
+
- **Intent**: Carry data between processes to reduce number of method calls
|
|
187
|
+
- **Use when**: API request/response shapes differ from domain models
|
|
188
|
+
|
|
189
|
+
## Functional Patterns
|
|
190
|
+
|
|
191
|
+
### Pipe / Compose
|
|
192
|
+
```typescript
|
|
193
|
+
// Function composition — chain transformations
|
|
194
|
+
const pipe = <T>(...fns: ((arg: T) => T)[]) =>
|
|
195
|
+
(value: T) => fns.reduce((acc, fn) => fn(acc), value);
|
|
196
|
+
|
|
197
|
+
const processUser = pipe(
|
|
198
|
+
validateEmail,
|
|
199
|
+
normalizeUsername,
|
|
200
|
+
hashPassword,
|
|
201
|
+
createUserRecord,
|
|
202
|
+
);
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Immutability
|
|
206
|
+
- Prefer `const`, `readonly`, `Object.freeze()`
|
|
207
|
+
- Return new objects instead of mutating — `{ ...obj, key: newValue }`
|
|
208
|
+
- Use immutable data structures for shared state
|
|
209
|
+
|
|
210
|
+
### Currying & Partial Application
|
|
211
|
+
- Transform multi-arg function into sequence of single-arg functions
|
|
212
|
+
- Create specialized functions from general ones
|
|
213
|
+
|
|
214
|
+
### Monads (Optional/Result)
|
|
215
|
+
- Wrap values in context (Maybe/Option for nullability, Result/Either for errors)
|
|
216
|
+
- Chain operations safely without null checks at each step
|
|
217
|
+
```typescript
|
|
218
|
+
// Result type for error handling without exceptions
|
|
219
|
+
type Result<T, E> = { ok: true; value: T } | { ok: false; error: E };
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Domain-Driven Design (DDD)
|
|
223
|
+
|
|
224
|
+
### Core Concepts
|
|
225
|
+
| Concept | Description |
|
|
226
|
+
|---------|-------------|
|
|
227
|
+
| Bounded Context | Explicit boundary with a specific domain model |
|
|
228
|
+
| Aggregate | Cluster of objects treated as a single unit for data changes |
|
|
229
|
+
| Entity | Object with identity that persists over time |
|
|
230
|
+
| Value Object | Immutable object defined by its attributes, no identity |
|
|
231
|
+
| Domain Event | Something meaningful that happened in the domain |
|
|
232
|
+
| Repository | Interface for accessing aggregates |
|
|
233
|
+
| Domain Service | Logic that doesn't belong to any single entity |
|
|
234
|
+
|
|
235
|
+
### Strategic Design
|
|
236
|
+
- Identify bounded contexts by business capabilities
|
|
237
|
+
- Define context maps — relationships between contexts
|
|
238
|
+
- Use ubiquitous language — domain experts and developers share vocabulary
|
|
239
|
+
- Anti-corruption layer between contexts with different models
|
|
240
|
+
|
|
241
|
+
### Tactical Design
|
|
242
|
+
- Aggregates enforce invariants — validate rules at aggregate root
|
|
243
|
+
- Entities have identity — compare by ID, not attributes
|
|
244
|
+
- Value objects are immutable — compare by value, not reference
|
|
245
|
+
- Domain events for cross-aggregate communication
|
|
246
|
+
|
|
247
|
+
## Anti-Patterns to Avoid
|
|
248
|
+
|
|
249
|
+
| Anti-Pattern | Problem | Solution |
|
|
250
|
+
|-------------|---------|----------|
|
|
251
|
+
| God Object | One class does everything | Split into focused classes (SRP) |
|
|
252
|
+
| Spaghetti Code | Tangled dependencies, no structure | Layer architecture, extract modules |
|
|
253
|
+
| Golden Hammer | Using one solution for everything | Choose tools based on problem |
|
|
254
|
+
| Premature Optimization | Optimizing before profiling | Measure first, optimize bottlenecks |
|
|
255
|
+
| Shotgun Surgery | One change requires editing many files | Consolidate related logic |
|
|
256
|
+
| Feature Envy | Method uses another class's data more than its own | Move method to the class it envies |
|
|
257
|
+
| Primitive Obsession | Using primitives instead of domain types | Create value objects (Email, Money) |
|
|
258
|
+
| Leaky Abstraction | Internal details exposed through interface | Design clean, minimal interfaces |
|
|
259
|
+
|
|
260
|
+
## Language-Specific Idioms
|
|
261
|
+
|
|
262
|
+
### PHP / Laravel
|
|
263
|
+
- **Facades** — Static-like access to services resolved from the container (`Cache::get()`, `Log::info()`)
|
|
264
|
+
- **Service Container** — Powerful DI container with auto-resolution, contextual binding
|
|
265
|
+
- **Service Providers** — Bootstrap and register application services (deferred loading)
|
|
266
|
+
- **Contracts** — Interfaces for framework services (swap implementations easily)
|
|
267
|
+
- **Pipelines** — Chain of responsibility pattern (`Pipeline::send($data)->through($pipes)`)
|
|
268
|
+
- **Observers** — Model lifecycle hooks (creating, updating, deleting events)
|
|
269
|
+
- **Policies** — Authorization logic encapsulated per model
|
|
270
|
+
- **Actions / Services** — Single-responsibility classes for business logic
|
|
271
|
+
- **Enums** — PHP 8.1+ backed enums for type-safe constants
|
|
272
|
+
- **Value Objects** — Use readonly classes (PHP 8.2+) or custom casts for domain types
|
|
273
|
+
|
|
274
|
+
### TypeScript/JavaScript
|
|
275
|
+
- Use interfaces for contracts, classes for implementation
|
|
276
|
+
- Prefer composition with mixins or higher-order functions
|
|
277
|
+
- Use discriminated unions for type-safe state variants
|
|
278
|
+
|
|
279
|
+
### Python
|
|
280
|
+
- Protocols and ABCs for interfaces
|
|
281
|
+
- Dataclasses and `__slots__` for value objects
|
|
282
|
+
- Context managers (`with`) for resource management
|
|
283
|
+
- Descriptors and metaclasses for advanced patterns
|
|
284
|
+
|
|
285
|
+
### Go
|
|
286
|
+
- Interfaces are implicit — define behavior, not structure
|
|
287
|
+
- Embed structs for composition (no inheritance)
|
|
288
|
+
- Accept interfaces, return structs
|
|
289
|
+
- Table-driven tests for pattern verification
|
|
290
|
+
|
|
291
|
+
### Rust
|
|
292
|
+
- Traits for polymorphism, generics for compile-time dispatch
|
|
293
|
+
- Enums with data for state machines (sum types)
|
|
294
|
+
- `Result<T, E>` for error handling (no exceptions)
|
|
295
|
+
- Ownership system enforces resource management patterns
|