ng-dependency-graph

Angular Dependency Graphs Explained

Understand how Angular components and services depend on each other, why it matters for architecture, how Nx tracks dependencies in monorepos, and tools to visualize and detect circular references in your component tree.

What is an Angular Dependency Graph?

In Angular applications, a dependency graph maps how components, services, and modules relate to each other. Each node is a component, service, or module, and each edge represents an import or injection relationship.

When you inject a service into a component using constructor(private service: MyService) or inject(MyService), you're creating an edge in the dependency graph. This graph becomes critical in larger applications, especially in monorepos where multiple apps share libraries.

Simple Angular dependency graph: AppComponent ├── HeaderComponent │ ├── NavService │ └── AuthService ├── MainComponent │ ├── ApiService │ └── DataService │ └── AuthService (shared) └── FooterComponent └── AuthService (shared)

The key insight: AuthService appears in multiple places. If you modify it, all dependent components might be affected. A dependency graph makes these relationships explicit.

Why Angular Dependency Graphs Matter

Detect Circular Dependencies

When Component A uses Service B, and Service B uses Component A, you get a circular dependency that causes initialization errors and undefined behavior.

Understand Component Coupling

A dependency graph reveals which components are tightly coupled. High coupling makes refactoring risky and testing difficult.

Plan Architecture Changes

Before extracting a service or splitting a component, visualize the dependency graph to understand the impact on the rest of your app.

Optimize for Monorepos

In Nx monorepos, the dependency graph determines which libraries must be rebuilt when code changes, directly affecting CI/CD speed.

Improve Code Quality

A clean dependency graph (fewer dependencies, no cycles) is a sign of good architecture and maintainability.

Onboard New Developers

Show new team members a visual dependency graph instead of asking them to read 100 import statements.

Angular Signals and Dependency Tracking

Angular Signals (introduced in Angular 14+) automatically track dependencies at runtime. When you use a signal in a computed() or effect(), Angular builds an implicit dependency graph.

Signals dependency graph: const $isLoading = signal(false); const $isSaving = signal(false); const $isBusy = computed(() => $isLoading() || $isSaving() ); // Dependency graph: // $isLoading —> $isBusy // $isSaving —/

Unlike RxJS where you explicitly subscribe, Signals use implicit dependency tracking. This is powerful but requires understanding the graph to avoid unintended dependencies.

Circular Dependencies in Angular

A circular dependency occurs when a component needs a service, and that service needs to use the component. For example:

Circular dependency: // header.component.ts import { NotificationService } from ‘./services/notification.service’; @Component({ … }) export class HeaderComponent { constructor(private notify: NotificationService) { } } // notification.service.ts import { HeaderComponent } from ‘./header.component’; @Injectable() export class NotificationService { constructor(header: HeaderComponent) { } // ❌ Circular! }

Angular tries to resolve this by returning incomplete instances, causing "Cannot read property X of undefined" errors. The fix: extract shared logic into a third service that neither component nor service imports from the other.

Tools for Visualizing Angular Dependencies

Tool Type Best For Output
Nx Graph CLI + Web UI Monorepos, library dependencies Interactive web visualization
ngd (Angular Dependency Graph) CLI Component & service visualization HTML, SVG, JSON
Angular DevTools Browser Extension Runtime component tree inspection Interactive DOM visualization
ESLint (angular-eslint) Linter Rules Detecting circular dependencies CLI warnings during development

How Nx Tracks Monorepo Dependencies

In an Nx monorepo, the dependency graph is central to performance. Run nx graph to see your entire monorepo as an interactive visualization. Nx uses this graph to:

Determine What to Build

Only rebuild libraries and apps that changed or depend on changes. If a leaf library changes, Nx rebuilds only that library and dependents.

Optimize CI/CD

Use nx affected to rebuild only affected projects, reducing CI pipeline time from hours to minutes.

Detect Architecture Violations

Define boundaries with Nx tags and rules to prevent a feature library from importing from admin libraries, for example.


FAQ

A component dependency is another component you import or use in a template (e.g., <app-header>). A service dependency is injected via constructor or inject() and provides shared logic. Both appear in the dependency graph.
Use tools like ngd, Nx graph, or ESLint with angular-eslint. They scan your imports and flag circular references. You'll see errors like "Cannot read property X of undefined" at runtime if you have undetected cycles.
Not essential, but helpful. Small apps (5-10 components) are simple enough to track mentally. Larger apps (100+ components) or monorepos absolutely need visualization to understand architecture.
Nx graph visualizes library and app dependencies in monorepos (high level). ngd visualizes component and service dependencies within an app or library (low level). Use both for complete understanding.
Not with standard tools. Signals build implicit dependency graphs at runtime. Angular DevTools can inspect the component tree, but viewing signal dependencies requires debugging with DevTools or adding custom logging.

Quick Service/Component Analyzer

Paste a snippet of Angular code to see the detected imports and dependencies.

// Paste Angular code