Skip to content

Server → internal: cross-domain via exported services, not repos or DB

**Scope: server → internal domain** (server-to-internal). One backend domain calling another inside your monolith — inject the exported service, not HTTP to `/api/...`, not the other domain's repository or tables. The repository is internal to the domain. The service is the public API.

nestjs-module-cross-module-via-services

Why it matters

Failure modes if this rule is ignored
StakeIf ignored
API drift
  • Import another domain's repository — you skip their service and its validation entirely.
Hidden coupling
  • If the workspace domain touches the catalog's repository, a change to the catalog's internal schema breaks the workspace.

How to fix

If WorkspaceModule needs catalog information, it does not inject CatalogRepository, hit the DB directly, or fetch the catalog API. It injects the CatalogService exported by CatalogModule. Browser clients use a generated data-access client; external vendors use a dedicated integration client.

Examples

Bad
ts
// libs/workspace/backend-feature/src/lib/workflows.service.ts
@Injectable()
export class WorkflowsService {
  constructor(
    @InjectRepository(Item)
    private catalogRepo: Repository<Item>,    // ← reaching into catalog's repo
  ) {}

  async attachToItem(workflowId: string, itemId: string) {
    const item = await this.catalogRepo.findOne({ where: { id: itemId } });
    // bypasses every CatalogService validation
  }
}
Good
ts
// libs/catalog/backend-feature/src/lib/catalog.service.ts
@Injectable()
export class CatalogService {
  async getItem(id: string): Promise<Item> { /* ... */ }
}

@Module({
  providers: [CatalogService, CatalogRepository],
  exports: [CatalogService],   // ← repository is NOT exported
})
export class CatalogModule {}

// libs/workspace/backend-feature/src/lib/workflows.service.ts
@Injectable()
export class WorkflowsService {
  constructor(private catalog: CatalogService) {}   // through the front door

  async attachToItem(workflowId: string, itemId: string) {
    const item = await this.catalog.getItem(itemId);
    // goes through CatalogService validation, authorization, logging
  }
}

Contribute

Released under the MIT License.

esc