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
- Shared Services (
@shared/) - Cross-cutting utilities and types - Domain Services - Core business logic and entity management
- Application Services - Use cases and orchestration logic
- 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
| Service | Description | Status |
|---|---|---|
| Domain Errors | Comprehensive error type system with discriminated unions | ✅ Complete |
| Logger | Structured logging with Cloudflare Workers compatibility | ✅ Complete |
| Financial Constants | Money handling, account codes, and financial calculations | ✅ Complete |
| ID Generation | Unique identifier generation and validation | ✅ Complete |
Core Domain Services
Portfolio Management
| Service | Description | Neverthrow Status | Location |
|---|---|---|---|
| Equity Portfolio | Equity registrations, transactions, and daily snapshots | ✅ Migrated | worker/services/portfolio/equity/ |
| Bond Portfolio | Bond registrations, pricing, and accrued interest calculations | ✅ Migrated | worker/services/portfolio/bond/ |
Financial Services
| Service | Description | Neverthrow Status | Location |
|---|---|---|---|
| Accounting | Double-entry bookkeeping, journal entries, and account balances | ✅ Migrated | worker/services/accounting/ |
| Bank Account | Cash management, interest calculation, and reconciliation | ✅ Migrated | worker/services/bank-account/ |
| FDR (Fixed Deposits) | Fixed deposit management and interest accrual | ✅ Migrated | worker/services/fdr/ |
Entity Management
| Service | Description | Neverthrow Status | Location |
|---|---|---|---|
| Fund | Fund creation, management, and configuration | ✅ Migrated | worker/services/fund/ |
| Investor | Investor registration and management | ✅ Migrated | worker/services/investor/ |
| Organization | Organization management and tenant isolation | ✅ Migrated | worker/services/organization/ |
Integration Services
| Service | Description | Neverthrow Status | Location |
|---|---|---|---|
| Market Data | DSE API integration for securities and pricing data | ✅ Migrated | worker/services/market-data/ |
| Authentication | User authentication and session management | ✅ Migrated | worker/services/auth/ |
| Unrealized Gain/Loss | Mark-to-market calculations for portfolio securities | ✅ Migrated | worker/services/accounting/ |
Transaction Services
| Service | Description | Neverthrow Status | Location |
|---|---|---|---|
| Unit Transaction | Unit purchase and surrender transaction queries | ✅ Migrated | worker/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(), andmatch()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
- Complete Bond Portfolio Migration: Finish neverthrow migration for bond services
- Add New Services: Document any additional services added to the system
- Performance Optimization: Implement caching and batching for high-volume operations
- Enhanced Monitoring: Add detailed metrics and alerting for service health
- Documentation Updates: Keep service documentation synchronized with implementation
Last Updated: 2025-01-11 - Reflects current neverthrow migration status and service structure