Skip to content

TheEskhaton/ark

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

30 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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. Generate a starter config in your solution root
cd /path/to/your/solution
ark init

# 2. Edit architecture.toml to match your layers, then:
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

Generate a starter architecture.toml in the solution root.

ark init

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

About

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

Resources

License

Stars

Watchers

Forks

Contributors

Languages