As enterprise applications grow, maintaining a clean architecture becomes increasingly challenging. New developers join the team, features evolve rapidly, and dependencies accumulate over time. Without consistent architectural governance, presentation layers may begin accessing databases directly, business logic may leak into controllers, and infrastructure concerns may spread throughout the codebase.
Traditional code reviews help identify these issues, but they rely on manual effort and can miss violations in large projects. ArchUnitNET provides an automated way to validate architectural rules as part of your test suite, helping teams detect structural problems before they reach production.
In this article, you'll learn how ArchUnitNET works, how to define architecture rules for ASP.NET Core applications, and how to integrate architecture validation into your CI/CD pipeline.
Why Architecture Validation Matters
A well-designed architecture improves:
Maintainability
Testability
Scalability
Team productivity
Code consistency
Over time, however, architectural boundaries can erode without automated validation.
Common examples include:
Controllers accessing databases directly
Domain models referencing infrastructure packages
Circular project dependencies
Business logic inside UI components
Unauthorized dependency usage
Automated architecture tests help identify these issues early.
What Is ArchUnitNET?
ArchUnitNET is a .NET library that allows developers to express architectural rules as executable tests.
Instead of documenting architectural guidelines and hoping developers follow them, you can verify them automatically during every build.
For example:
Developer
│
Source Code
│
Architecture Tests
│
Pass or Fail
If an architectural rule is violated, the test fails, preventing the issue from progressing unnoticed.
Typical Clean Architecture
A common layered architecture might look like this:
Presentation
│
Application
│
Domain
│
Infrastructure
Each layer should only depend on approved layers.
For example:
Presentation → Application
Application → Domain
Infrastructure → Domain
The Domain layer should generally avoid dependencies on Presentation or Infrastructure.
Installing ArchUnitNET
Add the NuGet package to your test project.
dotnet add package ArchUnitNET
Architecture tests should typically live in a dedicated test project alongside unit and integration tests.
Loading the Architecture
The first step is loading the assemblies to inspect.
using ArchUnitNET.Loader;
var architecture = new ArchLoader()
.LoadAssemblies(typeof(Program).Assembly)
.Build();
The loader analyzes the compiled assemblies rather than source code.
Defining Layers
Create reusable layer definitions.
using static ArchUnitNET.Fluent.ArchRuleDefinition;
var presentationLayer =
Types().That().ResideInNamespace("MyApp.Api");
var applicationLayer =
Types().That().ResideInNamespace("MyApp.Application");
var domainLayer =
Types().That().ResideInNamespace("MyApp.Domain");
Consistent namespace organization makes architecture validation easier to maintain.
Validating Layer Dependencies
Suppose your architecture requires the Domain layer to remain independent.
A rule may look like this:
var rule =
domainLayer
.Should()
.NotDependOnAny(applicationLayer);
If a domain class references the application layer, the architecture test fails.
Preventing Controller-to-Repository Dependencies
Controllers should usually communicate through application services rather than repositories.
Example rule:
var controllers =
Types().That().ResideInNamespace("MyApp.Api.Controllers");
var repositories =
Types().That().ResideInNamespace("MyApp.Infrastructure.Repositories");
An architecture rule can verify that controllers do not directly depend on repository implementations.
Enforcing Dependency Injection
A common architectural guideline is depending on abstractions rather than concrete implementations.
Architecture tests can help identify areas where concrete infrastructure classes are referenced directly instead of interfaces.
This reinforces dependency inversion across the application.
Validating Naming Conventions
Architecture validation is not limited to dependencies.
Examples include:
Controllers ending with
ControllerServices ending with
ServiceRepositories ending with
RepositoryInterfaces beginning with
I
Consistent naming improves readability across large codebases.
Preventing Circular Dependencies
Circular dependencies increase coupling and complicate maintenance.
Example:
Project A
│
Project B
│
Project C
│
Project A
Architecture tests help detect these cycles before they become widespread.
Running Architecture Tests in CI/CD
A typical pipeline includes architecture validation alongside other automated tests.
Source Code
│
Build
│
Unit Tests
│
Architecture Tests
│
Integration Tests
│
Deployment
Failing architecture tests stop structural issues before deployment.
Join the conversation! Your thoughts help the community grow.