Available from May 2026 — Currently Interviewing
Back to Portfolio
Systems BlogRead time: 6 mins

Architecting Double-Entry Accounting in PHP

RA
Reyyan Alam
Backend Engineer @ Hello Creative IT

When building the Hello Creative IT ERP, I was tasked with the most critical module: Financials. In accounting software, there is zero room for error. A single penny lost to floating-point drift can corrupt an entire fiscal year's records.

The Zero-Tolerance Policy

The first rule of engineering financial systems: Never use floats for currency. Instead, we utilized PostgreSQL and MySQL's DECIMAL(15,2) types to ensure exact decimal precision. But the database type is only half the battle.

Atomic Double-Entry Transactions

The core of the system is the double-entry principle: every debit must have a corresponding credit. I implemented this at the service layer level, ensuring that no ledger entry could ever exist in isolation.

// Ensuring ledger atomicity via Database Transactions
DB::transaction(function () use ($invoice) {
    // 1. Record the primary transaction
    $entry = LedgerEntry::create([...]);

    // 2. Create the DEBIT record (e.g. Accounts Receivable)
    $entry->items()->create([
        'account_id' => $receivableAccount->id,
        'debit' => $invoice->total,
        'credit' => 0
    ]);

    // 3. Create the CREDIT record (e.g. Sales Revenue)
    $entry->items()->create([
        'account_id' => $revenueAccount->id,
        'debit' => 0,
        'credit' => $invoice->total
    ]);

    // Internal check: Balance must be ZERO
    if ($entry->items->sum('debit') !== $entry->items->sum('credit')) {
        throw new Exception("Ledger Imbalance Detected");
    }
});

The Ledger Pipeline

By wrapping these operations in monolithic database transactions, we guaranteed that if the credit failed, the debit would roll back instantly. This prevents partial data states where money "disappears" from the system.

Real-time Balance Sheet Generation

Generating a Balance Sheet or P&L report for a company with thousands of transactions can involve heavy computation. I designed a Materialized View pattern where ledger balances are aggregated into summary tables every time a batch is posted, allowing for sub-second report generation regardless of total data volume.

Data Integrity Summary

Precision

Strict DECIMAL data types with no floating-point drift.

Atomicity

All side-effects bound to ACID transactions.

Auditability

Immutable trails for every ledger mutation.

Building financial software taught me that the most important features are often the ones the user never sees: the integrity checks, the rollback loops, and the defensive architecture that keeps the books perfectly balanced, every single day.