Domain Isolation
Strict enforcement of domain boundaries through barrel files and dependency injection.
The Golden Rule
Cross-domain imports MUST use barrel files (index.ts), not internal implementation files.
Barrel Files (Public API)
Each domain exposes its public API through an index.ts barrel file:
// ✅ CORRECT - Import from barrel
import type { FundService, FundEntity } from "../fund";
import type { InvestorService } from "../investor";
// ❌ WRONG - Direct import from internals (ESLint error)
import { FundService } from "../fund/service";
import { FundEntity } from "../fund/domain";
Internal Imports Within Same Domain
Within the same domain, you can import directly from internal files:
// ✅ Allowed within fund/ domain
import type { FundEntity } from "./domain";
import { FundRepository } from "./repository";
Dependency Injection
Cross-domain dependencies must be injected, not imported:
// ✅ Correct - Dependency injection
class BankAccountService {
constructor(
private repository: BankAccountRepository,
private fundService?: FundService // Injected via factory
) {}
}
// ❌ Wrong - Direct instantiation
class BankAccountService {
private fundService = new FundService(...); // Don't do this!
}
ESLint Enforcement
The project has an ESLint rule that enforces barrel imports:
# Attempting to import internal files:
import { FundService } from './fund/service';
# ESLint error:
# Import from domain barrel (index.ts) instead of internal files.
# Use "./fund" not "./fund/service" or "./fund/domain".
Benefits
- Clear Public API: Barrel files define what's public vs. internal
- Encapsulation: Internal changes don't affect consumers
- Refactoring Safety: Move files within domain without breaking imports
- Enforced Boundaries: ESLint prevents accidental coupling
- Better Testing: Mock entire domains at the barrel level
See Also
- DDD Approach - Detailed domain architecture
- Project Structure - File organization
- Type Exports - Export best practices