Remote Facade Design Pattern

The Remote Facade design pattern is used to reduce the number of remote calls between the client and the server. It acts as a single entry point to the underlying subsystem, offering coarse-grained methods that aggregate and manage the details of more complex operations. This pattern helps minimize latency issues caused by multiple network requests by wrapping complex processes inside a unified interface.

In PHP, the Remote Facade pattern is particularly useful in cases where the server provides a RESTful API or web services to a client.

facade

How It Works

  • The Facade class simplifies the interaction with various subsystems or services.
  • The client makes a single call to the Facade instead of multiple fine-grained calls.
  • The Facade coordinates with underlying services, performs any necessary data processing, and returns a simpler response to the client.

Implementation

Let's create a detailed example of the Remote Facade pattern using PHP, simulating a real-world e-commerce system with a REST API. This example will cover:

  1. Subsystem Services: Separate services for users, orders, and inventory.
  2. Remote Facade: A class that acts as a single entry point for the client.
  3. Client Request: Simulating how a client (like a frontend or mobile app) interacts with the API.

Folder Structure

ecommerce-app/
│── public/                  # Public-facing directory
│   ├── index.php            # Main entry point
│
│── src/                     # Core application logic
│   ├── Services/            # Subsystems (fine-grained remote services)
│   │   ├── UserService.php
│   │   ├── OrderService.php
│   │   ├── InventoryService.php
│   │
│   ├── Facade/              # Remote Facade
│   │   ├── ECommerceFacade.php
│   │
│   ├── Config/              # Configuration files (optional)
│   │   ├── config.php
│
│── api/                     # API layer (if exposing via REST)
│   ├── dashboard.php        # API endpoint using the facade
│
│── tests/                   # Unit tests for services and facade
│
│── vendor/                  # Composer dependencies (if used)
│
│── composer.json            # PHP dependency manager file

Setting Up Autoloading

To make namespaces work, configure Composer.

{
    "autoload": {
        "psr-4": {
            "Services\\": "src/Services/",
            "Facade\\": "src/Facade/"
        }
    }
}
composer dump-autoload

1️⃣ Implementing the Folder Structure

1.1 - src/Services/ (Subsystem Services)

Each service simulates a remote API or database call.

src/Services/UserService.php

namespace Services;

class UserService {
    /**
     * Get user information by user ID.
     *
     * @param int $userId
     * @return array
     */
    public function getUserInfo(int $userId): array {
        // Simulate data fetched from a database or external API
        return [
            'id' => $userId,
            'name' => 'Alice Johnson',
            'email' => '[email protected]',
            'loyaltyPoints' => 120
        ];
    }
}

src/Services/OrderService.php

namespace Services;

class OrderService {
    /**
     * Get the user's most recent orders.
     *
     * @param int $userId
     * @return array
     */
    public function getRecentOrders(int $userId): array {
        // Simulate order data (e.g., from a database or API)
        return [
            [
                'orderId' => 101,
                'status' => 'Delivered',
                'totalAmount' => 250.50
            ],
            [
                'orderId' => 102,
                'status' => 'Pending',
                'totalAmount' => 89.99
            ]
        ];
    }
}

src/Services/InventoryService.php

namespace Services;

class InventoryService {
    /**
     * Check the stock level of a given product.
     *
     * @param int $productId
     * @return array
     */
    public function checkStock(int $productId): array {
        // Simulate stock information
        return [
            'productId' => $productId,
            'stockLevel' => 15,
            'reorderThreshold' => 5
        ];
    }
}

1.2 - src/Facade/(Remote Facade)

This ECommerceFacade.php aggregates responses from multiple services.

src/Facade/ECommerceFacade.php

namespace Facade;

use Services\UserService;
use Services\OrderService;
use Services\InventoryService;

/**
 * ECommerceFacade simplifies interactions with multiple services.
 * This is the Remote Facade entry point for client applications.
 */
class ECommerceFacade {
    private UserService $userService;
    private OrderService $orderService;
    private InventoryService $inventoryService;

    /**
     * Constructor initializes the service objects.
     */
    public function __construct() {
        $this->userService = new UserService();
        $this->orderService = new OrderService();
        $this->inventoryService = new InventoryService();
    }

    /**
     * Get dashboard data for a user, including user info,
     * recent orders, and stock info for a selected product.
     *
     * @param int $userId
     * @param int $productId
     * @return array
     */
    public function getDashboardData(int $userId, int $productId): array {
        // Retrieve user information
        $userInfo = $this->userService->getUserInfo($userId);

        // Retrieve user's recent orders
        $recentOrders = $this->orderService->getRecentOrders($userId);

        // Retrieve stock information for a specific product
        $productStock = $this->inventoryService->checkStock($productId);

        // Aggregate and return the data to the client
        return [
            'user' => $userInfo,
            'recentOrders' => $recentOrders,
            'productStock' => $productStock
        ];
    }
}

2️⃣ Exposing as an API

If you want to use this in an API, create an api/dashboard.php file.

api/dashboard.php

require_once __DIR__ . '/../vendor/autoload.php';

use Facade\ECommerceFacade;

// Set response type to JSON
header('Content-Type: application/json');

// Retrieve query parameters (with defaults)
$userId = $_GET['userId'] ?? 1;
$productId = $_GET['productId'] ?? 2001;

// Create the facade instance
$facade = new ECommerceFacade();

// Get the dashboard data using the facade
$data = $facade->getDashboardData((int) $userId, (int) $productId);

// Output the data as JSON
echo json_encode($data, JSON_PRETTY_PRINT);

📌 To test the API, start a local PHP server and visit:

http://localhost:8000/api/dashboard.php?userId=1&productId=2001
{
  "user": {
    "id": 1,
    "name": "Alice Johnson",
    "email": "[email protected]",
    "loyaltyPoints": 120
  },
  "recentOrders": [
    {
      "orderId": 101,
      "status": "Delivered",
      "totalAmount": 250.5
    },
    {
      "orderId": 102,
      "status": "Pending",
      "totalAmount": 89.99
    }
  ],
  "productStock": {
    "productId": 2001,
    "stockLevel": 15,
    "reorderThreshold": 5
  }
}

3️⃣ Running the Application

If you don’t want an API and just want to test the facade, use public/index.php:

public/index.php

require_once __DIR__ . '/../vendor/autoload.php';

use Facade\ECommerceFacade;

// Create the facade instance
$facade = new ECommerceFacade();

// Fetch dashboard data
$data = $facade->getDashboardData(1, 2001);

// Print results to browser (for testing purposes)
echo "";
print_r($data);
echo "

Start a local server:

php -S localhost:8000 -t public
Array
(
    [user] => Array
        (
            [id] => 1
            [name] => Alice Johnson
            [email] => [email protected]
            [loyaltyPoints] => 120
        )

    [recentOrders] => Array
        (
            [0] => Array
                (
                    [orderId] => 101
                    [status] => Delivered
                    [totalAmount] => 250.5
                )

            [1] => Array
                (
                    [orderId] => 102
                    [status] => Pending
                    [totalAmount] => 89.99
                )

        )

    [productStock] => Array
        (
            [productId] => 2001
            [stockLevel] => 15
            [reorderThreshold] => 5
        )

)

Why Use Remote Facade?

Reduces multiple API calls → Instead of calling three services separately, the client makes a single request.
Encapsulates complex logic → The client does not need to worry about how the data is fetched or aggregated.
Improves performance → Reduces network latency by combining responses.
Enhances maintainability → Changes to services do not affect the client, as long as the facade interface remains stable.

When Should You Use This Pattern?

🔹 When you have a distributed system with multiple microservices.
🔹 When your client should not directly interact with complex subsystems.
🔹 When you want to minimize network requests and improve performance.