Load the architecture once per test class to avoid repeated assembly scanning:
private static readonly Architecture Architecture =
new ArchLoader()
.LoadAssemblies(typeof(SomeClassInTargetAssembly).Assembly)
.Build();For multiple assemblies:
private static readonly Architecture Architecture =
new ArchLoader()
.LoadAssemblies(
typeof(Domain.Entity).Assembly,
typeof(Application.Service).Assembly,
typeof(Infrastructure.Repository).Assembly)
.Build();IArchRule rule = Types()
.That()
.Are(predicate)
.Should()
.Be(condition);
rule.Check(Architecture);Using xUnit:
[Fact]
public void Controllers_Should_Not_Depend_On_Repositories()
{
Types()
.That().ResideInNamespace("Controllers")
.Should().NotDependOnAny(Types().That().ResideInNamespace("Repositories"))
.Check(Architecture);
}Using MSTest:
[TestMethod]
public void Controllers_Should_Not_Depend_On_Repositories()
{
Types()
.That().ResideInNamespace("Controllers")
.Should().NotDependOnAny(Types().That().ResideInNamespace("Repositories"))
.Check(Architecture);
}Types().That().ResideInNamespace("MyApp.Domain")
Types().That().ResideInNamespaceContaining("Services")
Types().That().ResideInNamespaceMatching(@"MyApp\..*\.Handlers")Types().That().HaveNameEndingWith("Controller")
Types().That().HaveNameStartingWith("I")
Types().That().HaveNameMatching(@".*Handler$")Types().That().AreClasses()
Types().That().AreInterfaces()
Types().That().AreAbstract()
Types().That().AreSealed()
Types().That().ArePublic()
Types().That().ImplementInterface(typeof(IService))Types().That().AreAssignableTo(typeof(BaseClass))
Types().That().ImplementInterface(typeof(IHandler<>))Types().That().HaveAnyAttributes(typeof(SerializableAttribute))
Types().That().DoNotHaveAnyAttributes(typeof(ObsoleteAttribute)).Should().NotDependOnAny(Types().That().ResideInNamespace("Infrastructure"))
.Should().OnlyDependOn(Types().That().ResideInNamespace("Domain"))
.Should().DependOnAny(Types().That().ResideInNamespace("Contracts")).Should().ResideInNamespace("MyApp.Application")
.Should().ResideInNamespaceContaining("Handlers").Should().HaveNameEndingWith("Service")
.Should().HaveNameStartingWith("I").Should().BePublic()
.Should().BeInternal()
.Should().BeSealed().Should().ImplementInterface(typeof(IDisposable))
.Should().BeAssignableTo(typeof(BaseEntity))Define layers as reusable predicates:
private static readonly IObjectProvider<IType> DomainLayer =
Types().That().ResideInNamespace("MyApp.Domain").As("Domain Layer");
private static readonly IObjectProvider<IType> ApplicationLayer =
Types().That().ResideInNamespace("MyApp.Application").As("Application Layer");
private static readonly IObjectProvider<IType> InfrastructureLayer =
Types().That().ResideInNamespace("MyApp.Infrastructure").As("Infrastructure Layer");
private static readonly IObjectProvider<IType> PresentationLayer =
Types().That().ResideInNamespace("MyApp.Web").As("Presentation Layer");Use layers in rules:
[Fact]
public void Domain_Should_Not_Depend_On_Application()
{
Types().That().Are(DomainLayer)
.Should().NotDependOnAny(ApplicationLayer)
.Check(Architecture);
}Combine multiple conditions:
Types()
.That().ResideInNamespace("MyApp.Domain")
.And().AreClasses()
.And().AreNotAbstract()
.Should().BeSealed()
.OrShould().ImplementInterface(typeof(IEntity))
.Check(Architecture);Create reusable custom predicates:
public static class CustomPredicates
{
public static IPredicate<IType> AreAggregateRoots()
{
return new SimplePredicate<IType>(
type => type.ImplementsInterface(typeof(IAggregateRoot)),
"are aggregate roots");
}
}Use in rules:
Types().That().Are(CustomPredicates.AreAggregateRoots())
.Should().ResideInNamespace("MyApp.Domain.Aggregates")
.Check(Architecture);Check that vertical slices do not depend on each other:
SliceRuleDefinition.ForFunctions()
.Should().NotDependOnEachOther()
.Check(Architecture);Check for circular dependencies:
Types().That().ResideInNamespace("MyApp")
.Should().BeFreeOfCycles()
.Check(Architecture);