Skip to main content

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

  1. Clear Public API: Barrel files define what's public vs. internal
  2. Encapsulation: Internal changes don't affect consumers
  3. Refactoring Safety: Move files within domain without breaking imports
  4. Enforced Boundaries: ESLint prevents accidental coupling
  5. Better Testing: Mock entire domains at the barrel level

See Also