Skip to main content

ADR: GL Engine Architecture

Status: Accepted Date: 2026-02-19 Decision Makers: Hal Casteel (Lead Architect)

Context

Avivatec's FP&A platform requires a production-grade General Ledger engine that:

  • Enforces double-entry integrity at the database level
  • Complies with Brazilian SPED ECD export requirements (IN RFB 2.003/2021)
  • Supports multi-tenancy via PostgreSQL Row-Level Security
  • Provides multi-currency support with revaluation
  • Includes AI-native hooks (NLQ, anomaly detection, account mapping)

Decision

Build a self-contained GL Engine as a TypeScript service layer over PostgreSQL 16+, using database triggers for business rule enforcement rather than application-level validation alone.

Key Architectural Choices

DecisionChoiceRationale
LanguageTypeScript (Node.js 20+)Type safety, team familiarity, Avivatec stack alignment
DatabasePostgreSQL 16+RLS, triggers, JSONB, partitioning, mature ecosystem
Multi-tenancyRow-Level Security (RLS)Database-enforced isolation, no application-level filtering bugs
Double-entryStatement-level triggerAll lines validated atomically in a single INSERT
ImmutabilityAudit log triggers + UPDATE/DELETE preventionRegulatory compliance, tamper-proof trail
Period controlState machine trigger (FUTURE->OPEN->CLOSED->LOCKED)Prevents posting to wrong periods at DB level
SPED exportISO-8859-1, pipe-delimited, SHA-256 hashPer Brazilian federal requirements
Event systemNATS JetStream (with in-memory fallback)Decoupled event publishing for downstream consumers
TestingVitest with forked poolTenant isolation between parallel test suites

Architecture Layers

GL-PROTOTYPE/
src/
types/ # Type definitions (gl.types.ts, errors.ts, sped.types.ts)
services/ # Business logic (account, journal, period, balance, currency, sped, audit, ai, report)
events/ # Event publisher (NATS / in-memory)
schema/
migrations/ # DDL (tables, indexes, constraints)
triggers/ # PL/pgSQL triggers
rls/ # Row-Level Security policies
functions/ # Stored functions (verify_balances, carry_forward, recompute, trial_balance)
seed/ # Sample Brazilian Chart of Accounts
tests/
integration/ # 8 test suites, 37 scenarios

Database Schema (8 Tables)

TablePurposeRLSAudit
gl_accountsChart of AccountsYesYes
gl_fiscal_periodsPeriod state machineYesYes
gl_journal_entriesJournal headersYesYes
gl_journal_linesJournal line itemsYesYes
gl_account_balancesPeriod balances (materialized)YesYes
gl_exchange_ratesFX rate time-seriesYesNo
gl_audit_logImmutable audit trail (partitioned)YesN/A
gl_sped_exportsSPED export recordsYesNo

Trigger Enforcement

TriggerLevelPurpose
fn_enforce_double_entrySTATEMENT (AFTER INSERT on gl_journal_lines)Validates debits = credits for the entire entry
fn_prevent_posted_entry_modificationROW (BEFORE UPDATE on gl_journal_entries)Blocks changes to POSTED entries except status transitions
fn_check_period_on_postROW (BEFORE UPDATE on gl_journal_entries)Ensures period is OPEN when posting
fn_period_state_machineROW (BEFORE UPDATE on gl_fiscal_periods)Enforces FUTURE->OPEN->CLOSED->LOCKED transitions
fn_audit_logROW (AFTER INSERT/UPDATE on gl_* tables)Records all changes to immutable audit log
fn_prevent_audit_modificationROW (BEFORE UPDATE/DELETE on gl_audit_log)Makes audit log tamper-proof

Consequences

Positive

  • Double-entry integrity guaranteed at database level regardless of application bugs
  • Multi-tenancy isolation impossible to bypass from application code
  • SPED compliance built into the export pipeline with pre-validation
  • Audit trail is immutable and complete
  • 37 integration tests cover all critical paths

Negative

  • Statement-level triggers require batch INSERT (cannot insert lines one-at-a-time)
  • RLS requires SET LOCAL within transactions for every query
  • PostgreSQL-specific (not portable to other databases)

Risks

  • Performance under high-volume: mitigated by partitioned audit log and indexed queries
  • Currency revaluation complexity: simplified in prototype, needs production hardening

Test Coverage

SuiteTestsCoverage
Double-entry integrity7Balanced, unbalanced, 0-line, 10-line, rounding, concurrent
Period control8State transitions, posting to each state
Balance verification3100-entry verify, corruption detect, close
Audit trail5Post audit, UPDATE/DELETE blocked, reversal chain, period close
Multi-tenancy3Account isolation, cross-tenant block, audit isolation
Idempotency2Duplicate ref reject, different refs allowed
SPED export5Balanced export, unmapped fail, pipe format, encoding, hash
Multi-currency4Rate time-series, revaluation, most-recent rate, missing rate
Total37All passing