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)
·
CustomerId
·
IsCallingPaymentService (Flag set before calling
payment gateway)
·
IsPaymentSuccessful
·
IsAddressChangeSuccessful
·
IsMobileUpdateSuccessful
·
IsRequestComplete
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.
No comments:
Post a Comment