What is Ledger and why does it need Idempotence?
Tags: javascript, webdev
Published: 2024-06-20 22:14:43


[PTBR Version](https://daniloab.substack.com/p/o-que-e-ledger-e-por-que-precisa)

## What is Ledger Series
1. [What is a Ledger and why you need to learn about it?](https://dev.to/woovi/what-is-ledger-and-why-does-it-need-idempotence-18n9)
2. [What is Ledger and why does it need Idempotence?](https://dev.to/woovi/what-is-ledger-and-why-does-it-need-idempotence-18n9)
3. [What is a Ledger and Why Floating Points Are Not Recommended?](https://dev.to/woovi/what-is-a-ledger-and-why-floating-points-are-not-recommended-1f4l)

In our previous [blog post](https://dev.to/woovi/what-is-a-ledger-and-why-you-need-to-learn-about-it-4d0g), we discussed what a ledger is and why it is essential to learn about it. We explored its origins, how it works, and its importance in financial institutions. Now, we will delve into a critical concept in building robust and reliable ledgers: idempotency.

## What is Idempotency?
Idempotency is the property of certain operations that can be applied multiple times without changing the result beyond the initial application. In other words, if an operation is idempotent, performing it once or multiple times has the same effect. This concept is crucial in distributed systems, APIs, and financial transactions to prevent duplicate processing.

## Why is Idempotency Important in a Ledger?
1. **Preventing Duplicate Transactions**: In a ledger, idempotency ensures that if a transaction is accidentally submitted more than once, it does not get recorded multiple times, which could lead to incorrect balances.
2. **Consistency**: Idempotency helps maintain the consistency and integrity of financial records, which is vital for audits and regulatory compliance.
3. **Error Handling**: Systems can safely retry operations without the risk of applying the same transaction multiple times, making the system more robust and fault-tolerant.
Implementing Idempotency in a Ledger
To illustrate how to implement idempotency in a ledger, we will update our previous example to include a transaction ID. This transaction ID will be used to ensure that each transaction is only recorded once.

Define the structure of the ledger in MongoDB:
```ts
{
  _id: ObjectId("60c72b2f9b1d8e4d2f507d3a"),
  date: ISODate("2023-06-13T12:00:00Z"),
  description: "Deposit",
  amount: 1000.00,
  balance: 1000.00,
  transactionId: "abc123"
}
```

Function to add a new entry to the ledger and calculate the balance with idempotency:
```ts
const { MongoClient } = require('mongodb');

async function addTransaction(description, amount, transactionId) {
  const url = 'mongodb://localhost:27017';
  const client = new MongoClient(url);
  
  try {
    await client.connect();
    const database = client.db('finance');
    const ledger = database.collection('ledger');

    // Check if the transaction already exists
    const existingTransaction = await ledger.findOne({ transactionId: transactionId });
    if (existingTransaction) {
      console.log('Transaction already exists:', existingTransaction);
      return;
    }

    // Get the last entry in the ledger
    const lastEntry = await ledger.find().sort({ date: -1 }).limit(1).toArray();
    const lastBalance = lastEntry.length > 0 ? lastEntry[0].balance : 0;

    // Calculate the new balance
    const newBalance = lastBalance + amount;

    // Create a new entry in the ledger
    const newEntry = {
      date: new Date(),
      description: description,
      amount: amount,
      balance: newBalance,
      transactionId: transactionId
    };

    // Insert the new entry into the ledger
    await ledger.insertOne(newEntry);

    console.log('Transaction successfully added:', newEntry);
  } finally {
    await client.close();
  }
}

// Example usage
addTransaction('Deposit', 500.00, 'unique-transaction-id-001');
```

## How to keep the same transactionId?
**Important**: Your system needs to ensure that the transactionId is always created as expected to ensure that the transaction is unique. How to do this?

Let's assume you have an employee payment system and you cannot double pay employees. To do this, you can have a helper model that initiates a payment, a PaymentIntent. This PaymentIntent has the following model

```ts
{
  _id: ObjectId("60c72b2f9b1d8e4d2f508d82"),
  taxID: '12345678990', // cpf do funcionário
  description: "Salário",
  amount: 3000.00,
  status: 'PENDING',
  transactionId: '456',
  companyId: '123',
}
```

Once you call the entry creation function in the ledger you can create the transactionId by combining: company id + paymentIntent transactionId. Thus, every time you process the payment for that PaymentIntent, the same transactionId will always be created, ensuring that the Ledger can identify that entry.

## Conclusion
Implementing idempotency in your ledger system is crucial for maintaining accurate and reliable financial records. By ensuring that each transaction is only recorded once, you can prevent duplicate entries and maintain the integrity of your data.

As we have seen, idempotency is not just a technical detail but a fundamental principle that helps build robust and fault-tolerant systems. In our next blog post, we will explore more advanced topics in ledger management and how to handle other challenges such as concurrency and eventual consistency.

Stay tuned for more insights into building reliable financial systems!

---

Visit us [Woovi](https://woovi.com/)!

---

Follow me on [Twitter](https://x.com/daniloab_)

If you like and want to support my work, become my [Patreon](https://www.patreon.com/daniloab)

Want to boost your career? Start now with my mentorship through the link

https://mentor.daniloassis.dev

See more at https://linktr.ee/daniloab

Photo of <a href="https://unsplash.com/pt-br/@thecreativv?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash">The Creativv</a> in <a href="https://unsplash.com/pt-br/fotografias/edificio-de-concreto-marrom-e-branco-qLEKtlHvGfo?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash">Unsplash</a>