ark-cli 0.1.2

Architectural boundary enforcer for .NET solutions
ark-cli-0.1.2 is not a library.

ark

Crates.io License: MIT

Architectural boundary enforcer for .NET solutions. Parses .csproj project graphs and C# source files to catch layer violations before they reach CI.

$ ark check

  × Layer 'Domain' (MyApp.Domain) must not depend on layer 'Infrastructure' (MyApp.Infrastructure.Data)
   ╭─[MyApp.Domain/MyApp.Domain.csproj:6:27]
 6 │     <ProjectReference Include="..\MyApp.Infrastructure.Data\MyApp.Infrastructure.Data.csproj" />
   ·                           ──────────────────────────────────────────────────────────────────
   ╰─

What it checks

Check What it catches
Project references .csproj <ProjectReference> that cross forbidden layer boundaries
Package policies <PackageReference> packages banned from specific layers (e.g. EF Core in Domain)
Source imports using directives in .cs files referencing a forbidden layer's namespace (tree-sitter)

Violations are reported with source spans pointing directly to the offending line.


Quick start

# 1. Scaffold architecture.toml interactively
cd /path/to/your/solution
ark init

# 2. Check for violations
ark check

Installation

Pre-built binaries (Linux, Windows x86/arm) are available on the releases page.

Or install via Cargo:

cargo install ark-cli

Or build from source:

git clone https://github.com/TheEskhaton/ark
cd ark
cargo build --release
# binary: target/release/ark

Configuration

ark reads architecture.toml from the solution root. Run ark init to generate a starter file, or write one by hand:

layers = [
  { name = "Presentation",   patterns = ["*.Api", "*.Web", "*.Host"]                   },
  { name = "Application",    patterns = ["*.Application", "*.UseCases"]                },
  { name = "Domain",         patterns = ["*.Domain", "*.Core"]                         },
  { name = "Infrastructure", patterns = ["*.Infrastructure", "*.Persistence"]          },
]

# Any dependency not listed here is forbidden by default.
dependency_rules = [
  { from = "Presentation",   to = "Application",   allowed = true  },
  { from = "Application",    to = "Domain",         allowed = true  },
  { from = "Infrastructure", to = "Domain",         allowed = true  },
  { from = "Domain",         to = "Infrastructure", allowed = false },
]

package_policies = [
  { layer = "Domain", forbidden = ["Microsoft.EntityFrameworkCore", "Microsoft.AspNetCore"] },
]

ignore_patterns = ["*.Tests", "*.Specs", "*.IntegrationTests"]

Layer patterns use glob syntax — *.Domain matches any project name ending in .Domain. Any dependency not listed in dependency_rules is forbidden by default.

C# source scanning

Add namespace_patterns to a layer to also check using directives in .cs files for that layer (powered by tree-sitter):

layers = [
  { name = "Domain", patterns = ["*.Domain"], namespace_patterns = ["MyApp.Domain.*"] },
]

Omit namespace_patterns to skip source scanning for that layer.


Commands

ark check

Run all architectural checks and report violations.

ark check                  # exit 1 if any violations
ark check --strict         # also exit 1 on warnings (unmatched projects)
ark check --no-baseline    # ignore ark-baseline.json even if present

ark baseline

Brownfield adoption: snapshot existing violations so ark check only fails on new ones. Teams can quarantine known debt and clean it up incrementally.

ark baseline               # write current violations → ark-baseline.json
ark check                  # now only fails on violations introduced since the snapshot

Commit ark-baseline.json alongside your config. Stale entries (violations that no longer exist) surface as warnings so you know when debt has been paid off.

ark explain

Show which layer a project belongs to and what it can depend on.

ark explain MyApp.Domain
Project: MyApp.Domain
Layer:   Domain

Dependency rules:
  → Application              forbidden  [default]
  → Infrastructure           forbidden  [explicit]
  → Presentation             forbidden  [explicit]

Other projects in this layer:
  MyApp.Core

Useful when a project shows up as unmatched or when onboarding to an unfamiliar solution.

ark graph

Export the project dependency graph.

ark graph                               # Mermaid to stdout
ark graph --format dot -o graph.dot     # Graphviz DOT file

ark init

Interactively scaffold architecture.toml from your real solution structure.

ark init

The wizard:

  1. Scans all .csproj files and builds a dependency graph
  2. Groups projects into architectural tiers by topological depth
  3. Walks you through naming each tier as a layer
  4. Reviews each detected inter-layer dependency — allow or forbid
  5. Writes architecture.toml tailored to your solution

For brownfield teams with existing violations, run ark baseline right after to snapshot the current state.


CI integration

GitHub Actions

- name: Check architecture
  run: |
    curl -sSL https://github.com/TheEskhaton/ark/releases/latest/download/ark-latest-x86_64-unknown-linux-gnu.tar.gz | tar xz
    ./ark check

Or via Cargo (slower, but no binary download needed):

- name: Check architecture
  run: |
    cargo install ark-cli --quiet
    ark check

ark exits 0 on clean, 1 on violations.


Global flags

ark --root <path>     # solution root (default: current directory)
ark --config <path>   # config file (default: architecture.toml)

Useful when running ark from a directory other than the solution root:

ark --root /path/to/solution check

Exit codes

Code Meaning
0 No violations
1 One or more violations found

Use --strict to also exit 1 on warnings (e.g. projects that match no layer).


Performance

ark uses rayon for parallel project and file parsing.

Operation Target Typical (ABP Framework, ~500 projects)
Project graph scan < 50 ms ~30 ms
Full source scan (tree-sitter) < 500 ms ~200 ms

License

MIT