-1

I'm already posting this question on StackOverflow, but thought that asking it in different communities might give me different insight. So here's the repost of the question:

Hello, I'm having a financial related service that enable our users to topup their balance in our system, and then disburse the money. They'll be charged for each disbursement. The problem is, I'm having a need to do a series of step that need to be done in several places.

Related component:

  • Transaction : record of the transaction info (amount, beneficiary account, etc)
  • Payment : record of the payment info (amount, payment status)
  • Journal Entries : record the flow of money for every transaction and payment (for every movement of money in our system, we track which account act as the debit account and which as the credit account, like in accounting)

Flow of the business process:

  • Suppose there's a transaction and payment already created, next step is to confirm the transaction
  • Change transaction status to confirmed
  • Change payment status to confirmed
  • Insert new record to journal entries, with debit and credit account specified specifically for this process
  • Increment or decrement the balance of the credit and debit account

Those series of step might be done in several places, so I try to find a way to prevent other programmer that try to implement this flow forgetting a step or doing it wrong.

What I do now:

  • Create a journal entry helper. Consist of a lot of static function for each of possible business process that involve moving money between accounts. For example in this question, when confirming a payment, it moved money from user_deposit to unearned_revenue account, so user_deposit will be the debit account, and unearned_revenue will be the credit account in the newly inserted journal entry. Each of this static function only differ in telling which account will be the credit account and which will be the debit account. Beside that, this helper will also decrement or increment the balance of the credit and debit account. So in this example, it will decrement user_deposit balance and increment unearned_revenue balance. Example function name related to this example: withdrawPayment and withdrawTransaction
  • Create a payment helper. Different payment type, in different state, will need different journal entry helper. So there are a lot of static function in this helper that change the payment status, and then call the related journal entry helper. Example function name related to this example: confirmAndWithdrawCashPayment
  • Create an instance method on the transaction named like confirmAndwithdraw, which will call confirmAndWithdrawCashPayment on payment helper and withdrawTransaction on journal entry helper, which will call withdrawPayment on journal entry helper.
  • Whenever a programmer need to do this step, they just call the confirmAndwithdraw function from the transaction object

This'll work of course, but I also know that this is a very bad design. Any suggestion of a design pattern or any solution suitable for this case?

Mainly I'm looking for a way to wrap a series of steps so that I can remove those helper class while also providing a convenient way for other programmers to implement this business process.

Sorry for the long question. If the question is not clear, please do tell me so I can provide more information.

1

I think you're mixing the roles of the various classes in such a way as to not know who is responsible for what. So consequently nobody is the "owner" of any other and every movement requires necessarily dealing with all three.

The goal should be to understand the relationship between each one and designate one as the "master" and controller which you depend on to handle the details related to the other two. For instance, Payment might be a good candidate here. Payment creates and saves a Transaction for every single movement from account to account and keeps track of the Transaction's status and its own. And as each Transaction comes to a conclusion, you update the Journal entries so a log is kept.

The caller doesn't know or doesn't care about the passage from account to account. It only knows that the source account is X and the destination account is Y. The details of the movement are handled internally by Payment.

If Payment doesn't quite fit the bill, then you can always create a dedicated class for organizing these aspects. Minimize interface calls to passing only bare essentials and let this new class deal with the intricasies of the operation.

0

Just create an interface that defines this process, and is implemented as a stateless class (ie. A service). The interface can be required wherever you need it to be, and the implementation with the steps to follow can be the implementation that's injected.

You should not be repeating yourself anywhere, especially when executing important steps. Do it one place, and do it right. Anywhere in the system where that process is needed, can outsource the logic to the one place, where the logic is already defined and tested.

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.