Accounting Service API
The accounting module provides a single aggregate-driven service implementation that enforces rich domain rules and business logic. The service exposes promise-based APIs while the neverthrow migration for the portfolio/accounting vertical is in progress (see NEVERTHROW_MIGRATION_PLAN.md).
Location: worker/services/accounting/accounting-service.ts
Implementation
AggregateAccountingService— wraps operations with domain aggregates (FundAccountingAggregate,JournalEntryAggregate) to enforce invariants and ensure transaction integrity
The service accepts an AccountingRepository instance in its constructor.
AggregateAccountingService
Provides aggregate-level validation before persisting state. Method signatures include comprehensive domain checks and business rule enforcement:
Account Management
createAccount(data: CreateAccountRequest): Promise<Account>— prevents duplicate account codes per fund.updateAccount(id: string, data: UpdateAccountRequest): Promise<Account>— loads the account aggregate and applies changes atomically.getAccountsByFundId(fundId: string): Promise<Account[]>getAccountBalance(accountId: string): Promise<AccountBalance>
Journal Entries
-
createJournalEntry(data: CreateJournalEntryRequest): Promise<JournalEntry>— routes throughFundAccountingAggregate.postJournalEntry, persisting updated account balances before storing the entry.Business Rule: All journal entries (primitives) can only be recorded on the calendar day after the last EOD date. If EOD was done for 2025-07-01, transactions can only be recorded for 2025-07-02. This validation is enforced automatically and returns a
BusinessRuleViolationerror if the date is not allowed. -
getJournalEntries(filters?: GetJournalEntriesRequest): Promise<JournalEntry[]> -
closePeriod(fundId: string, periodEndDate: Date): Promise<void>
Reporting
getTotalByAccountType(fundId: string, accountType: "asset" | "liability" | "equity" | "revenue" | "expense"): Promise<number>getNetAssetValue(fundId: string): Promise<number>getFinancialPosition(fundId: string): Promise<FinancialPosition>getAccountBalanceAsOf(accountId: string, asOfDate: Date): Promise<{ balance: number }>getTotalByAccountTypeAsOf(fundId: string, accountType: "asset" | "liability" | "equity" | "revenue" | "expense", asOfDate: Date): Promise<number>getNetAssetValueAsOf(fundId: string, asOfDate: Date): Promise<number>
Assets
calculateDepreciation(params: DepreciationParams): Promise<DepreciationResult>— posts a balanced depreciation entry via the aggregate pipeline.createAmortizableAsset(data: CreateAmortizableAssetRequest): Promise<AmortizableAsset>getAmortizableAssetsByFund(fundId: string): Promise<AmortizableAsset[]>
Implementation Notes
- The service does not return
Resultobjects yet. Continue to wrap calls intry/catchblocks when exposing these methods to tRPC until the Phase 2 migration updates the signatures. - When the migration lands, the service will adopt
Result<*, DomainError>signatures and share the domain error helpers documented in other services.
Last Validated: 2025-11-10 against commit
0342a62e