
In Part 1 of this series, we covered some of Angular’s foundational design patterns like Singleton, Observer, and Decorator.
Now, let’s take it up a notch.
In this follow-up, we’ll explore four more essential patterns: Factory, Facade, Proxy, and Dependency Injection. These patterns play a critical role in writing Angular applications that are modular, scalable, and cleanly architected.
🏭 1. Factory Pattern
🔍 What It Does:
The Factory Pattern creates objects without having to specify their exact class—making your code more dynamic and configurable.
💡 How Angular Uses It:
You’ll commonly see this with the useFactory
provider in @NgModule
.
✅ Example:
tsCopyEditexport function configFactory() {
return new ConfigService({ apiEndpoint: 'https://api.example.com' });
}
@NgModule({
providers: [
{ provide: ConfigService, useFactory: configFactory }
],
})
export class AppModule {}
This is especially helpful when your service needs dynamic setup or configuration at runtime.
🧰 2. Facade Pattern
🔍 What It Does:
The Facade Pattern provides a simplified interface to a more complex system—great for abstracting and centralizing logic.
💡 How Angular Uses It:
A common use case is creating facades for state management or service orchestration.
✅ Example:
@Injectable({ providedIn: 'root' })
export class UserFacade {
constructor(private userService: UserService, private authService: AuthService) {}
getUserDetails(userId: string) {
return this.authService.isLoggedIn()
? this.userService.getUserById(userId)
: throwError(() => new Error('Not logged in'));
}
}
This keeps your components clean by moving business logic to the facade layer.
🪞 3. Proxy Pattern
🔍 What It Does:
The Proxy Pattern acts as a substitute or placeholder for another object—often used for caching, access control, or lazy loading.
💡 How Angular Uses It:
You might build a proxy service to reduce redundant API calls by caching results.
✅ Example:
@Injectable({ providedIn: 'root' })
export class UserServiceProxy {
private cache = new Map<string, User>();
constructor(private userService: UserService) {}
getUser(userId: string): Observable<User> {
return this.cache.has(userId)
? of(this.cache.get(userId)!)
: this.userService.getUserById(userId).pipe(
tap(user => this.cache.set(userId, user))
);
}
}
This pattern helps optimize performance by avoiding unnecessary backend requests.
🧪 4. Dependency Injection (DI) Pattern
🔍 What It Does:
Dependency Injection decouples the creation of dependencies from their usage—making your code easier to maintain, mock, and test.
💡 How Angular Uses It:
Angular’s DI system is at the core of how components, directives, and services interact.
✅ Example:
@Injectable({ providedIn: 'root' })
export class ApiService {
getData(): Observable<string[]> {
return of(['Item 1', 'Item 2']);
}
}
@Component({ /* ... */ })
export class DashboardComponent {
constructor(private apiService: ApiService) {}
ngOnInit() {
this.apiService.getData().subscribe(data => {
console.log(data);
});
}
}
With DI, services are injected automatically, keeping your code clean and testable.
🧠 Wrapping Up the Series
So far, we’ve covered seven key Angular design patterns:
✅ Singleton
✅ Observer
✅ Decorator
✅ Factory
✅ Facade
✅ Proxy
✅ Dependency Injection
Mastering these patterns will make your Angular apps more modular, efficient, and easy to scale—especially as complexity grows.
📌 Save this post as a quick reference for your next project.
💬 Got a pattern you use all the time that I missed? Drop it in the comments—I’d love to hear your take!