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.
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:
- Subsystem Services: Separate services for users, orders, and inventory.
- Remote Facade: A class that acts as a single entry point for the client.
- 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.