Journal of CashAccount Microservice, day 5

Donnie Z
3 min readJun 7, 2024

--

Continuing from previous article, the CashAccount application still using “programmer’s approach” inside the code logics. Today we will use accounting terminology in our application.

While it is okay to keep insists using “programmer’s approach” on the code level, to improve the overall clarity and standardization, it is preferably to switch to use finance and accounting terms as much as possible, including on the code. This will improve communication quality, minimizing knowledge gap between business users, including subject-matter-expert, to programmers and other IT person.

Practical Debit-Credit

On the code level, I refactor all “fromAccount” and “toAccount” terms and replace it with “debitAccount” and “creditAccount”, which naturally make sense because on Saving account, the source of fund or the “fromAccount” will be decreased. And because it is sitting on Liability side of balance sheet, to decrease the balance we perform debit operation. Or so as our friendly accountant from Finance Dept explained.

Refactor “fromAccount” into “debitAccount”

And then we replicate the changes into all of our codes.

Java Money

The balance and transaction amount type currently use BigDecimal. I decided to use Java Money instead. There is no fundamental reason to do this. It is common for applications to use BigDecimal or other Numeric types for this purpose.

Import necessary libraries into pom.xml:

  <dependency>
<groupId>org.javamoney</groupId>
<artifactId>moneta</artifactId>
<version>${moneta.version}</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>io.hypersistence</groupId>
<artifactId>hypersistence-utils-hibernate-63</artifactId>
<version>${hypersistence-utils.version}</version>
</dependency>

Add annotations into Entities with MonetaryAmount type, for example on CashAccount entity:

@AttributeOverride(name = "amount", column = @Column(name = "acc_bal"))
@AttributeOverride(name = "currency", column = @Column(name = "acc_ccy"))
@CompositeType(MonetaryAmountType.class)
private MonetaryAmount accountBalance;

Create AccountTransaction Service

Next we adopt the Debit Credit transaction into Create AccountTransactionService, now the balance calculation is done by a new API that perform addition or subtraction of the account balance based on Debit or Credit and the Account’s Balance Sheet side.

// make sure balance is positive after transaction (configurable via AccountType.minimumBalance)
var debitAccountNewBalance = CashAccountUtilities.debit(debitAccount.getAccountBalance(), accountTransaction.getTransactionAmount(), debitAccount.getAccountType().getBalanceSheetEntry());
if (null != debitAccountType.getMinimumBalance() && debitAccountType.getMinimumBalance().compareTo(debitAccountNewBalance) > 0)
throw new RuntimeException("Transaction amount exceed available balance - debit");

var creditAccountNewBalance = CashAccountUtilities.credit(creditAccount.getAccountBalance(), accountTransaction.getTransactionAmount(), creditAccount.getAccountType().getBalanceSheetEntry());
if (null != creditAccountType.getMinimumBalance() && creditAccountType.getMinimumBalance().compareTo(creditAccountNewBalance) > 0)
throw new RuntimeException("Transaction amount exceed available balance - credit");

Now both Debit and Credit side has minimum balance check because we both side might increase or decrease the balance.

Also we can check applicability of currency during transaction. I use minimumBalance to store the allowed currency of an account type. This may be not ideal for those account types that does not have minimum balance amount, but lets address it later.

// no currency conversion for now, so currency must match
if (!accountTransaction.getTransactionAmount().getCurrency().equals(debitAccountType.getMinimumBalance().getCurrency())
|| !accountTransaction.getTransactionAmount().getCurrency().equals(creditAccountType.getMinimumBalance().getCurrency())) {
throw new RuntimeException("Invalid combination of currencies");
}

A Little Adoption on Create Account

When creating a new account, we set the balance amount to zero, and apply currency from acount type minimum balance currency.

On CashAccountController.create() I get the cached Zero MonetaryAmount:

cashAccount.setAccountBalance(MA.ZERO_THROUGH_TEN_PER_CCY
.get(accountType.getMinimumBalance().getCurrency())[0]);

Conclusion

Now CashAccount application use Java Money with proper Debit Credit operations. Our developer cannot wait to learn double ledger accounting now, like it or not.

References

  1. Source code can be found on donniexyz/med-spring-boot-demo tag article/med-5
  2. MonetaryAmount JPA Hibernate from vladmihalcea

--

--