Skip to content

Dependencies flow one direction

Dependencies flow one way: apps → feature → data-access → shared — shared never imports a domain.

nx-isolation-deps-one-direction

Why it matters

Failure modes if this rule is ignored
StakeIf ignored
Hidden coupling
  • Once shared knows a domain, it's not shared anymore.
Hard to change
  • Adding a new domain requires editing shared — every new domain ripples through cross-team review.

How to fix

Layering: apps → features → data-access/ui → util/shared. shared may not know about a specific domain. ui may not import feature. Nx enforces with type: tags.

shared provides a mechanism (a registry, an interface, a base class). The domain registers into it from its own app entry point. nx graph shows flow goes one way.

Examples

Bad
ts
// libs/shared/auth/src/lib/permissions.ts
import { StudioPermissions } from '@acme/studio/permissions';
import { SitesPermissions } from '@acme/sites-management/permissions';

export function canAccess(user: User, resource: string) {
  if (resource.startsWith('studio/')) return StudioPermissions.check(user);
  if (resource.startsWith('sites/'))  return SitesPermissions.check(user);
}
Good
ts
// libs/shared/auth/src/lib/registry.ts
type Checker = (user: User, resource: string) => boolean;
const checkers = new Map<string, Checker>();

export function registerPermissionChecker(prefix: string, checker: Checker) {
  checkers.set(prefix, checker);
}

export function canAccess(user: User, resource: string) {
  const prefix = resource.split('/')[0];
  return checkers.get(prefix)?.(user, resource) ?? false;
}

// apps/studio/src/main.ts
import { registerPermissionChecker } from '@acme/shared/auth';
import { StudioPermissions } from '@acme/studio/feature-permissions';
registerPermissionChecker('studio', StudioPermissions.check);

Contribute

Released under the MIT License.

esc