Page MenuHomePhabricator

SmashPig Adapter/Mapper Layer
Closed, ResolvedPublic

Description

Following on from the discussions had around T330425: Refactor Dlocal API class. We agreed on the introduction of a new Adapter layer in SmashPig to handle the responsibility of specifically translating between our Payment Provider Service Layer and our API Gateway Layer

We used AI to generate a quick example of how this could work, by asking: "Show me an example of an adapter pattern between a service layer and gateway layer in PHP"

Answer: In this PHP example, we'll demonstrate an adapter pattern between a service layer and a gateway layer. We'll create a simple application that retrieves user data from two different external systems (APIs) using the adapter pattern.

Define the gateway interface and gateway classes: (Resembles our existing API layer)

1// Gateway Interface
2interface UserGateway {
3 public function getUserData($userId);
4}
5
6// Gateway Class for External System 1
7class ExternalSystem1Gateway implements UserGateway {
8 public function getUserData($userId) {
9 // Code to fetch user data from External System 1
10 // For simplicity, we return a hardcoded response
11 return [
12 'id' => $userId,
13 'name' => 'John Doe',
14 'email' => 'john@example.com'
15 ];
16 }
17}
18
19// Gateway Class for External System 2
20class ExternalSystem2Gateway implements UserGateway {
21 public function getUserData($userId) {
22 // Code to fetch user data from External System 2
23 // For simplicity, we return a hardcoded response
24 return [
25 'userId' => $userId,
26 'userName' => 'Jane Doe',
27 'userEmail' => 'jane@example.com'
28 ];
29 }
30}

Define the adapter interface and adapter classes: (We don't have this layer yet)

1// Adapter Interface
2interface UserAdapter {
3 public function getUser($userId);
4}
5
6// Adapter Class for External System 1
7class ExternalSystem1Adapter implements UserAdapter {
8 private $gateway;
9
10 public function __construct(UserGateway $gateway) {
11 $this->gateway = $gateway;
12 }
13
14 public function getUser($userId) {
15 $userData = $this->gateway->getUserData($userId);
16
17 return [
18 'id' => $userData['id'],
19 'name' => $userData['name'],
20 'email' => $userData['email']
21 ];
22 }
23}
24
25// Adapter Class for External System 2
26class ExternalSystem2Adapter implements UserAdapter {
27 private $gateway;
28
29 public function __construct(UserGateway $gateway) {
30 $this->gateway = $gateway;
31 }
32
33 public function getUser($userId) {
34 $userData = $this->gateway->getUserData($userId);
35
36 return [
37 'id' => $userData['userId'],
38 'name' => $userData['userName'],
39 'email' => $userData['userEmail']
40 ];
41 }
42}

Define the service class: (Resembles our existing PaymentProvider layer)

1// Service Class
2class UserService {
3 private $adapter;
4
5 public function __construct(UserAdapter $adapter) {
6 $this->adapter = $adapter;
7 }
8
9 public function getUser($userId) {
10 return $this->adapter->getUser($userId);
11 }
12}

Use the service class with different adapters:

1// Instantiate the gateways
2$externalSystem1Gateway = new ExternalSystem1Gateway();
3$externalSystem2Gateway = new ExternalSystem2Gateway();
4
5// Instantiate the adapters
6$externalSystem1Adapter = new ExternalSystem1Adapter($externalSystem1Gateway);
7$externalSystem2Adapter = new ExternalSystem2Adapter($externalSystem2Gateway);
8
9// Use the service with different adapters
10$userService1 = new UserService($externalSystem1Adapter);
11$userService2 = new UserService($externalSystem2Adapter);
12
13$user1 = $userService1->getUser(1);
14$user2 = $userService2->getUser(2);
15
16print_r($user1);
17print_r($user2);

In this example, the UserService class interacts with the adapter layer, which in turn communicates with the gateway layer to fetch user data from different external systems. The adapter layer normalizes the data format, allowing the service layer to remain decoupled from the specific external systems' implementations.

Event Timeline

jgleeson updated the task description. (Show Details)
AKanji-WMF subscribed.

document (ideal goal) for current sprint, test with processor currently not being worked on (e.g. Adyen, Ingenico)

XenoRyet moved this task from Sprint +1 to Next on the Fundraising-Backlog board.

Change #1018359 had a related patch set uploaded (by Jgleeson; author: Jgleeson):

[wikimedia/fundraising/SmashPig@master] WIP: SmashPig Adapter Layer POC

https://gerrit.wikimedia.org/r/1018359

jgleeson renamed this task from SmashPig Adapter Layer to SmashPig Adapter/Mapper Layer.May 23 2024, 11:56 AM

I think we can close this as it became a back-and-forth conversation instead of a piece of work. We produced an experimented patch related to this for the gravy integration here T364506: Gravy backend createPayment and https://gerrit.wikimedia.org/r/c/wikimedia/fundraising/SmashPig/+/1029267