DotNetArchGuard 1.0.0

dotnet add package DotNetArchGuard --version 1.0.0
                    
NuGet\Install-Package DotNetArchGuard -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="DotNetArchGuard" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="DotNetArchGuard" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="DotNetArchGuard" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add DotNetArchGuard --version 1.0.0
                    
#r "nuget: DotNetArchGuard, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package DotNetArchGuard@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=DotNetArchGuard&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=DotNetArchGuard&version=1.0.0
                    
Install as a Cake Tool

DotNetArchGuard

Architecture validation and consistency checks to enforce architecture rules and coding patterns across teams.

Problems Solved

  • Copy-paste utilities everywhere: Same utility code duplicated across projects
  • No enforced architecture rules: Architecture violations not caught until code review
  • Inconsistent coding patterns: Different teams use different patterns

Installation

dotnet add package DotNetArchGuard

Quick Start

1. Validate Architecture Rules

Problem: Architecture violations (e.g., controllers accessing repositories directly) not caught early.

using DotNetArchGuard;
using System.Reflection;

// ✅ GOOD: Validate architecture rules in tests
[Fact]
public void Controllers_ShouldNotAccessRepositoriesDirectly()
{
    var assembly = Assembly.GetExecutingAssembly();
    var validator = new ArchitectureValidator();
    
    validator.AddRule(new NoDirectRepositoryAccessRule());
    
    var result = validator.Validate(assembly);
    
    Assert.True(result.IsValid, 
        string.Join("\n", result.Violations.Select(v => v.Message)));
}

2. Custom Architecture Rules

Problem: Need to enforce custom architecture rules for your project.

// ✅ GOOD: Create custom architecture rule
public class NoDirectDatabaseAccessRule : ArchitectureRule
{
    public NoDirectDatabaseAccessRule()
    {
        Name = "NoDirectDatabaseAccess";
        Description = "Services should not access database directly, use repositories";
    }
    
    public override List<Violation> Validate(Type[] types)
    {
        var violations = new List<Violation>();
        var services = types.Where(t => t.Name.EndsWith("Service"));
        var dbContextType = typeof(DbContext);
        
        foreach (var service in services)
        {
            var fields = service.GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);
            var properties = service.GetProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);
            
            if (fields.Any(f => f.FieldType == dbContextType || 
                               f.FieldType.IsSubclassOf(dbContextType)) ||
                properties.Any(p => p.PropertyType == dbContextType || 
                                   p.PropertyType.IsSubclassOf(dbContextType)))
            {
                violations.Add(new Violation
                {
                    RuleName = Name,
                    TypeName = service.Name,
                    Message = $"Service {service.Name} directly accesses database"
                });
            }
        }
        
        return violations;
    }
}

// Use in tests
[Fact]
public void Services_ShouldNotAccessDatabaseDirectly()
{
    var assembly = Assembly.GetExecutingAssembly();
    var validator = new ArchitectureValidator();
    
    validator.AddRule(new NoDirectDatabaseAccessRule());
    
    var result = validator.Validate(assembly);
    Assert.True(result.IsValid);
}

Real-World Example

// Architecture validation tests
public class ArchitectureTests
{
    private readonly Assembly _assembly = Assembly.GetExecutingAssembly();
    
    [Fact]
    public void Controllers_ShouldNotAccessRepositoriesDirectly()
    {
        var validator = new ArchitectureValidator();
        validator.AddRule(new NoDirectRepositoryAccessRule());
        
        var result = validator.Validate(_assembly);
        
        if (!result.IsValid)
        {
            var messages = string.Join("\n", 
                result.Violations.Select(v => $"- {v.TypeName}: {v.Message}"));
            throw new Exception($"Architecture violations:\n{messages}");
        }
    }
    
    [Fact]
    public void Services_ShouldNotAccessDatabaseDirectly()
    {
        var validator = new ArchitectureValidator();
        validator.AddRule(new NoDirectDatabaseAccessRule());
        
        var result = validator.Validate(_assembly);
        Assert.True(result.IsValid);
    }
    
    [Fact]
    public void AllRules_ShouldPass()
    {
        var validator = new ArchitectureValidator();
        
        // Add all architecture rules
        validator.AddRule(new NoDirectRepositoryAccessRule());
        validator.AddRule(new NoDirectDatabaseAccessRule());
        // Add more rules...
        
        var result = validator.Validate(_assembly);
        
        Assert.True(result.IsValid, 
            $"Architecture validation failed with {result.Violations.Count} violations:\n" +
            string.Join("\n", result.Violations.Select(v => 
                $"- [{v.RuleName}] {v.TypeName}: {v.Message}")));
    }
}

// Example of violation that would be caught:
// ❌ BAD: Controller accessing repository directly
public class UsersController : ControllerBase
{
    private readonly IUserRepository _repository; // Violation!
    
    [HttpGet]
    public IActionResult GetUsers()
    {
        return Ok(_repository.GetAll()); // Should use service
    }
}

// ✅ GOOD: Controller uses service
public class UsersController : ControllerBase
{
    private readonly IUserService _userService;
    
    [HttpGet]
    public IActionResult GetUsers()
    {
        return Ok(_userService.GetUsers());
    }
}

Built-in Rules

NoDirectRepositoryAccessRule

Ensures controllers don't directly access repositories (should use services).

validator.AddRule(new NoDirectRepositoryAccessRule());

Creating Custom Rules

public class MyCustomRule : ArchitectureRule
{
    public MyCustomRule()
    {
        Name = "MyCustomRule";
        Description = "Description of what this rule enforces";
    }
    
    public override List<Violation> Validate(Type[] types)
    {
        var violations = new List<Violation>();
        
        // Your validation logic here
        // Check types, fields, properties, methods, etc.
        
        foreach (var type in types)
        {
            if (/* violation condition */)
            {
                violations.Add(new Violation
                {
                    RuleName = Name,
                    TypeName = type.Name,
                    Message = "Description of violation"
                });
            }
        }
        
        return violations;
    }
}

Best Practices

  1. Run architecture tests in CI/CD to catch violations early
  2. Create custom rules for your project's specific architecture
  3. Fail builds on violations to enforce architecture
  4. Document rules so team understands what's being enforced
  5. Review violations regularly to identify patterns
  6. Update rules as architecture evolves

API Reference

  • ArchitectureValidator - Validates architecture rules
  • ArchitectureRule - Base class for architecture rules
  • NoDirectRepositoryAccessRule - Built-in rule for repository access
  • ValidationResult - Result of architecture validation
  • Violation - Represents an architecture violation
Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 124 12/29/2025

Initial release. See README for details.