No dependency cycles
assertNoDependencyCycles checks directed depends-on edges between services, components and computeds:
export function keepTheDependencyGraphAcyclic(graph: ArchitectureGraph) {
assertNoDependencyCycles(graph.graph);
}What it prevents
The obvious cycle is two services that construct each other:
Left → Right → LeftIn source, it often appears as a harmless pair of yield* calls. At runtime it can fail as a recursive construction, an incomplete service or a provider that only breaks when a route is first visited.
The rule also catches a self-dependency and cycles involving computed values.
Shared dependencies are not cycles
This is valid:
AdminPage → Auth
Checkout → AuthBoth branches depend on a shared kernel; there is no path back from Auth to either branch. provides, contains, loads and renders are structural edges and are not treated as dependency cycles.
How to break a real cycle
Usually one side should depend on a smaller contract:
- extract a read-only service from the two large services;
- move shared policy into a third service;
- pass a value as an input instead of resolving the owning service;
- move a derived value into the consumer instead of publishing it back.
Do not silence a cycle by adding an allow list: this assertion has no such escape hatch because a cycle changes construction semantics.