Accounting Module Overview
v0.1.0Architecture, bounded contexts, and fluent gateway for the Obelawium Accounting & Financial Management suite.
Obelawium Accounting Domain Module
The Obelawium Accounting module (obelaw/ium-accounting) is an enterprise-grade, headless double-entry General Ledger and Financial Management System (FMS) built for the Obelawium (IUM) ecosystem. Designed according to Domain-Driven Design (DDD) principles, it provides mathematical zero-variance integrity, hierarchical Chart of Accounts (COA) governance, immutable Storno reversals, Maker-Checker duty segregation, multi-currency conversions, and an Anti-Corruption Layer (ACL) for cross-domain event ingestion.
Core Philosophy: Headless & Decoupled
In accordance with Obelawium’s core architectural tenets, ium-accounting is 100% headless:
- Zero UI / Presentation Bias: Contains no Blade templates, JavaScript widgets, controllers, or HTTP routing logic.
- Pure Domain Orchestration: All double-entry bookkeeping rules, balancing checks, and financial state transitions reside entirely within domain services and aggregates.
- Universal Consumption: The domain can be consumed from any presentation layer, including Laravel Blade, Filament admin panels, Laravel Nova, mobile apps, scheduled cron daemons, queue workers, or REST/GraphQL APIs.
- Fluent Gateway Access: Every capability is invoked through Obelawium’s centralized fluent domain gateway:
ium()->accounting()->[service]()->[method]()
The 9 Core Domain Services
The Accounting bounded context is partitioned into nine specialized domain services:
graph TD
IUM["ium()->accounting() Fluent Gateway"]
subgraph CoreLedger["Core Ledger Services"]
ACC["accounts()<br/>Hierarchical Chart of Accounts"]
JRN["journalEntries()<br/>Voucher Drafting, Approval & Storno"]
LDG["ledger()<br/>General Ledger, Trial Balance & Statements"]
end
subgraph Governance["Fiscal Governance Services"]
PER["fiscalPeriods()<br/>Fiscal Calendar & Hard Period Locks"]
CLS["closing()<br/>Nominal Sweep & Retained Earnings Close"]
AUD["audit()<br/>Append-Only Forensic Audit Trail"]
end
subgraph FinancialOps["Financial Operations Services"]
CUR["currencies()<br/>ISO 4217 & Daily FX Snapshots"]
CST["costCenters()<br/>Multi-Dimensional Cost Centers"]
ACL["acl()<br/>Anti-Corruption Layer Event Ingestion"]
end
IUM --> CoreLedger
IUM --> Governance
IUM --> FinancialOps
| Service Accessor | Service Class | Primary Responsibilities |
|---|---|---|
ium()->accounting()->accounts() | AccountService | Hierarchical Chart of Accounts tree, classification, leaf vs aggregator validation. |
ium()->accounting()->journalEntries() | JournalEntryService | Journal voucher drafting, zero-sum validation, Maker-Checker workflow, posting, and Storno reversals. |
ium()->accounting()->ledger() | LedgerService | General Ledger book of entry, account ledgers, Trial Balance, Balance Sheet, and Income Statement (P&L). |
ium()->accounting()->fiscalPeriods() | FiscalPeriodService | Fiscal years, monthly/quarterly periods, soft/hard locks, and date-to-period resolution. |
ium()->accounting()->costCenters() | CostCenterService | Analytical accounting dimensions (Cost Centers, Projects, Departments, Business Units). |
ium()->accounting()->closing() | ClosingService | Fiscal year-end nominal account zeroing and Retained Earnings rollover. |
ium()->accounting()->audit() | AuditService | Append-only forensic accounting audit trail for statutory compliance and regulatory reporting. |
ium()->accounting()->acl() | AclIngestionService | Anti-Corruption Layer translating upstream domain events (Billing, Inventory, CRM) into balanced vouchers. |
ium()->accounting()->currencies() | CurrencyService | Multi-Currency ISO 4217 registry, daily exchange rate snapshots, conversion, and FX formatting. |
Non-Negotiable Domain Invariants (“The Law”)
Financial ledgers must never compromise on mathematical truth. Every write operation through ium()->accounting() is guarded by strict architectural invariants:
- Zero-Sum Balance Invariant: Every voucher must satisfy $\sum \text{Debits} = \sum \text{Credits}$ down to the exact minor unit (cent/fils). Imbalanced submissions immediately throw
UnbalancedJournalEntryException. - General Ledger Immutability & Storno Reversals: Posted vouchers (
POSTED,REVERSED) are strictly immutable. Eloquent hooks prevent any in-placeupdate()ordelete(), throwingImmutableJournalEntryException. Corrections must use the Storno reversal pattern (reverse()), generating an equal-and-opposite posted voucher. - Leaf-Only Account Postings: Postings are only permitted on leaf accounts (
is_leaf = true). Aggregator parent nodes throwNonPostingAccountException. Adding a child to an account automatically converts the parent to a non-leaf aggregator. - Fiscal Period Hard Locks: Transactions must resolve to an open period (
status === OPEN). Postings into locked periods throwFiscalPeriodClosedException. - Maker-Checker Segregation of Duties: When strict governance is active, a voucher’s creator (
creator_id) cannot approve that voucher (approver_id). Violations throwMakerCheckerViolationException. - Base Functional Currency Rate Immutability: The organization’s functional currency exchange rate is locked to
1.000000. Attempting to set an exchange rate on the base currency throwsBaseCurrencyRateImmutableException. - Functional Currency General Ledger Denomination: While vouchers can be drafted in any active ISO currency, General Ledger postings (
GeneralLedgerPosting) are recorded in the functional reporting currency usingamount_functional = (int) round($amount * $exchangeRate). - Anti-Corruption Layer (ACL) Isolation: External modules (Billing, Sales, Inventory, CRM) never write directly to accounting tables. They communicate exclusively via the ACL (
ium()->accounting()->acl()->ingest(...)) or registered domain event mappings.
Quick Example: Drafting & Posting a Voucher
use Obelaw\Ium\Accounting\Data\CreateJournalEntryDto;
// 1. Prepare a balanced journal voucher
$dto = CreateJournalEntryDto::fromMajor([
'entry_date' => '2026-03-31',
'currency_code' => 'USD',
'memo' => 'Client Invoice Settlement & Sales Tax',
'lines' => [
['account_code' => '1120', 'side' => 'debit', 'amount' => 1150.00, 'memo' => 'Accounts Receivable'],
['account_code' => '4100', 'side' => 'credit', 'amount' => 1000.00, 'memo' => 'SaaS Subscription Revenue'],
['account_code' => '2150', 'side' => 'credit', 'amount' => 150.00, 'memo' => 'Sales Tax Payable'],
],
]);
// 2. Draft the voucher
$entry = ium()->accounting()->journalEntries()->createDraft($dto);
// 3. Maker-Checker approval workflow
ium()->accounting()->journalEntries()->submit($entry->id, $accountantId);
ium()->accounting()->journalEntries()->approve($entry->id, $controllerId);
$postedEntry = ium()->accounting()->journalEntries()->post($entry->id, $controllerId);
Repositories & Resources
- Package Repository: https://github.com/obelaw/ium-accounting
- AI Agent Skill Repository: https://github.com/obelaw/skill-ium-accounting-developmint
- Next Step: Read Installation & Setup to configure
ium-accountingin your Laravel application.