📐 Angular Design Patterns (Part 2): Factory, Facade, Proxy & Dependency Injection

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!

Leave a Comment

Your email address will not be published. Required fields are marked *