Published on

Facade design pattern

Authors
multividas-thumbnail-alt

In this article, we’ll take a deep dive into Facade design pattern, exploring its key features and providing practical expls of how to use it.


Table Of Contents

  • Facade design pattern
  • How to implement Facade
  • Creating Facade class
  • Conclusion

Facade design pattern

Facade Design Pattern provides a simplified, unified interface to a complex system or set of subsystems. It hides the complexity of the system by exposing only essential methods, making it easier for clients to interact with the system.

Key features:

  • Simplifies interfaces: Offers a straightforward interface to a complex set of classes, making it easier to use.

  • Decouples clients: Reduces dependencies between the client and the complex subsystem.

  • Client has to interact with a large number of interfaces and classes in a subsystem to get result. So client gets tighly coupled with those interfaces and classes. Facade solves this problem.

  • Facade is NOT just a one to one method forwarding to other classes.

singleton-design-pattern

How to implement Facade

To implement a Facade class:

  • Identify Subsystems: Determine the complex subsystems or classes that the facade will simplify. These are typically classes with complex interactions or multiple methods.

  • Create the Facade Class: Design a facade class that provides a simplified and unified interface to these subsystems.

  • Include Subsystem Instances: In the facade class, include instances of the subsystem classes. These can be injected via the constructor or created directly within the facade.

  • Implement Simplified Methods: Add methods in the facade class that internally call the appropriate methods of the subsystem classes.

  • Expose the Facade: Use the facade class in your client code to interact with the subsystems through the simplified interface.

Creating Facade class

Create the Subsystem or Service Class:

This class handles the actual logic that will be abstracted by the facade.

php
interface ApiRepositoryInterface
{
  public function showAll(Collection $collection, int $code = 200): JsonResponse;

  public function showOne(Model $instance, int $code = 200): JsonResponse;
}

Bind the Service to the Service Container

Bind the service class to Laravel's service container via a service provider.

php
class ApiResponserServiceProvider extends ServiceProvider
{
  public function register(): void
  {
    $this->app->bind(ApiRepositoryInterface::class, function () {
      return new ApiRepository();
    });
  }
}

Now, register the service provider in config/app.php.

php
// config/app.php

'providers' => [
  // Other Service Providers
  \Multividas\ApiResponser\Providers\ApiResponserServiceProvider::class,
],

Create the Facade Class:

Create the facade class that extends Laravel's Facade class and implements the simplified interface.

php
class ApiResponser extends Facade
{
  protected static function getFacadeAccessor(): string
  {
    return ApiRepositoryInterface::class;
  }
}

Register the Facade Alias:

Optionally, add the facade alias in the config/app.php file for easy access.

php
// config/app.php
use Multividas\ApiResponser\Facades\ApiResponser as ApiResponserFacade;

'aliases' => [
  // Other Facade Aliases
  'ApiResponser' => ApiResponserFacade::class,
],

Use the Facade in Controller or Anywhere

Using the ApiResponser facade to access the methods of ApiRepositoryInterface in your controller.

php
use Multividas\ApiResponser\Facades\ApiResponser;

class PostsController
{
  public function index(): JsonResponse
  {
    return ApiResponser::showAll(Post::all());
  }

  public function show(string $postId): JsonResponse
  {
    $post = Post::find($postId);

    if (!$post instanceof Post) {
      return $this->infoResponse('Post Not Found', 404, (object)[]);
    }

    return ApiResponser::showOne($post);
  }
}

Simplified interface: The ApiResponser facade provides a clean and easy-to-use interface for interacting with the methods of ApiRepositoryInterface (like showAll() and showOne()), hiding the complexity of its underlying implementation.

Static access: With the facade, you're able to access the methods of the underlying ApiRepositoryInterface via static calls (ApiResponser::showAll(), etc.), without worrying about dependency injection or object instantiation in the controller.

Encapsulation: The facade hides the logic of how the ApiRepositoryInterface is resolved, providing a layer of abstraction.

Conclusion:

  • Subsystem: ApiRepository and ApiRepositoryInterface does the actual work (structuring and generating API responses).
  • Facade: ApiResponser facade provides a simplified interface for interacting with the ApiRepositoryInterface.
  • Client: The controller (or other parts of the app) can use the ApiResponser facade to perform tasks without directly instantiating or interacting with the service.

Additional Resources

Conclusion

As software engineers, we use design patterns daily, this improves readability and lower maintenance cost. Facade Design Pattern provides a simplified, unified interface to a complex system or set of subsystems