Facade + PHP 8 🐘 (Design Patterns)

Gabriel Anhaia
Apr 24 · 9 min read

Sometimes when working on a legacy project, we have complex code that can't just be changed or refactored. I will show you one of the best options to eliminate all this complexity using the Design Pattern Facade.

About this series of articles

This article’s main goal is to teach you about this subject that I am passionate about. After writing two books about design patterns (In Brazil), I started this series of articles to share through them and help you become a more professional developer. I hope you enjoy it.

Keep it in mind: The most challenging part about learning design patterns is “when I should apply them.” My advice is to focus on the examples and try to think in different situations that you have a similar problem; it doesn’t matter which industry or type of project you work with. The Design Patterns are not related to a specific business. They are intended to solve programming problems related to algorithms’ behavior, the structure of your code, or even creating the objects on your application.

If you would like to check the examples and keep up to date about the new posts, don’t forget to follow me and to subscribe on the GitHub Repository below:

Firstly

Name: Facade, also known as façade.

Type: Structural.

Origin: from the book “Design Patterns: Elements of Reusable Object-Oriented Software.”

The hypothetical problem

As I always do in my articles, let's start from this imaginary situation. You work as a Software Engineer for a company with an Application responsible for managing products; it doesn't matter if it's for an e-commerce platform or whatever else you can imagine.
The point is. All we need to know in this example is that this application has the following responsibilities:

  • Managing products
  • Managing products stock
  • Managing products price
  • Integration with different platforms that need to know about the products, stocks, prices. (Including a marketplace to sell the products online, like Amazon, for example).

Regarding the code of this project/repository. 50% was refactored, and it is very well written, follows the best practices of software development, has a clean code, and a very well-defined architecture. But it's still the same repository. It's just an internal separation/reorganization.
However, the other 50% of the repository is legacy code, and it's a complete mess. The typical legacy project that no one wants to touch.

In short: 50% of the code was refactored, and it's good, and the other 50% is terrible.

On a beautiful Monday morning, in a sprint planning, a new task arrives. This task is related to a new flow for product creation. The requirement is to create a new Endpoint for the users to register a batch of products all at once. And you have a very tight deadline, and it should be deployed in less than two weeks.

The flow looks like that:

  1. Register the list of products
  2. Send each product for a marketplace (external integration)
  3. Send each product stock for a marketplace (external integration)
  4. Send each product price for a marketplace (external integration)
  5. Send each product update to an ERP informing that the Product is on the Marketplace.

It's a lot of code that already exists, but today it happens for individual products, and it is a complete mess. All this code that we need wasn't refactored yet.

So, basically, we have a lot of code that already does each of the items above, but we need to create a new endpoint that will process all five items.

The code for each one is extremely complex. In a perfect world, we would have enough time to refactor it. But unfortunately, we will have to use the old code in the new API/structure of code to deliver this new feature on time.

How would we do/execute all of those five steps in a way that will be easy to use for now in our new endpoint, but we can refractor soon?

The Facade Pattern

The Facade pattern is an analogy to Facade architecture (common in buildings). In the Software world, it is an object that is used as a front-facing interface responsible for abstracting the complexity behind it.

Imagine that you have parts of your application that have a high complexity to use. And for some reason, you cannot just refactor it, but you need to use all those many complex objects. You can easily apply the Facade Pattern to create a unified interface to mask them and make an entire flow easier to use.

As you can see in the diagram above, our Client (not part of the pattern) represents our application/framework is responsible for using the complex subsystem classes, we create the Facade that hides the complexity, and then, in the end, the Client knows just the Facade interface, and never calls the other classes directly.

Now we know that it does, let's see the main three reasons that make sense to use this pattern:

  1. When we have this complex sub-system, or even a library that we are importing into our application, and we want to improve the readability by hiding the complex components with a single Facade class.
  2. When we want to create a more generic functionality that groups many other smaller ones.
  3. When we are refactoring an application, and we want to create a point that hides the old code to separate the old and new logic.

In a few words, Facade is:

Provides a simplified interface.

What is the difference between Facade, Adapter, and Decorator?

When we start studying design patterns, it's widespread to look at those patterns and see similarities, but it is important to say that they have different purposes.

All of them are Structural Patterns, but they have the following differences:

Facade: Provides a simplified interface

Adapter: Converts one interface into another to match with what the client is expecting. [Adapter + PHP 8 🐘 (Design Patterns)]

Decorator: It adds responsibilities to an interface by wrapping the original code.

Solving the legacy system complexity

Let's jump into our first example and see how we can solve our problem using the Facade Pattern.

First of all, let's create the complex classes that the Facade will mask; just keep in mind that those classes are not the implementation of the pattern; they will be completely different in your own system. The idea here is to see how complex and different their interfaces are.

The examples can be found here: https://github.com/gabrielanhaia/php-design-patterns

The first class we will create is to represent an Entity of a Product in our system:

<?php

namespace App\Facade\Component;

final class Product
{
protected string $uuid;

protected string $name;

protected int $barCode;

protected int $stock = 0;

protected float $price;

protected bool $isOnMarketplace = false;

public function isOnMarketplace(): bool
{
return $this->isOnMarketplace;
}

public function setIsOnMarketplace(bool $isOnMarketplace): Product
{
$this->isOnMarketplace = $isOnMarketplace;
return $this;
}

public function getUuid(): string
{
return $this->uuid;
}

public function setUuid(string $uuid): Product
{
$this->uuid = $uuid;
return $this;
}

public function getName(): string
{
return $this->name;
}

public function setName(string $name): Product
{
$this->name = $name;
return $this;
}

public function getBarCode(): int
{
return $this->barCode;
}

public function setBarCode(int $barCode): Product
{
$this->barCode = $barCode;
return $this;
}

public function getStock(): int
{
return $this->stock;
}

public function setStock(int $stock): Product
{
$this->stock = $stock;
return $this;
}

public function getPrice(): float
{
return $this->price;
}

public function setPrice(float $price): Product
{
$this->price = $price;
return $this;
}
}

It has just the most properties, getters, and setters. It can be compared as an Entity used to be stored when using Doctrine ORM.

Another example is a Repository responsible for interacting with a database for example.

<?php

declare(strict_types=1);

namespace App\Facade\Component;

class ProductRepository
{
public function persist(Product $product): void
{
// It could insert into the database here.
echo $product->getName() . " persisted.\n";
}

public function update(Product $product): void
{
// It could update into the database here.
echo $product->getName() . " updated.\n";
}
}

It makes use of our product.

Now, let's create a Util Class to generate Bar-codes:

<?php

declare(strict_types=1);

namespace App\Facade\Component;


class BarCodeUtil
{
public static function generateBarCode()
{
/**
* This is just an example. It's not a real barcode as you can see.
*/
return rand(1000000000000, 9000000000000);
}
}

Following our requirements brought by the business side, we have the code responsible for interacting with the external API of a Marketplace to insert products, update stock and price:

<?php


namespace App\Facade\Component;

class MarketplaceIntegration
{
public function createProduct(Product $product): void
{
// It could call an external service here to create the product.
echo $product->getName() . " created on the Marketplace.\n";
}

public function insertPrice(string $productUuid, float $price): void
{
// It could call an external service here to insert price into the marketplace.
echo $productUuid . " price inserted on the Marketplace.\n";

}

public function insertStock(string $productUuid, int $stock = 0): void
{
// It could call an external service here to insert the amount of products in stock.
echo $productUuid . " stock amount inserted on the Marketplace.\n";
}
}

And last but not least, we need to send the product update to an ERP:

<?php

declare(strict_types=1);

namespace App\Facade\Component;

class IntegrationSoapErp
{
/**
* This is just an example.
*/
public function setProductOnMarketplace(Product $product, bool $isOnMarketplace)
{
// It could call an external service to inform that the product was added/removed to the Marketplace.
echo $product->getName() . " updated on the ERP system.\n";
}
}

All those classes above were created to represent our legacy code, and there is no pattern to be followed there. It shows many different, completely different interfaces and is not following the best practices or even a standard. They are just classes that do something, and they are not in a specific layer of our application. In other words, they are a complete mess 😢.

Implementing a Facade:

Let's recap the requirements in the order that they should be executed:

  1. Register the list of products
  2. Send each product for a marketplace (external integration)
  3. Send each product stock for a marketplace (external integration)
  4. Send each product price for a marketplace (external integration)
  5. Send each product update to an ERP informing that the Product is on the Marketplace.
<?php

declare(strict_types=1);

namespace App\Facade;

use App\Facade\Component\BarCodeUtil;
use App\Facade\Component\IntegrationSoapErp;
use App\Facade\Component\MarketplaceIntegration;
use App\Facade\Component\Product;
use App\Facade\Component\ProductRepository;

class ProductFlowFacade
{
private ProductRepository $productRepository;
private MarketplaceIntegration $marketplaceIntegration;
private IntegrationSoapErp $integrationSoapErp;

public function __construct(
ProductRepository $productRepository,
MarketplaceIntegration $marketplaceIntegration,
IntegrationSoapErp $integrationSoapErp
)
{
$this->productRepository = $productRepository;
$this->marketplaceIntegration = $marketplaceIntegration;
$this->integrationSoapErp = $integrationSoapErp;
}

public function createProducts(array $products): void
{
foreach ($products as $product) {
$product = (new Product)
->setBarCode(BarCodeUtil::generateBarCode())
->setName($product['name'])
->setPrice($product['price'])
->setStock($product['stock'])
->setUuid('3786ebae-4816-46a8-88ac-7aa5b530524f');

$this->productRepository->persist($product);

$this->marketplaceIntegration->createProduct($product);

$this->marketplaceIntegration->insertPrice($product->getUuid(), $product->getPrice());

$this->marketplaceIntegration->insertStock($product->getUuid(), $product->getStock());

$product->setIsOnMarketplace(true);

$this->productRepository->update($product);

$this->integrationSoapErp->setProductOnMarketplace($product, true);
}
}
}

That is how our Facade looks like. As you can see, we are grouping in a single method all the complexity behind the entire flow. We are creating instances of products, updating them, storing them, sending them to a Marketplace, and updating them into the ERP.

The complexity was hidden behind this class/method. In our client, we will end up having something as simple as:

...use App\Facade\Component\IntegrationSoapErp;
use App\Facade\Component\MarketplaceIntegration;
use App\Facade\Component\ProductRepository;
use App\Facade\ProductFlowFacade;
...

$productRepository = new ProductRepository;
$marketplaceIntegration = new MarketplaceIntegration;
$integrationSoapErp = new IntegrationSoapErp;

$productFlowFacade = new ProductFlowFacade($productRepository, $marketplaceIntegration, $integrationSoapErp);

$products = [
['name' => 'Nice Mug', 'price' => 10.5, 'stock' => 50],
['name' => 'Smartphone Model X', 'price' => 100, 'stock' => 1000],
];
$productFlowFacade->createProducts($products);

We just call the method createProducts, and the magic happens.

A final note to keep in mind is that you need to be careful when using the Facade pattern. Don't start creating complex classes, and just hide them behind a Facade. That is not the best option when designing a new system/module.

Try to have a good architecture, splitting the layers of your application, and building a clean code with the right responsibilities in a way where you don't need to use it.

MestreDev

Software Development, StartUps, and Career.

Gabriel Anhaia

Written by

Software Engineer (Billie GmbH), Freelancer, and Author. Brazil/Germany. Book Design Patterns em PHP: https://goo.gl/NNDZqe

MestreDev

MestreDev

Software Development, StartUps, and Career.

Gabriel Anhaia

Written by

Software Engineer (Billie GmbH), Freelancer, and Author. Brazil/Germany. Book Design Patterns em PHP: https://goo.gl/NNDZqe

MestreDev

MestreDev

Software Development, StartUps, and Career.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store