From software-development
Design patterns reference for TypeScript backend development — GoF patterns, DDD aggregates, and Clean Architecture layers. Loaded by backend-software-developer-agent when designing services, implementing complex business logic, or structuring application architecture.
How this skill is triggered — by the user, by Claude, or both
Slash command
/software-development:design-patternsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
**When:** Object creation logic is complex or varies by context.
When: Object creation logic is complex or varies by context.
interface Notification { send(to: string, body: string): Promise<void>; }
class EmailNotification implements Notification { /* ... */ }
class SmsNotification implements Notification { /* ... */ }
function createNotification(channel: "email" | "sms"): Notification {
const map: Record<string, () => Notification> = {
email: () => new EmailNotification(),
sms: () => new SmsNotification(),
};
return map[channel]();
}
When: Multiple interchangeable algorithms for the same operation.
interface PricingStrategy {
calculate(base: number, quantity: number): number;
}
class BulkPricing implements PricingStrategy {
calculate(base: number, qty: number) { return qty > 100 ? base * qty * 0.85 : base * qty; }
}
class OrderService {
constructor(private pricing: PricingStrategy) {}
total(base: number, qty: number) { return this.pricing.calculate(base, qty); }
}
When: Decouple side effects from core logic.
import { EventEmitter } from "node:events";
const bus = new EventEmitter();
bus.on("order.placed", (order: Order) => emailService.sendConfirmation(order));
bus.on("order.placed", (order: Order) => analyticsService.track("purchase", order));
// In OrderService:
async place(order: Order) {
const saved = await this.repo.save(order);
bus.emit("order.placed", saved);
return saved;
}
When: Add behavior to a class without modifying it.
class CachedUserRepo implements UserRepository {
constructor(private inner: UserRepository, private cache: Map<string, User>) {}
async findById(id: string): Promise<User | null> {
if (this.cache.has(id)) return this.cache.get(id)!;
const user = await this.inner.findById(id);
if (user) this.cache.set(id, user);
return user;
}
async save(user: User) { this.cache.delete(user.id); return this.inner.save(user); }
}
When: Abstract data access behind a collection-like interface.
interface Repository<T, ID = string> {
findById(id: ID): Promise<T | null>;
findAll(filter?: Partial<T>): Promise<T[]>;
save(entity: T): Promise<T>;
delete(id: ID): Promise<void>;
}
Prefer dependency injection over true singletons. In Node.js, module-level instances are effectively singletons already.
// Module-scoped instance — simple and testable when injected
export const prisma = new PrismaClient();
Avoid static getInstance() patterns — they hide dependencies and break testing.
Immutable, compared by value, no identity.
class Money {
constructor(readonly amount: number, readonly currency: string) {}
add(other: Money): Money {
if (this.currency !== other.currency) throw new Error("Currency mismatch");
return new Money(this.amount + other.amount, this.currency);
}
equals(other: Money) { return this.amount === other.amount && this.currency === other.currency; }
}
Cluster of entities with a single root that guards invariants.
class Order {
private items: OrderItem[] = [];
addItem(product: Product, qty: number) {
if (this.status !== "draft") throw new Error("Cannot modify placed order");
this.items.push(new OrderItem(product.id, product.price, qty));
}
get total(): Money { return this.items.reduce((sum, i) => sum.add(i.subtotal), new Money(0, "USD")); }
}
Record what happened; process side effects asynchronously.
interface DomainEvent { readonly occurredAt: Date; readonly type: string; }
class OrderPlaced implements DomainEvent {
readonly type = "order.placed";
constructor(readonly orderId: string, readonly occurredAt = new Date()) {}
}
Domain — Entities, Value Objects, interfaces (zero dependencies)
Application — Use cases / services (depends on Domain only)
Infrastructure — DB repos, HTTP clients, frameworks (implements Domain interfaces)
Interface — Controllers, routes, CLI (calls Application services)
Key rule: Dependencies point inward. Infrastructure implements Domain interfaces; Domain never imports Infrastructure.
npx claudepluginhub bartekck/bartek-marketplace --plugin software-developmentImplements Clean Architecture, DDD, and Hexagonal Architecture patterns in NestJS/TypeScript apps for complex backend structuring, domain layers with entities/aggregates, ports/adapters, use cases, and refactoring anemic models.
Implements Clean Architecture, Hexagonal Architecture, and Domain-Driven Design for backend systems. Use when architecting new systems or refactoring for maintainability.
Applies DDD tactical patterns using entities, value objects, aggregates, repositories, and domain events to enforce explicit invariants in domain code.