Best JavaScript & TypeScript Design Patterns for Scalable Applications
Introduction
When building modern web applications, scalability and maintainability are crucial. JavaScript and TypeScript are widely used for frontend (React, Vue, Angular) and backend (Node.js) development. Design patterns provide solutions to common software development problems, helping developers write better code.
In this article, we’ll explore the most important JavaScript and TypeScript design patterns for building scalable applications.
1. Singleton Pattern
- Ensures a class has only one instance and provides a global access point.
- Useful for managing global app state, caching, and configurations.
Example in TypeScript:
class Singleton {
private static instance: Singleton;
private constructor() {}
static getInstance(): Singleton {
if (!Singleton.instance) {
Singleton.instance = new Singleton();
}
return Singleton.instance;
}
}
const instance1 = Singleton.getInstance();
const instance2 = Singleton.getInstance();
console.log(instance1 === instance2); // true
2. Factory Pattern
- Creates objects without specifying the exact class.
- Useful for handling complex object creation.
Example:
class Developer {
constructor(public name: string) {}
}
class DeveloperFactory {
static createDeveloper(type: string, name: string) {
if (type === "frontend") return new Developer(name + " (Frontend)");
if (type === "backend") return new Developer(name + " (Backend)");
return new Developer(name);
}
}
const dev1 = DeveloperFactory.createDeveloper("frontend", "Alice");
console.log(dev1);
3. Observer Pattern
- Defines a subscription model where objects get notified of changes.
- Useful for event-driven programming (e.g., Redux store updates).
Example:
class Subject {
private observers: Function[] = [];
subscribe(observer: Function) {
this.observers.push(observer);
}
notify(data: any) {
this.observers.forEach(observer => observer(data));
}
}
const subject = new Subject();
subject.subscribe(data => console.log("Observer 1:", data));
subject.notify("New event occurred!");
4. Module Pattern
- Encapsulates code and prevents global scope pollution.
Example:
const Module = (() => {
let count = 0;
return {
increment: () => count++,
getCount: () => count,
};
})();
Module.increment();
console.log(Module.getCount()); // 1
5. Strategy Pattern
- Defines a family of algorithms and lets the client choose at runtime.
- Useful for authentication strategies, payment methods, etc.
Example:
class PayPal {
pay(amount: number) {
console.log(`Paid ${amount} using PayPal`);
}
}
class CreditCard {
pay(amount: number) {
console.log(`Paid ${amount} using Credit Card`);
}
}
class PaymentContext {
constructor(private strategy: { pay: (amount: number) => void }) {}
executePayment(amount: number) {
this.strategy.pay(amount);
}
}
const payment = new PaymentContext(new PayPal());
payment.executePayment(100);
Conclusion
Design patterns improve code structure and maintainability. By mastering these patterns, you can write better, more scalable applications in JavaScript and TypeScript.
Comments
Post a Comment