Architecture tests with NetArchTest for enforcing dependency rules and project structure conventions. Covers layer dependency validation and naming rules. Trigger: architecture test, NetArchTest, dependency rule, fitness function.
From dotnet-ai-kitnpx claudepluginhub faysilalshareef/dotnet-ai-kit --plugin dotnet-ai-kitThis skill uses the workspace's default tool permissions.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Compares coding agents like Claude Code and Aider on custom YAML-defined codebase tasks using git worktrees, measuring pass rate, cost, time, and consistency.
NetArchTest validates assembly dependencies and naming conventionsHandler, etc.<!-- Test project .csproj -->
<PackageReference Include="NetArchTest.Rules" />
<PackageReference Include="FluentAssertions" />
namespace {Company}.{Domain}.Tests.Architecture;
public sealed class LayerDependencyTests
{
private static readonly Assembly DomainAssembly =
typeof(Order).Assembly;
private static readonly Assembly ApplicationAssembly =
typeof(CreateOrderCommand).Assembly;
private static readonly Assembly InfrastructureAssembly =
typeof(ApplicationDbContext).Assembly;
[Fact]
public void Domain_ShouldNotDependOn_Application()
{
var result = Types.InAssembly(DomainAssembly)
.ShouldNot()
.HaveDependencyOn(ApplicationAssembly.GetName().Name!)
.GetResult();
result.IsSuccessful.Should().BeTrue(
because: FormatFailures(result));
}
[Fact]
public void Domain_ShouldNotDependOn_Infrastructure()
{
var result = Types.InAssembly(DomainAssembly)
.ShouldNot()
.HaveDependencyOn(InfrastructureAssembly.GetName().Name!)
.GetResult();
result.IsSuccessful.Should().BeTrue(
because: FormatFailures(result));
}
[Fact]
public void Application_ShouldNotDependOn_Infrastructure()
{
var result = Types.InAssembly(ApplicationAssembly)
.ShouldNot()
.HaveDependencyOn(InfrastructureAssembly.GetName().Name!)
.GetResult();
result.IsSuccessful.Should().BeTrue(
because: FormatFailures(result));
}
private static string FormatFailures(TestResult result) =>
string.Join(", ", result.FailingTypeNames ?? []);
}
namespace {Company}.{Domain}.Tests.Architecture;
public sealed class NamingConventionTests
{
[Fact]
public void Handlers_ShouldEndWith_Handler()
{
var result = Types.InAssembly(typeof(CreateOrderHandler).Assembly)
.That()
.ImplementInterface(typeof(IRequestHandler<,>))
.Should()
.HaveNameEndingWith("Handler")
.GetResult();
result.IsSuccessful.Should().BeTrue(
because: FormatFailures(result));
}
[Fact]
public void Validators_ShouldEndWith_Validator()
{
var result = Types.InAssembly(typeof(CreateOrderValidator).Assembly)
.That()
.Inherit(typeof(AbstractValidator<>))
.Should()
.HaveNameEndingWith("Validator")
.GetResult();
result.IsSuccessful.Should().BeTrue(
because: FormatFailures(result));
}
[Fact]
public void Entities_ShouldHave_PrivateSetters()
{
var entityTypes = Types.InAssembly(typeof(Order).Assembly)
.That()
.ResideInNamespace("{Company}.{Domain}.Query.Domain")
.GetTypes();
foreach (var type in entityTypes)
{
var publicSetters = type.GetProperties()
.Where(p => p.SetMethod?.IsPublic == true
&& p.Name != "ETag"); // Cosmos exception
publicSetters.Should().BeEmpty(
because: $"{type.Name} should use private setters");
}
}
}
[Fact]
public void CommandHandlers_ShouldNotDirectly_AccessDbContext()
{
var result = Types.InAssembly(typeof(CreateOrderHandler).Assembly)
.That()
.HaveNameEndingWith("Handler")
.ShouldNot()
.HaveDependencyOn("Microsoft.EntityFrameworkCore")
.GetResult();
result.IsSuccessful.Should().BeTrue(
because: "Handlers should use ICommitEventService, not DbContext directly");
}
[Fact]
public void EventData_ShouldBe_Sealed_Records()
{
var eventDataTypes = Types.InAssembly(typeof(OrderCreatedData).Assembly)
.That()
.ImplementInterface(typeof(IEventData))
.GetTypes();
foreach (var type in eventDataTypes)
{
type.IsSealed.Should().BeTrue(
because: $"{type.Name} should be sealed");
type.IsClass.Should().BeTrue(); // records are classes
}
}
| Anti-Pattern | Correct Approach |
|---|---|
| No architecture tests | Add tests for critical dependency rules |
| Tests only at creation | Run in CI to prevent drift |
| Overly strict rules | Focus on critical boundaries |
| Ignoring test failures | Fix violations or update rules with justification |
# Find NetArchTest
grep -r "NetArchTest" --include="*.csproj" tests/
# Find architecture tests
grep -r "Types.InAssembly\|ShouldNot.*HaveDependencyOn" --include="*.cs" tests/
# Find naming convention tests
grep -r "HaveNameEndingWith" --include="*.cs" tests/