Following design pattern can be used to implement a long running process with multiple independent transactions.
Scenario – On the click of “Submit”, Call to the payment gateway (via PaymentService) needs to be done, Address is to be updated (AddressService) and MobileNumber is to be updated (UserProfileService).
Assumption – 1) Call to AddressService.UpdateAddress and UserProfileService.UpdateMobileNumber is idempotent.
2) Retry logic is not implemented.
Caution – Duplicate payment scenario needs to be taken care of.
Step1) On the click the “Submit”, Capture all the details like Customer Id, new address, new mobile number, accountnumber, Bank IFSC code, amount, and unique RequestId etc, create a XML message and send it to ServiceBus topic/queue via ABCService. On receiving the success message from ABCService, Show message to the end user, that “Your request is successfully recorded and the changes will be reflected shortly.”
Step2) There will be another service ABCTransactionService, which is auto-started and listening to the ServiceBus topic/queue for any incoming message. As soon as the message in Point 1 arrives, it is processed by calling the PaymentService, UserProfileService, AddressService and recording their status in a TrackingTable.
Details below -
The structure of the TrackingTable will be –
· RequestId (Primary Key)
· IsCallingPaymentService (Flag set before calling payment gateway)
Processing Detail –
a) Read message from ServiceBus topic/queue.
b) Check if IsCallingPaymentService is false or empty. (empty/false for a fresh case)
c) If IsCallingPaymentService is false or empty, Update IsCallingPaymentService to true and Call PaymentService.
d) Else Abandon the message to send it to the deadletter queue and process manually. This approach is needed to prevent duplicate call to PaymentService (deduct from Customer account twice).
e) Capture the response of PaymentService and update the IsPaymentSuccessful to true/false depending on the response code. In case of exceptions (timeout,communication failure), Update IsPaymentSuccessful to false. If the IsPaymentSuccessful is false, abandon the message (manual check required) else proceed.
UpdateAddress and UpdateMobileNumber is not critical and are idempotent. Hence we are not capturing the flag before the call is happened. The “before operation” flag is needed in the following case – Call to PaymentService is successful but Call to TrackingTable for updating flag failed (because of DBConnection!!). In this case, we might think that Call to PaymentService never happened (as per the tracking table, IsPaymentSuccessful is empty), and call it again during manual processing (unless someone checks the logs in detail).
f) Call UpdateAddress and capture the response. Update the response in TrackingTable – IsAddressChangeSuccessful. In case of any failure/exception abandon the message to move it to the deadletter queue.
g) Do the same for UpdateMobileNumber operation.
h) Update the IsRequestComplete to true and de-queue the message. (receivecontext.complete)
FAILURE SCENARIOS –
1) Go to Deadletter queue to see if there are issues.
2) Check the RequestId and find the values against that requestid in the TrackingTable.
3) Check the values of IsPaymentSuccessful=true and IsCallingPaymentService=true to see if the payment was successful. Check logs if payment failed.
4) To reprocess the message, Open ServiceBusExplorer and go the Deadletter queue and resubmit the message to the required queue/topic.
5) We will be safe to reprocess the message as Call to PaymentService is will not go (if already called) – Manual check of logs required in case of Payment failure. Other operation can be called anyway because of idempotency.