Skip to main content

Backend Services Overview

Asset360 v3 backend services are organized using Domain-Driven Design (DDD) principles with clean separation between domain logic, application services, and infrastructure concerns. All services have been migrated to use neverthrow Result<T, DomainError> patterns for explicit error handling.

Architecture Overview

Service Categories

  1. Shared Services (@shared/) - Cross-cutting utilities and types
  2. Domain Services - Core business logic and entity management
  3. Application Services - Use cases and orchestration logic
  4. Infrastructure Services - External integrations and technical concerns

Error Handling Pattern

All services use the neverthrow Result<T, DomainError> pattern:

import { Result, ok, err } from "neverthrow";
import type { DomainError } from "@shared/domain-errors";

// Instead of throwing exceptions
function doWork(): Result<Data, DomainError> {
if (invalid) {
return err(validationError("Invalid input", "field"));
}
return ok(data);
}

// Handle results explicitly
const result = await service.doWork();
if (result.isOk()) {
// Success path
const data = result.value;
} else {
// Error handling
const error = result.error;
logger.error("Operation failed", { error: formatError(error) });
}

Service Directory

Shared Services

ServiceDescriptionStatus
Domain ErrorsComprehensive error type system with discriminated unions✅ Complete
LoggerStructured logging with Cloudflare Workers compatibility✅ Complete
Financial ConstantsMoney handling, account codes, and financial calculations✅ Complete
ID GenerationUnique identifier generation and validation✅ Complete

Core Domain Services

Portfolio Management

ServiceDescriptionNeverthrow StatusLocation
Equity PortfolioEquity registrations, transactions, and daily snapshots✅ Migratedworker/services/portfolio/equity/
Bond PortfolioBond registrations, pricing, and accrued interest calculations✅ Migratedworker/services/portfolio/bond/

Financial Services

ServiceDescriptionNeverthrow StatusLocation
AccountingDouble-entry bookkeeping, journal entries, and account balances✅ Migratedworker/services/accounting/
Bank AccountCash management, interest calculation, and reconciliation✅ Migratedworker/services/bank-account/
FDR (Fixed Deposits)Fixed deposit management and interest accrual✅ Migratedworker/services/fdr/

Entity Management

ServiceDescriptionNeverthrow StatusLocation
FundFund creation, management, and configuration✅ Migratedworker/services/fund/
InvestorInvestor registration and management✅ Migratedworker/services/investor/
OrganizationOrganization management and tenant isolation✅ Migratedworker/services/organization/

Integration Services

ServiceDescriptionNeverthrow StatusLocation
Market DataDSE API integration for securities and pricing data✅ Migratedworker/services/market-data/
AuthenticationUser authentication and session management✅ Migratedworker/services/auth/
Unrealized Gain/LossMark-to-market calculations for portfolio securities✅ Migratedworker/services/accounting/

Transaction Services

ServiceDescriptionNeverthrow StatusLocation
Unit TransactionUnit purchase and surrender transaction queries✅ Migratedworker/services/unit-transaction/

Migration Status

✅ Fully Migrated (Neverthrow)

These services use Result<T, DomainError> for all public methods:

  • Domain Errors: Complete error type system
  • Equity Portfolio: All methods return Results
  • Bond Portfolio: All methods return Results (complex accrued interest calculations)
  • Accounting: Full neverthrow integration
  • Bank Account: Complete migration
  • FDR: Full migration
  • Fund: Complete migration
  • Investor: Complete migration
  • Organization: Complete migration
  • Market Data: Complete migration
  • Authentication: Complete migration
  • Unrealized Gain/Loss: Complete migration
  • Unit Transaction: Complete migration with authorization checks

🎉 Migration Complete!

All backend services have been successfully migrated to neverthrow patterns with comprehensive error handling using Result<T, DomainError>.

Service Composition Patterns

Dependency Injection

Services use constructor injection for dependencies:

export class AccountingService implements IAccountingService {
constructor(
private accountRepository: IAccountRepository,
private journalEntryRepository: IJournalEntryRepository,
private logger: Logger = createLogger({ service: "accounting" }),
) {}

createJournalEntry(
request: CreateJournalEntryRequest,
): Promise<Result<JournalEntry, DomainError>> {
this.logger.info("Creating journal entry", { fundId: request.fundId });
// Implementation...
}
}

Service Factory

The factory.ts provides centralized service creation:

export const createDomainServices = (env: Env): DomainServices => ({
accounting: new AccountingService(accountRepository, journalEntryRepository),
equityPortfolio: new EquityPortfolioService(equityRepository, fundService),
// ... other services
});

Cross-Service Communication

Services communicate through well-defined interfaces and return Result types:

class EODService {
async processPortfolioValuation(
fundId: string,
date: string,
): Promise<Result<void, DomainError>> {
// Get holdings from portfolio services
const equityHoldings = await this.equityPortfolio.getHoldingsForDate({
fundId,
asOf: date,
});
if (equityHoldings.isErr()) return equityHoldings;

const bondHoldings = await this.bondPortfolio.getHoldingsForDate({
fundId,
asOf: date,
});
if (bondHoldings.isErr()) return bondHoldings;

// Process with accounting service
const unrealizedResult =
await this.accounting.processUnrealizedGainLoss(/* ... */);
return unrealizedResult;
}
}

Testing Patterns

Service Testing

Services are tested with Result patterns:

describe("AccountingService", () => {
it("should create journal entry with valid data", async () => {
const result = await service.createJournalEntry(validRequest);
expect(result.isOk()).toBe(true);
expect(result.value.id).toBeDefined();
});

it("should return validation error for invalid data", async () => {
const result = await service.createJournalEntry(invalidRequest);
expect(result.isErr()).toBe(true);
expect(result.error.type).toBe("ValidationError");
});
});

Integration Testing

Integration tests verify service composition:

describe("EOD Workflow", () => {
it("should process complete end-of-day flow", async () => {
const result = await eodService.execute({ fundId, businessDate });
expect(result.isOk()).toBe(true);

// Verify side effects
const accounts = await accountingService.getAccountsByFundId(fundId);
expect(accounts).toBeDefined();
});
});

Performance Considerations

Database Operations

  • Use repository patterns for consistent data access
  • Implement connection pooling where applicable
  • Batch operations for bulk data processing

Memory Management

  • Avoid loading large datasets into memory
  • Use streaming for report generation
  • Implement pagination for list operations

Error Handling Performance

  • Result objects have minimal overhead compared to exceptions
  • Use map(), flatMap(), and match() for efficient error propagation
  • Avoid excessive error object creation in hot paths

Monitoring and Observability

Structured Logging

All services use the shared logging utility:

const logger = createLogger({
service: "accounting",
version: "1.0.0",
});

logger.info("Processing transaction", {
transactionId,
amount,
fundId,
});

Error Tracking

Domain errors include structured data for monitoring:

return err({
type: "ValidationError",
message: "Amount must be positive",
field: "amount",
details: {
provided: amount,
rule: "positive_number",
},
});

Metrics

Services emit metrics for:

  • Method execution times
  • Error rates by type
  • Database operation performance
  • External API call success rates

Security Considerations

Authorization

  • Services check user permissions through middleware
  • Fund-level access control is enforced at service boundaries
  • Organization isolation is maintained throughout

Data Validation

  • Input validation using structured domain errors
  • Type safety with TypeScript strict mode
  • Sanitization of external data sources

Audit Trail

  • All financial operations create audit trails
  • User actions are logged with correlation IDs
  • Data changes are tracked with timestamps and user context

Next Steps

  1. Complete Bond Portfolio Migration: Finish neverthrow migration for bond services
  2. Add New Services: Document any additional services added to the system
  3. Performance Optimization: Implement caching and batching for high-volume operations
  4. Enhanced Monitoring: Add detailed metrics and alerting for service health
  5. Documentation Updates: Keep service documentation synchronized with implementation

Last Updated: 2025-01-11 - Reflects current neverthrow migration status and service structure