Hi all,
I've been tinkering with Nim for a short while now and at my current job I've had the luxury of doing some learning and development. One of the tasks was to build a WebAPI using hexagonal architecture, domain driven design, and event sourcing using Kotlin — that was fun and but it got me thinking... how could I do the same in Nim? So I thought I'd give it a try and it seemed to work out well.
So here is the repo for you guys to see: https://github.com/TokenChingy/coin-ledger, let me know your thoughts.
This is not related. I've seen this pattern many times at work, and I really can't figure it out. Why is this happening? I may be missing something...
proc credit*(self: WalletDomain, walletId: UUID, transactionId: string, coins: int): Wallet =
try:
result = self.processTransaction(walletId, transactionId, CREDIT, coins)
except InsufficientCoinsException as exception:
raise exception
except NegativeCreditException as exception:
raise exception
except DuplicateTransactionException as exception:
raise exception
proc debit*(self: WalletDomain, walletId: UUID, transactionId: string, coins: int): Wallet =
try:
result = self.processTransaction(walletId, transactionId, DEBIT, coins)
except InsufficientCoinsException as exception:
raise exception
except NegativeDebitException as exception:
raise exception
except DuplicateTransactionException as exception:
raise exception
I'm assuming you're talking about splitting out the credit and debit procedures?
If that is the case, it is so we can reduce cognitive load. The other benefit is, if we want to do something different in one of the procedures, we can do so without the need to refactor an encompassing procedure.
It does mean that there is more duplicate code which could potentially create maintainability problems, but if that's the case, your domain is probably too complex and could be broken down.
Basically: Different things should have different code, even if it looks very similar to before.
Sidenote:
2 Things could drastically reduce your amount of repeated code here:
Particularly regarding 1):
Depending on how complex the assigned values you have are, you may even be able to get away with just using default assignment as I mentioned. Like so:
proc newEmptyWallet*(walletId: UUID): Wallet =
type
Wallet* = ref object
walletId*: UUID
transactionId*: string = ""
version*: int = 0
coins*: int = 0
let emptyWallet = Wallet(walletId: someUUID)
The above will output the same as your proc.
Though even more strictly speaking, in this particular case you can ignore the assignments altogether because the int and string fields will be default populated with "" and 0 anyway when you instantiate a wallet.
Thanks for the heads up! I'll have a look at it! The TransactionEvent mapping to Transaction and back was annoying for me.
I'm going to look at constructor or default assignments as writing multiple overridden procedures is tedious, and, I'm sure, quite tedious to follow.