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:
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 Controller
Services ending with Service
Repositories ending with Repository
Interfaces 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.
Monitoring Architectural Health
Useful indicators include:
Architecture test pass rate
Dependency violations
Layer boundary violations
Circular dependency count
Unauthorized package references
Tracking these indicators helps teams identify long-term architectural trends.
Comparison of Validation Approaches
| Approach | Advantages | Limitations |
|---|
| Manual Code Review | Human judgment | Time-consuming and inconsistent |
| Documentation Only | Easy to create | Cannot enforce compliance |
| Static Analysis | Detects code issues | Limited architectural awareness |
| ArchUnitNET | Automated architecture validation | Requires well-defined architectural rules |
Many teams combine architecture tests with static analysis and code reviews.
Common Mistakes
| Mistake | Better Approach |
|---|
| Defining overly complex rules | Start with simple, meaningful architectural constraints |
| Using inconsistent namespaces | Maintain predictable project organization |
| Ignoring architecture test failures | Treat violations as technical debt requiring investigation |
| Writing rules that duplicate compiler checks | Focus on architectural boundaries rather than language rules |
| Running architecture tests only occasionally | Execute them as part of every CI build |
Troubleshooting
Tests Report Unexpected Violations
Verify:
Namespace organization
Assembly loading
Layer definitions
Rule configuration
Incorrect namespaces often lead to inaccurate architecture results.
Architecture Tests Become Difficult to Maintain
Review whether:
Rules reflect the current architecture.
Layer definitions remain consistent.
Project organization has changed.
Architecture tests should evolve alongside the application.
Build Time Increases
Architecture validation generally executes as part of the test suite.
If execution time becomes significant:
Best Practices
Keep architectural rules simple and focused.
Organize projects with consistent namespaces.
Validate architectural boundaries in every build.
Version architecture rules alongside application changes.
Review failed architecture tests before suppressing them.
Combine architecture validation with code reviews and static analysis.
Regularly update rules as the architecture evolves.
Conclusion
Maintaining a clean software architecture requires more than documentation and code reviews. As applications evolve, architectural boundaries can gradually erode unless they are continuously verified. ArchUnitNET enables teams to express architectural principles as executable tests, making architecture validation an automated part of the development process.
By integrating architecture tests into CI/CD pipelines, enforcing dependency rules, validating naming conventions, and monitoring architectural health over time, development teams can reduce architectural drift and maintain more consistent, scalable, and maintainable ASP.NET Core applications.
Frequently Asked Questions
What types of rules can ArchUnitNET validate?
ArchUnitNET can validate architectural concerns such as layer dependencies, namespace organization, naming conventions, project boundaries, and dependency relationships between assemblies and types.
Should architecture tests replace code reviews?
No. Architecture tests automate repeatable structural checks, while code reviews remain important for evaluating business logic, design decisions, readability, and maintainability.
Is ArchUnitNET only useful for Clean Architecture?
No. It can be applied to many architectural styles, provided the project has well-defined structural rules that can be expressed as automated tests.
How often should architecture tests run?
For most projects, architecture tests should execute alongside the regular automated test suite in the CI/CD pipeline so that architectural violations are detected as early as possible.