dotnet add package Purview.Architecture.Testing
Add it to a test project that already references the projects you want to govern, or create a
dedicated Architecture.UnitTests project. The test project must reference the production projects,
directly or transitively, because the type-level rule reads their compiled assemblies from the test
output.
Create .config/architecture-policy.json at the repository root. The smallest useful policy names
each responsibility and what it may depend on:
{
"$schema": "https://raw.githubusercontent.com/purview-dev/architests/main/architecture-policy.schema.json",
"version": 2,
"sourceRoots": ["src"],
"responsibilities": [
{
"name": "Domain",
"category": "domain",
"purpose": "The domain model, its invariants and its rules.",
"mayDependOn": [],
"mustNotDependOn": ["Application", "Persistence", "Composition"]
},
{
"name": "Application",
"category": "application",
"purpose": "Coordinates the use cases and workflows.",
"mayDependOn": ["Domain"],
"mustNotDependOn": ["Persistence", "Composition"]
},
{
"name": "Persistence",
"category": "infrastructure",
"purpose": "Durable storage and persistence-specific configuration.",
"mayDependOn": ["Domain", "Application"],
"mustNotDependOn": ["Composition"]
},
{
"name": "Composition",
"category": "composition",
"purpose": "The composition root that selects and wires the implementations.",
"mayDependOn": ["Domain", "Application", "Persistence"],
"mustNotDependOn": []
}
]
}A project name is <Responsibility>[.<Implementation>], and only the first segment is
classified: Persistence.SqlServer and Persistence are both Persistence. Qualification is a
naming choice, never a different set of rules.
sourceRoots defaults to ["src"]. Getting it wrong finds no projects, which the graphDiscovery
rule reports rather than letting every other rule pass vacuously.
using Purview.Architecture.Testing;
[InheritsTests]
public sealed class ArchitectureTests : PurviewArchitectureTests;That is the whole suite. Each rule is a separate [Test], so a failure names the rule rather than
collapsing the set into one assertion.
[InheritsTests]is required. TUnit does not walk base types during discovery, so without the attribute the class compiles, the run is green, and nothing is enforced — the worst failure this package can have. TUnit's analyzer reportsTUnit0030; treat it as an error.
dotnet testIf graphDiscovery fails, sourceRoots is wrong. If naming fails, a project name does not map to
a responsibility — add an alias or a waiver. If dependencyMatrix or assemblyDependencyMatrix
fails, the message names the offending reference, the direction the dependency should run, and the
likely fix.
The project-level matrix only sees declared ProjectReferences. To also catch a type that arrives
transitively or through a package, enable the assembly check:
"assemblyRules": {
"enabled": true
}An empty assemblyRoots scans the test output directory, which already holds every project the test
project references. See Type-level rules for how an assembly is attributed to
a project.
[InheritsTests]
public sealed class ArchitectureTests : PurviewArchitectureTests
{
protected override string? RepositoryRoot => "/path/to/repo";
}RepositoryRoot defaults to probing upward from the test binary for the nearest policy file. The
search stops at the repository boundary (.git) rather than continuing into parent folders —
walking past it would happily find a sibling checkout's policy and validate this repository against
someone else's rules.
SkipRule opts a whole repository out of a rule. Prefer a waiver: it
is per project, carries a reason and a review date, and is itself checked.