Enforce a repository's architecture policy as TUnit tests, so the rules are defined once rather than copied into every repository's test project.
- Project-level dependency direction — a declared
ProjectReferencethe matrix forbids, or does not permit. - Type-level dependency direction — a type in one responsibility using a type in another, including a dependency that only arrives transitively or through a package. Read from the compiled assemblies by metadata only: nothing is loaded or executed.
- Composition boundary — only the composition root's category is exempt from being referenced back to.
- Sibling independence — qualified implementations of a responsibility must not reference one another.
- Forbidden packages and framework references — e.g.
Microsoft.AspNetCore.Appin a portable layer. - Type naming and shape — interface, attribute and exception conventions; sealed classes, read-only collections, root namespaces and attributes, read from metadata.
- Cycles, naming and test references — the rest of the standard set, reported readably.
- Waivers — reviewed, per-project departures that keep every other rule in force and are themselves checked.
- An extension point — add a bespoke rule to the same reporting,
SkipRuleand waiver model, without a second architecture framework.
Adoption is two things: a policy file and one derived class.
{
"$schema": "https://raw.githubusercontent.com/purview-dev/architests/main/architecture-policy.schema.json",
"version": 3,
"sourceRoots": ["src"],
"responsibilities": [
{
"name": "Core",
"aliases": ["Domain"],
"category": "domain",
"purpose": "The domain model, its invariants and its rules.",
"mayDependOn": [],
"mustNotDependOn": ["Application", "Persistence"],
"forbiddenFrameworkReferences": ["Microsoft.AspNetCore.App"]
}
]
}[InheritsTests]
public sealed class ArchitectureTests : PurviewArchitectureTests;
[InheritsTests]is required. TUnit does not walk base types during discovery, so without it the class compiles, the run is green and nothing is enforced. TUnit's analyzer reportsTUnit0030— treat it as an error.
dotnet add package Purview.Architecture.Testing
The same policy can be checked outside a test run:
dotnet tool install --global Purview.Architecture.Testing.Cli
architests validate
architests waivers list --staleExit codes are part of the contract: 0 clean, 1 findings, 2 a malformed command line.
| Rule id | Catches |
|---|---|
policyIntegrity |
A policy that names an unknown responsibility, permits and forbids the same dependency, or declares a duplicate name or alias |
graphDiscovery |
No production projects discovered, so every other rule would pass vacuously |
naming |
A project name that maps to no responsibility or alias |
discouragedName |
A project named after a category rather than a responsibility |
dependencyMatrix |
A declared ProjectReference the matrix forbids or does not permit |
assemblyDependencyMatrix |
A type-level reference the matrix forbids or does not permit, including one that arrives transitively |
typeNaming |
A public interface without I, an attribute or exception without its suffix, or a discouraged suffix such as Manager |
typeShape |
An unsealed public class, a public field, a mutable collection return, or a utility class that is not static |
typeNamespace |
A public type declared outside its project's root namespace |
attributePolicy |
A missing or forbidden attribute, [Obsolete] without a message, or InternalsVisibleTo naming production code |
apiSurface |
The public API differs from the committed baseline |
apiDocumentation |
A public type or member has no XML documentation |
portImplementation |
A port in an inner layer has no implementation |
portPurity |
A port exposes an infrastructure technology |
dependencyBudget |
A project references beyond its budget, or the budget still allows a reference that has gone |
siblingIndependence |
A qualified implementation referencing a qualified sibling |
compositionBoundary |
A non-composition, non-host project referencing the composition root |
forbiddenPackage |
A package the responsibility owns matching a forbidden prefix |
forbiddenFramework |
A framework reference matching a forbidden prefix for the responsibility |
cycle |
A cycle in the project graph |
testReference |
A production project referencing a test project |
waiverIntegrity |
A stale, unjustified or malformed waiver, or one excusing an unknown or unwaivable rule |
- Getting started — adopt the package in a repository, step by step.
- Tutorial — grow one application and its policy from a two-project matrix to type, API, port and budget rules.
- Policy reference — every field, every rule id, every default.
- Type-level rules — how the assembly check works and how an assembly is attributed to a project.
- API contracts — the committed baseline, XML documentation and the port rules.
- Migration and budget — adopt incrementally and ratchet the project graph.
- Extending — add your own rule to the standard set.
- Command line — validate, capture the API, review waivers and render the graph.
- The policy schema — the published JSON Schema.
src/samples/ArchitectureSample is a small, fully
working repository that adopts the package: four projects, a policy, and a test project whose whole
suite is one derived class.
dotnet test src/samples/ArchitectureSample/tests/Sample.ArchitectureTests/Sample.ArchitectureTests.csprojsrc/src/ArchitectureTesting— the package: policy reader, project and assembly graph, rule evaluator, and the TUnit base class.src/src/Architecture.CLI— thearchitestsdotnet tool.src/tests/ArchitectureTesting.UnitTests— rule tests against in-memory and fixture graphs.src/tests/ArchitectureTesting.CLI.UnitTests— the tool's parser and commands.src/tests/Repository.UnitTests— the skill self-tests, the rule/documentation drift test, and the package applied to this repository.src/samples/ArchitectureSample— the adoption sample.architecture-policy.schema.json— the published policy schema, embedded into the package.skills/— the agent skills this repository publishes.
npx skills add purview-dev/architestsdotnet tool restore
just restore
just build
just test
just lint-check
just pack
just pipeline-pr
Part of Purview.BuildSdk · purview.dev