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" />
· ──────────────────────────────────────────────────────────────────
╰─
| 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.
# 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 checkPre-built binaries (Linux, Windows x86/arm) are available on the releases page.
Or install via Cargo:
cargo install ark-cliOr build from source:
git clone https://github.com/TheEskhaton/ark
cd ark
cargo build --release
# binary: target/release/arkark 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.
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.
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 presentBrownfield 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 snapshotCommit 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.
Show which layer a project belongs to and what it can depend on.
ark explain MyApp.DomainProject: 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.
Export the project dependency graph.
ark graph # Mermaid to stdout
ark graph --format dot -o graph.dot # Graphviz DOT fileGenerate a starter architecture.toml in the solution root.
ark init- 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 checkOr via Cargo (slower, but no binary download needed):
- name: Check architecture
run: |
cargo install ark-cli --quiet
ark checkark exits 0 on clean, 1 on violations.
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| 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).
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 |
MIT