Every large SwiftUI codebase eventually says:
“We follow clean architecture.”
Then six months later:
- features import each other
- ViewModels talk to services directly
- infrastructure leaks upward
- cycles appear
- nobody knows what depends on what
Architecture without enforcement always decays.
This post shows how to design dependency graph visualization & auditing for SwiftUI apps so your architecture is:
- visible
- enforceable
- reviewable
- resilient over time
🧠 The Core Principle
Architecture is a graph — not a guideline.
If you can’t see your dependency graph, you can’t control it.
🧱 1. Define Allowed Dependency Directions
Before tooling, define rules.
Example:
Views
→ ViewModels
→ Domain
→ Infrastructure
Rules:
- no upward dependencies
- no cross-feature imports
- infrastructure never imports features
- features never import each other directly
Write this down. This is your contract.
🧬 2. Treat Modules as Graph Nodes
Each logical module is a node:
- Feature modules
- Domain modules
- Infrastructure modules
- Shared utilities
The graph edges are imports.
This means:
- imports = architectural dependencies
- build errors = enforcement points
🔍 3. Generate the Dependency Graph
Use static analysis to extract imports.
Example approaches:
- Swift Package Manager manifests
- Xcode build logs
- custom SourceKit parsing
- scripts that scan
importstatements
Output a graph format (DOT / JSON):
FeatureA → Domain
FeatureA → FeatureB ❌
Visualization exposes reality instantly.
🧭 4. Visualize the Graph
Render the graph using:
- Graphviz
- Mermaid
- custom dashboards
You should be able to:
- see cycles
- spot illegal edges
- identify hotspots
- track growth over time
If architecture can’t be visualized, it can’t be trusted.
🧪 5. Automated Dependency Audits
Turn architecture rules into tests.
Example:
fail if Feature imports Infrastructure
fail if Feature imports Feature
fail if cycle detected
Run audits:
- locally
- in CI
- on every PR
Architecture violations should fail builds — not code reviews.
🧠 6. Ownership & Boundaries
Every node should have:
- an owner
- a purpose
- allowed dependencies
When a dependency is added:
- ask why
- document it
- review the boundary
Undefined ownership accelerates decay.
🔁 7. Detect Cycles Early
Cycles kill:
- testability
- refactors
- parallel work
Graph tooling should detect:
- direct cycles
- transitive cycles
- hidden cycles via shared utilities
Break cycles immediately — they only get worse.
⚠️ 8. Common Dependency Anti-Patterns
Avoid:
- shared “Utils” modules that import everything
- feature-to-feature imports
- infrastructure leaking into ViewModels
- global singletons as dependency shortcuts
- “temporary” imports that stay forever
If it’s convenient, it’s probably wrong.
🧠 Mental Model
Think:
Architecture Rules
→ Dependency Graph
→ Automated Audits
→ Build Enforcement
Not:
“Let’s try to remember the rules”
🚀 Final Thoughts
Dependency graph auditing gives you:
- enforceable architecture
- fearless refactoring
- faster onboarding
- safer scaling
- long-lived codebases
Architecture is not documentation.
It is constraint + visibility.
Top comments (0)