Show HN: An open-source financial ledger API written in Clojure
github.com
github.com
I have been iterating a startup since the start of the year. And I'm using Clojure for everything I do.
The first product was a ledger service, but it ended up not fitting well as a managed service. We pivoted to a different idea and decided to open-source the ledger project.
It brings years of fintech experience, with the most common problems faced at scale for a ledger solved into the design.
I hope this will be helpful to some of the companies that are struggling with this kind of system.
Let me know if you have any questions :)
This is a great presentation about this: https://youtu.be/6yqfmXiZTlM?t=1447
The link you posted is a really good resource for someone who wants to start using DynamoDB though.
You can create relationships on flat text files. It doesn't mean that you have a relational database now.
Sequence is clearly an API and I ought stick with consumer packages @ immediate needs. I'll spin up the container to check it out in general; Please let me know if I'm mistaken / you recommend specifics it'd be helpful for me to check out and feed back on -
[0] - https://en.m.wikipedia.org/wiki/List_of_cognitive_biases#Fre... [1] - https://github.com/frappe/books
Fun thing, I am currently building a similar ledger in clojure. The task is much harder then expected. My goals are very different then yours; I would like to implement a video game around a fictional financial system. Consequently, It is very important to me to implement the concept of "interest rates".
My solution is over engineered power 25. Nevertheless, you might be interested into it. It is based on datomic. https://gitlab.com/didiercrunch/skirmish/-/tree/master/src/c...
100 Eurocents equal 1€
So you do not have any rounding involved
Also if a request comes in twice there are no requestids involved. Therefore it might be possible to generate another transaction it seems.
There are great blog posts from Stripe engineers about these request ids to make requests idempotent with Postgres databases. I highly recommend reading them.
I'm working on enabling idempotent requests, should come out next week. I'm using the same pattern used by Stripe as well (big fan). An optional `x-request-id` header that will ensure only once processing if present. This is very important to enable consistency over the network with other services using the ledger.
Here is a piece I wrote about how it's achieved: https://decimals.substack.com/p/things-i-wish-i-knew-before-....
https://github.com/decimals/sequence/blob/master/src/decimal...
Each “journal” will use 2x4 WCU for the write to the table and 4 WCU for the global index updates (a total of 12 WCU for each journal).
I do have a few issues with the structure though. They seem to use LSIs to add a timestamp (instead of just using KSUID or similar for the ID), which ends up limiting each partition to a maximum of 10GB (a limit that doesn’t exist if you don’t use LSIs).
10GB might seem like a lot per account (which seems like the PK the partition is based off), but even at a conservative 200 bytes per transaction, that limits a single account to 50 million transactions forever.
Coupled with the fact this only seems to support literal double-entry accounting (not 2+ balanced transactions per journal item), you could quickly hit that limit if you are processing multi-item invoices for instance where items end up being allocated to different sales accounts.
Although if you were processing multi-item invoices you’d also have to work out how to make this transactionally-safe yourself, as this only guarantees ACID-compliance of a single DR/CR transaction.
Unfortunately, this wouldn’t necessarily be as easy as just allowing extra entries to a single DynamoDB transaction as those are limited to a max of 25 (and as covered, each entry requires 4 item updates).
DynamoDB isn’t actually a bad idea if you need an ACID-compliant database with read-after-write consistency. But I think the model needs to be rethought a bit to deal with some of the other design considerations DynamoDB forces on you.
There are definitely improvements to be made. This is the first version, I wanted to get it out and start getting feedback sooner. This is my take on each point:
For some reason Dynamodb was not sorting the partition with KSUIDs string, so I added the LSI. This is probably something I did wrong and is just matter of fixing the query or the insert and move back to using the PK.
Adding more than one balanced transaction per request is on the roadmap, but this will be limited to the DynamoDB transactions API limit of 25 items as you mentioned (25 / 2*2 WCU per tx = 6 transactions per request).
Each entry only uses 4 items in the worst case (seeding a new currency in an account). In most cases (existing balances/currencies), there will be only 2 new items.
I think even more improvements can be made. And the goal of open-sourcing is to get more heads thinking about how we can do it :)
But in a nutshell: availability and scalability. Yes you can achieve this with relational DBs, but it will be quite complex, while No-SQL gives it by design. All while ensuring all the goodness of relational data modeling, consistency and queries. The only requirement is a good table design: https://www.youtube.com/watch?v=6yqfmXiZTlM&t=1s