DEV Community

Cover image for Angular — Facade Design Pattern and how it can improve performance
V.D
V.D

Posted on

Angular — Facade Design Pattern and how it can improve performance

Introduction

As an Angular developer, you may have come across the need to simplify complex code and provide a simplified interface to other parts of the application. This is where the Facade Design Pattern comes into play. In this blog post, we will discuss the Facade Design Pattern, its implementation in Angular, and how it can improve the performance of your application.

What is the Facade Design Pattern?

The Facade Design Pattern is a structural design pattern that provides a simplified interface to a complex system of classes, functions, and APIs. It allows you to encapsulate a group of complex subsystems and expose a simplified interface to the client code.

In simple terms, a Facade is like a wrapper around a complex system that provides a simple interface for clients to interact with. The Facade hides the complexity of the system and exposes a simple API for clients to use.

Implementation in Angular

In Angular, we can implement the Facade Design Pattern using Services. Services are the backbone of Angular applications and are used to share data, logic, and functionality across multiple components. Services can also be used to encapsulate complex subsystems and provide a simplified interface to the client code.

Let’s take a look at an example of how we can implement the Facade Design Pattern using Services in Angular.

Example 1

Suppose we have a complex system of classes and functions that are responsible for handling authentication in our Angular application. The authentication system consists of the following classes and functions:

  • AuthService: Responsible for handling user authentication
  • UserService: Responsible for managing user data
  • TokenService: Responsible for managing user tokens
  • JwtHelperService: Responsible for decoding JWT tokens Our application has multiple components that require access to these classes and functions. To simplify the code and provide a simplified interface to the client code, we can create a Facade Service called AuthFacadeService.

The AuthFacadeService will encapsulate the complex authentication subsystem and provide a simplified interface to the client code. The AuthFacadeService will have the following methods:

  • login(): Handles user login
  • logout(): Handles user logout
  • getUser(): Retrieves user data
  • isAuthenticated(): Checks if the user is authenticated

Here’s how the **AuthFacadeService can be implemented:**

import { Injectable } from '@angular/core';
import { AuthService } from './auth.service';
import { UserService } from './user.service';
import { TokenService } from './token.service';
import { JwtHelperService } from './jwt-helper.service';

@Injectable({
  providedIn: 'root'
})
export class AuthFacadeService {

  constructor(
    private authService: AuthService,
    private userService: UserService,
    private tokenService: TokenService,
    private jwtHelperService: JwtHelperService
  ) { }

  login(username: string, password: string) {
    // Call AuthService to handle user authentication
    const token = this.authService.login(username, password);

    // Save token to TokenService
    this.tokenService.saveToken(token);
  }

  logout() {
    // Call AuthService to handle user logout
    this.authService.logout();

    // Remove token from TokenService
    this.tokenService.removeToken();
  }

  getUser() {
    // Call UserService to retrieve user data
    return this.userService.getUser();
  }

  isAuthenticated(): boolean {
    // Check if the user is authenticated using the JWT token
    const token = this.tokenService.getToken();
    return token && !this.jwtHelperService.isTokenExpired(token);
  }
}
Enter fullscreen mode Exit fullscreen mode

In the above code, we have created a Facade Service called AuthFacadeService that encapsulates the complex authentication subsystem. The AuthFacadeService provides a simplified interface to the client code with methods like login(), logout(), and getUser(). These methods internally use the AuthService to perform the necessary operations.

Example 2

Let’s take an example of an e-commerce application that has multiple services for handling different parts of the application, such as authentication, shopping cart, and payment. These services are tightly coupled and dependent on each other, making it difficult to maintain and improve performance. To solve this problem, we can create a facade service that exposes a simplified interface to these services.

First, let’s create the services that we want to simplify:

@Injectable()
export class AuthService {
  // Authentication logic
}

@Injectable()
export class CartService {
  // Shopping cart logic
}

@Injectable()
export class PaymentService {
  // Payment logic
}
Enter fullscreen mode Exit fullscreen mode

Now, let’s create the facade service that exposes a simplified interface to these services:

@Injectable()
export class FacadeService {
  constructor(
    private authService: AuthService,
    private cartService: CartService,
    private paymentService: PaymentService
  ) {}

  // Simplified interface to authentication
  login(username: string, password: string) {
    return this.authService.login(username, password);
  }

  // Simplified interface to shopping cart
  addToCart(item: Item) {
    return this.cartService.addToCart(item);
  }

  removeFromCart(item: Item) {
    return this.cartService.removeFromCart(item);
  }

  // Simplified interface to payment
  pay() {
    return this.paymentService.pay();
  }
}
Enter fullscreen mode Exit fullscreen mode

As you can see, the FacadeService provides a simplified interface to the complex system of services. Instead of having to interact with each service separately, the client can interact with the FacadeService and perform all the necessary actions.

Now, let’s take a look at how we can use this FacadeService in our components:

@Component({
  selector: 'app-login',
  template: `
    <form (ngSubmit)="onSubmit()">
      <input type="text" [(ngModel)]="username">
      <input type="password" [(ngModel)]="password">
      <button type="submit">Login</button>
    </form>
  `
})
export class LoginComponent {
  username: string;
  password: string;

  constructor(private facadeService: FacadeService) {}

  onSubmit() {
    this.facadeService.login(this.username, this.password)
      .subscribe(() => {
        // Redirect to dashboard
      }, (error) => {
        // Display error message
      });
  }
}

@Component({
  selector: 'app-cart',
  template: `
    <div *ngFor="let item of cartItems">
      {{ item.name }}
      <button (cllick)="addToCart(item)">Add to cart</button>
    <div> 
`
export class CartComponent {
  username: string;
  password: string;

  constructor(private facadeService: FacadeService) {}

  addToCart(item: Item) {
    this.facadeService.addToCart(item)
      .subscribe(() => {
        // Add to cart
      }, (error) => {
        // Display error message
      });
  }
}
Enter fullscreen mode Exit fullscreen mode

In the above code, we can clearly see that facade service is taking care of all the functionality related to login, adding to cart or remove from cart etc. By creating a simple interface that hides the complexity of the underlying system, we can improve the readability and testability of our code.

I hope this article has provided you with a good understanding of the Facade pattern and how to use it in Angular applications.

Overall, the facade design pattern is a powerful tool for improving the performance of your Angular applications by simplifying complex operations. By abstracting away the complexity of the underlying systems, the facade design pattern can make it easier for developers to write clean, readable code that is easier to maintain and update over time.

  • If you’re looking to improve the performance of your Angular applications, I highly recommend giving the facade design pattern a try. By simplifying complex operations and creating a clean, easy-to-use interface for client code, you can create more efficient, effective applications that deliver better results for your users.
  • By using the examples and implementation details outlined in this article, you can start implementing the Facade pattern in your Angular applications today.

Thanks for reading

Top comments (2)

Collapse
 
maxime1992 profile image
Maxime

You've articulated most of your article around the fact that the facade pattern will "improve the performance of your Angular applications".

Can you explain why it would? IMO, this pattern won't improve performances. It's a way to organise code and abstract some parts of it so that consumers aren't aware of some underlying implementations. But has nothing to do with runtime performances.

A good example I can think of is having a facade to mix data that comes from a store (ngrx for example) and data that aren't stored in it (let say a running timer for example, defined as an rxjs observable). Any component consuming the mixed data between the 2 wouldn't need to be aware of the complexity happening behind the scenes, which is nice. But at no point it'll improve the performances.

The FacadeService you wrote with AuthService, CartService, PaymentService is an anti pattern IMO. You're only putting every services in the same bag, with no additional value: no method combining multiple services, each service is already well isolatedm they're already provided as services so having a facade won't help for testing any further and now 2 separate components have access to way more than they should:

  • LoginComponent can pay, addToCart, removeFromCart
  • CartComponent can login

I think facades overall are a great pattern in some scenarios but should not always be used. If the abstraction layer in place is already enough for a simple case, using a facade will not help.

This is just my personal opinion.

Collapse
 
vivekdogra02 profile image
V.D

Hi Maxime,

Thank you for sharing your perspective and concerns regarding the use of the Facade pattern in terms of performance and code organization. You raise valid points, and I appreciate the opportunity to address them.

You are correct that the primary purpose of the Facade pattern is not to improve runtime performance but rather to provide a simplified interface and abstraction for consuming code. I apologize for any confusion caused by the mention of performance improvement. The primary benefits of the Facade pattern lie in code organization, encapsulation, and simplification of complex subsystems.

Regarding the example of mixing data from different sources, such as a store and a running timer, using a Facade can indeed provide a convenient way to abstract the complexity and present a unified interface to consumers. This abstraction can enhance code readability, maintainability, and ease of use. However, as you rightly pointed out, it does not directly impact runtime performance.

Regarding the example of the FacadeService with AuthService, CartService, and PaymentService, I understand your concerns. It's essential to carefully consider the context and requirements when applying the Facade pattern. While the Facade pattern can be useful for consolidating and simplifying access to multiple services, it should be used judiciously. In cases where the services are already well-isolated, and there are no shared operations or complex interactions between them, the use of a Facade might not provide significant additional value.

It's important to strike a balance between code organization, encapsulation, and the complexity introduced by adding a Facade.
In some cases, the existing abstraction layers provided by Angular itself, such as component composition, dependency injection, or module separation, might be sufficient. Evaluating the trade-offs and considering the specific needs of the application are crucial when deciding whether to introduce a Facade or rely on existing patterns and abstractions.

Ultimately, the choice of whether to use the Facade pattern or not depends on the specific requirements, complexity, and maintainability goals of the application. It is not a one-size-fits-all solution, and thoughtful consideration should be given to its application.

Thank you for raising these points, and I hope this response clarifies the purpose and considerations surrounding the Facade pattern in Angular development. If you have any further questions or concerns, please feel free to ask.