A high-performance, multi-tenant double-entry accounting ledger microservice built with Go and gRPC.
- Double-Entry Accounting: Full implementation of double-entry bookkeeping principles
- Multi-Tenancy: Row-Level Security (RLS) at the database level for complete tenant isolation
- gRPC API: High-performance Protocol Buffer-based API
- PostgreSQL 18: Leverages advanced PostgreSQL features including:
- Row-Level Security (RLS) for multi-tenancy
- Database functions for business logic
- JSONB for flexible metadata storage
- Atomic transactions
- Microservice Architecture: Designed to be deployed as an independent service
- Comprehensive Testing: Unit tests and integration tests included
The system uses PostgreSQL 18 with the following core tables:
tenants: Multi-tenant organization dataaccount_types: Chart of account types (Asset, Liability, Equity, Revenue, Expense)currencies: Multi-currency supportaccounts: Chart of accounts for each tenantjournal_entries: Double-entry journal transactionsjournal_entry_lines: Individual debit/credit entriesaccount_balances: Denormalized balances for performance
Row-Level Security (RLS) is implemented at the database level using PostgreSQL's native RLS feature. Each connection sets app.current_tenant_id which is enforced by RLS policies, ensuring complete data isolation between tenants.
The gRPC service provides the following operations:
- Tenant Management: Create and retrieve tenants
- Account Management: Create accounts, list accounts, retrieve balances
- Journal Entries: Create double-entry transactions, list entries with filters
- Reference Data: List account types and currencies
- Go 1.25.5 or higher
- PostgreSQL 18
- buf CLI (for Protocol Buffer generation)
- Docker (optional, for running database and tests)
- Clone the repository:
git clone https://github.com/hesabFun/ledger.git
cd ledger- Install dependencies:
go mod download- Install development tools:
make install-tools- Generate Protocol Buffer code:
make protoCopy .env.example to .env and configure your environment:
cp .env.example .envConfiguration options:
SERVER_HOST: gRPC server host (default: 0.0.0.0)SERVER_PORT: gRPC server port (default: 9090)DB_HOST: PostgreSQL host (default: localhost)DB_PORT: PostgreSQL port (default: 5432)DB_USER: Database user (default: postgres)DB_PASSWORD: Database password (default: postgres)DB_NAME: Database name (default: ledger)DB_SSL_MODE: SSL mode (default: disable)DB_MAX_CONNS: Maximum database connections (default: 25)DB_MIN_CONNS: Minimum database connections (default: 5)
make rungo run cmd/server/main.gomake build
./bin/ledgermake testmake coverageIntegration tests require a running PostgreSQL instance with the schema applied:
go test -v -tags=integration ./internal/repository/The service exposes a gRPC API defined in proto/ledger/v1/ledger.proto.
grpcurl -plaintext -d '{"name": "Acme Corp"}' \
localhost:9090 ledger.v1.LedgerService/CreateTenantgrpcurl -plaintext -d '{
"tenant_id": "uuid-here",
"account_number": "1000",
"name": "Cash",
"account_type_id": 1,
"currency_code": "USD"
}' localhost:9090 ledger.v1.LedgerService/CreateAccountgrpcurl -plaintext -d '{
"tenant_id": "uuid-here",
"reference_number": "INV-001",
"description": "Sale transaction",
"entry_date": "2024-01-15T10:00:00Z",
"lines": [
{
"account_id": "debit-account-uuid",
"debit": "100.00",
"credit": "0",
"description": "Cash received"
},
{
"account_id": "credit-account-uuid",
"debit": "0",
"credit": "100.00",
"description": "Revenue"
}
]
}' localhost:9090 ledger.v1.LedgerService/CreateJournalEntry.
├── cmd/
│ └── server/ # Main application entry point
├── internal/
│ ├── config/ # Configuration management
│ ├── db/ # Database connection and utilities
│ ├── repository/ # Data access layer
│ └── service/ # gRPC service implementation
├── proto/
│ └── ledger/v1/ # Protocol Buffer definitions
├── gen/ # Generated code (gitignored)
├── db-schema/ # Database schema and migrations (submodule)
├── Makefile # Build automation
├── buf.yaml # Buf configuration
└── buf.gen.yaml # Buf code generation config
After modifying .proto files:
make protomake lintmake fmtThe service leverages PostgreSQL functions for critical operations:
create_tenant(name): Creates a new tenantcreate_account(...): Creates a new account with automatic balance initializationcreate_journal_entry(...): Creates a balanced journal entry with automatic validation and balance updates
These functions ensure data integrity and encapsulate business logic at the database level.
- Connection pooling with configurable min/max connections
- Denormalized
account_balancestable for fast balance queries - Database indexes on foreign keys and frequently queried columns
- RLS policies optimized with proper indexing
- Row-Level Security (RLS) ensures tenant data isolation
- All tenant operations require tenant context
- Database functions validate business rules
- Prepared statements prevent SQL injection
- Fork the repository
- Create a feature branch
- Make your changes
- Write tests
- Run tests and linting
- Submit a pull request
MIT License - See LICENSE file for details
For issues and questions, please open an issue on GitHub.