Skip to main content

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 through FundAccountingAggregate.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 BusinessRuleViolation error 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 Result objects yet. Continue to wrap calls in try/catch blocks 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