Created
September 18, 2025 14:43
-
-
Save eladcandroid/bceed714c7b3a22f902e629913e37da6 to your computer and use it in GitHub Desktop.
Angular Facade Pattern Example - Simplifies complex subsystem interactions by providing a unified interface
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Complex subsystems | |
import { Injectable } from '@angular/core'; | |
import { HttpClient } from '@angular/common/http'; | |
import { Observable, of, throwError, BehaviorSubject } from 'rxjs'; | |
import { tap } from 'rxjs/operators'; | |
// User Model | |
export interface User { | |
id: string; | |
name: string; | |
email: string; | |
age: number; | |
} | |
// API Service | |
@Injectable() | |
export class UserApiService { | |
constructor(private http: HttpClient) {} | |
getUser(id: string): Observable<User> { | |
return this.http.get<User>(`/api/users/${id}`); | |
} | |
updateUser(id: string, data: Partial<User>): Observable<User> { | |
return this.http.patch<User>(`/api/users/${id}`, data); | |
} | |
} | |
// Cache Service | |
@Injectable() | |
export class UserCacheService { | |
private cache = new Map<string, User>(); | |
set(id: string, user: User): void { | |
this.cache.set(id, user); | |
} | |
get(id: string): User | undefined { | |
return this.cache.get(id); | |
} | |
clear(): void { | |
this.cache.clear(); | |
} | |
} | |
// Validation Service | |
@Injectable() | |
export class UserValidationService { | |
validateEmail(email: string): boolean { | |
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; | |
return emailRegex.test(email); | |
} | |
validateAge(age: number): boolean { | |
return age >= 18 && age <= 120; | |
} | |
} | |
// FACADE SERVICE - Simplifies complex subsystem interactions | |
@Injectable({ | |
providedIn: 'root' | |
}) | |
export class UserFacadeService { | |
private currentUser$ = new BehaviorSubject<User | null>(null); | |
constructor( | |
private userApi: UserApiService, | |
private userCache: UserCacheService, | |
private userValidation: UserValidationService | |
) {} | |
// Simplified interface for components | |
getCurrentUser(): Observable<User | null> { | |
return this.currentUser$.asObservable(); | |
} | |
loadUser(id: string): Observable<User> { | |
// Check cache first | |
const cachedUser = this.userCache.get(id); | |
if (cachedUser) { | |
this.currentUser$.next(cachedUser); | |
return of(cachedUser); | |
} | |
// Load from API and cache | |
return this.userApi.getUser(id).pipe( | |
tap(user => { | |
this.userCache.set(id, user); | |
this.currentUser$.next(user); | |
}) | |
); | |
} | |
updateUserProfile(id: string, email: string, age: number): Observable<User> { | |
// Validate | |
if (!this.userValidation.validateEmail(email)) { | |
return throwError(() => new Error('Invalid email format')); | |
} | |
if (!this.userValidation.validateAge(age)) { | |
return throwError(() => new Error('Invalid age')); | |
} | |
// Update | |
return this.userApi.updateUser(id, { email, age }).pipe( | |
tap(user => { | |
this.userCache.set(id, user); | |
this.currentUser$.next(user); | |
}) | |
); | |
} | |
logout(): void { | |
this.currentUser$.next(null); | |
this.userCache.clear(); | |
} | |
}// Component usage - clean and simple | |
import { Component } from '@angular/core'; | |
import { Observable } from 'rxjs'; | |
import { UserFacadeService, User } from './user-facade.service'; | |
@Component({ | |
selector: 'app-user-profile', | |
template: ` | |
<div *ngIf="user$ | async as user" class="user-profile"> | |
<h2>{{ user.name }}</h2> | |
<p>Email: {{ user.email }}</p> | |
<p>Age: {{ user.age }}</p> | |
<div class="actions"> | |
<button (click)="updateProfile()">Update Profile</button> | |
<button (click)="logout()">Logout</button> | |
</div> | |
</div> | |
`, | |
styles: [` | |
.user-profile { | |
padding: 20px; | |
border: 1px solid #ddd; | |
border-radius: 8px; | |
} | |
.actions { | |
margin-top: 20px; | |
} | |
button { | |
margin-right: 10px; | |
padding: 10px 20px; | |
background: #007bff; | |
color: white; | |
border: none; | |
border-radius: 4px; | |
cursor: pointer; | |
} | |
button:hover { | |
background: #0056b3; | |
} | |
`] | |
}) | |
export class UserProfileComponent { | |
user$: Observable<User | null> = this.userFacade.getCurrentUser(); | |
constructor(private userFacade: UserFacadeService) {} | |
ngOnInit(): void { | |
// Load user on component init | |
this.userFacade.loadUser('123').subscribe(); | |
} | |
updateProfile(): void { | |
// Simple method call - complexity is hidden in facade | |
this.userFacade.updateUserProfile('123', '[email protected]', 25) | |
.subscribe( | |
user => console.log('Profile updated:', user), | |
error => console.error('Update failed:', error) | |
); | |
} | |
logout(): void { | |
this.userFacade.logout(); | |
console.log('User logged out'); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment