Answer To ‘How Would You Design This Adapter? (Part 2)’
This article represents my answer to a system design programming problem.
To quickly recap the salient points:
AbcPaymentGateway
, a wrapper class for calling the API of a payment gateway of hypothetical ABC Corp, uses basic username/password authentication.- As part of the business logic workflow, a call to the datastore is made to retrieve the appropriate payment gateway credentials. These are stored in the
Credentials
class which has string fields forUsername
andPassword
.Credentials
does not have a field forAccessToken
. - We are to write an adapter for the hypothetical XYZ payment gateway too. Luckily, XYZ’s API also uses basic username/password authentication. Unfortunately, it also requires an additional, rarely changing access token.
- The question was along the lines of how can we implement
XyzPaymentGateway
and need to make the fewest number of changes to our existing system?
Well, one approach we could take is to add AccessToken
to the Credentials
class:
public class Credentials { public string Username { get; } public string Password { get; } public string AccessToken { get; } public Credentials(string username, string password, string accessToken) { Username = username; Password = password; AccessToken = accessToken; } }
If we are currently storing the Username
and (encrypted!) Password
in separate columns in our database, we would be likely to add another column for AccessToken
. We would only put a value in this column when the payment gateway is XYZ; otherwise, the value would be null/empty.
So, the database would change. Data access code would change too – it would need to become aware of this new column. The high-level workflow class, PayForShoppingCartUseCase
could remain oblivious to such detail – no change needed here. However, adapter class XyzPaymentGateway
must know about the presence of AccessToken
since it initiates the calls to the XYZ API.
Using the approach of adding AccessToken
into the Credentials
class means that we must make changes in many places.
Is there a better way whereby we could make fewer modifications to our system?
I think there is.
In my opinion, conceptually, the AccessToken
is a bit like a password. Therefore, the XyzPaymentGateway
could combine and un-combine the Password
and AccessToken
as required and store those as the Password
in the database and also in our Credentials
object.
For example, if both password and access token were GUIDs then we could simply combine the two and delimit them with a pipe (|) character:
A6766E0C-0741-46C1-9810-B460A8595759 | FFA194A2-77CF-4435-A673-1C107F6F4FC8
It would be dependent on the password and access token not having pipe characters in them, but it would work. Or as an alternative, we could serialise them into a JSON string and save that in the datastore:
{ "Password": "big fat secure password", "AccessToken": "jPAdwv3/jHGC3{A6AY25WKsj3Muv:2FZ~6wjRsVx9;7Z84D5.gtABc74T3k" }
As soon as we have an encoding that the XyzPaymentGateway
knows about, it can also undo this encoding of the Password
and AccessToken
from Credential.Password
. Let’s assume the encoding is a single pipe (|) character between Password
and AccessToken
strings
. The following function will extract the Password
and AccessToken
parts:
private string[] ParseIntoPasswordAndAccessToken(string password) { return password.Split('|'); }
I think that could work. The database would not need to be changed, except for possibly enlarging the (encrypted!) Password
column size. The Credentials
class would not need to be changed. The data access would not need to be changed. The use case code would not need to be changed. The only module that would require changing would be the XyzPaymentGateway
adapter – it would need to decompose the Credentials.Password
into a Password
and AccessToken
.
These changes would be limited to the XyzPaymentGateway
adapter class. AbcPaymentGateway
only uses Username
and Password
and therefore would not need this complication.
The critical takeaway is that in this way, we only wrote new code! And yet, we extended the behaviour of the system to be able to use the XYZ Payment Gateway. There is a SOLID Principle for writing code this way: The Open-Closed Principle.
Leave a Reply
Want to join the discussion?Feel free to contribute!