ArchitectureAssert Class

The assertions on the dependencies of the types of an ArchitectureModel, available as Assert.Architecture. Each assertion throws an AssertionException that lists every type that breaks it, with the statements that cause it. To check the names or the shape of types, use Assert.All with the members of TypeElement.

public sealed class ArchitectureAssert

Remarks

Each assertion fails when the types to check are empty, so that a typo in a namespace does not make it pass silently. Add your own assertions with an extension block on ArchitectureAssert.

Methods

NameDescription
DependsOnlyOn(IEnumerable<TypeElement>, IEnumerable<TypeElement>, string?, string?, string?)Asserts that the types only use the allowed types and each other. Only the types of the loaded assemblies are checked: use the overload that takes a predicate to also check the types of .NET and of the packages.
DependsOnlyOn(IEnumerable<TypeElement>, Func<TypeReference, bool>, string?, string?, string?)Asserts that the types only use each other and the types that match a predicate, including the types defined outside the loaded assemblies.
DoesNotAccessMember(IEnumerable<TypeElement>, Type, string, string?, string?)Asserts that the types do not use a member of type: call a method or a constructor, read or write a field, a property or an event.
DoesNotAccessMember<T>(IEnumerable<TypeElement>, string, string?, string?)Asserts that the types do not use a member of T: call a method or a constructor, read or write a field, a property or an event.
DoesNotDependOn(IEnumerable<TypeElement>, IEnumerable<TypeElement>, string?, string?, string?)Asserts that the types do not use any of the forbidden types. The dependencies between the checked types themselves are ignored.
DoesNotDependOn(IEnumerable<TypeElement>, Func<TypeReference, bool>, string?, string?, string?)Asserts that the types do not use any type that matches a predicate. Unlike the other overloads, the predicate also sees the types defined outside the loaded assemblies.
DoesNotDependOn(IEnumerable<TypeElement>, Type, string?, string?)Asserts that the types do not use type.
DoesNotDependOn<T>(IEnumerable<TypeElement>, string?, string?)Asserts that the types do not use T.
DoesNotDependOnEachOther(IEnumerable<IGrouping<string, TypeElement>>, string?, string?)Asserts that the slices do not use each other at all. Each type that uses another slice is reported.
DoesNotHaveCycles(IEnumerable<IGrouping<string, TypeElement>>, string?, string?)Asserts that the slices do not depend on each other in a cycle (Orders → Billing → Orders). A slice may use another one. Each group of slices that depend on each other is reported once, with one of the shortest cycles and the dependencies that make it.

DependsOnlyOn overloads

DependsOnlyOn(IEnumerable<TypeElement>, IEnumerable<TypeElement>, string?, string?, string?)

ArchitectureAssert.cs

Asserts that the types only use the allowed types and each other. Only the types of the loaded assemblies are checked: use the overload that takes a predicate to also check the types of .NET and of the packages.

public void DependsOnlyOn(IEnumerable<TypeElement> types, IEnumerable<TypeElement> allowedTypes, string? message = null, string? typesExpression = null, string? allowedTypesExpression = null)
Parameters
types IEnumerable<TypeElement>
The types to check.
allowedTypes IEnumerable<TypeElement>
The types that may be used.
message string?
The message that describes the failure.
typesExpression string?
The expression of types.
allowedTypesExpression string?
The expression of allowedTypes.
Exceptions
AssertionException
A type uses a type that is not allowed, or types is empty.

DependsOnlyOn(IEnumerable<TypeElement>, Func<TypeReference, bool>, string?, string?, string?)

ArchitectureAssert.cs

Asserts that the types only use each other and the types that match a predicate, including the types defined outside the loaded assemblies.

public void DependsOnlyOn(IEnumerable<TypeElement> types, Func<TypeReference, bool> predicate, string? message = null, string? typesExpression = null, string? predicateExpression = null)
Parameters
types IEnumerable<TypeElement>
The types to check.
predicate Func<TypeReference, bool>
Returns true for an allowed type, such as t => t.IsInNamespaceOrChildren("System").
message string?
The message that describes the failure.
typesExpression string?
The expression of types.
predicateExpression string?
The expression of predicate.
Exceptions
AssertionException
A type uses a type that is not allowed, or types is empty.
Example
var architecture = ArchitectureModel.FromAssemblyContaining<Order>();
var domain = architecture.Types.Where(t => t.IsInNamespaceOrChildren("SampleApp.Domain"));

// The types of the domain may use each other and the types of .NET, nothing else
Assert.Architecture.DependsOnlyOn(domain, t => t.IsInNamespaceOrChildren("System"));

DoesNotAccessMember overloads

DoesNotAccessMember(IEnumerable<TypeElement>, Type, string, string?, string?)

ArchitectureAssert.cs

Asserts that the types do not use a member of type: call a method or a constructor, read or write a field, a property or an event.

public void DoesNotAccessMember(IEnumerable<TypeElement> types, Type type, string memberName, string? message = null, string? typesExpression = null)
Parameters
types IEnumerable<TypeElement>
The types to check.
type Type
The type that declares the member.
memberName string
The name of the member (nameof(DateTime.Now)), or .ctor for the constructors.
message string?
The message that describes the failure.
typesExpression string?
The expression of types.
Exceptions
AssertionException
A type uses the member, or types is empty.

DoesNotAccessMember<T>(IEnumerable<TypeElement>, string, string?, string?)

ArchitectureAssert.cs

Asserts that the types do not use a member of T: call a method or a constructor, read or write a field, a property or an event.

public void DoesNotAccessMember<T>(IEnumerable<TypeElement> types, string memberName, string? message = null, string? typesExpression = null)
Type parameters
T
The type that declares the member.
Parameters
types IEnumerable<TypeElement>
The types to check.
memberName string
The name of the member (nameof(DateTime.Now)), or .ctor for the constructors.
message string?
The message that describes the failure.
typesExpression string?
The expression of types.
Exceptions
AssertionException
A type uses the member, or types is empty.
Example
var architecture = ArchitectureModel.FromAssemblyContaining<Order>();
var types = architecture.Types.Where(t => t.IsInNamespaceOrChildren("SampleApp"));

Assert.Architecture.DoesNotAccessMember<DateTime>(types, "Now", "the current time comes from TimeProvider");
Assert.Architecture.DoesNotAccessMember<DateTime>(types, nameof(DateTime.UtcNow), "the current time comes from TimeProvider");

DoesNotDependOn overloads

DoesNotDependOn(IEnumerable<TypeElement>, IEnumerable<TypeElement>, string?, string?, string?)

ArchitectureAssert.cs

Asserts that the types do not use any of the forbidden types. The dependencies between the checked types themselves are ignored.

public void DoesNotDependOn(IEnumerable<TypeElement> types, IEnumerable<TypeElement> forbiddenTypes, string? message = null, string? typesExpression = null, string? forbiddenTypesExpression = null)
Parameters
types IEnumerable<TypeElement>
The types to check.
forbiddenTypes IEnumerable<TypeElement>
The types that must not be used, such as the types of another layer.
message string?
The message that describes the failure.
typesExpression string?
The expression of types.
forbiddenTypesExpression string?
The expression of forbiddenTypes.
Exceptions
AssertionException
A type uses a forbidden type, or types or forbiddenTypes is empty.
Example
var architecture = ArchitectureModel.FromAssemblyContaining<Order>();
var domain = architecture.Types.Where(t => t.IsInNamespaceOrChildren("SampleApp.Domain"));
var infrastructure = architecture.Types.Where(t => t.IsInNamespaceOrChildren("SampleApp.Infrastructure"));

Assert.Architecture.DoesNotDependOn(domain, infrastructure, "the domain stays persistence-ignorant");
Assert.Architecture.DoesNotDependOn(domain, t => t.IsInNamespaceOrChildren("Microsoft.EntityFrameworkCore"));
Assert.Architecture.DoesNotDependOn<HttpClient>(domain);

DoesNotDependOn(IEnumerable<TypeElement>, Func<TypeReference, bool>, string?, string?, string?)

ArchitectureAssert.cs

Asserts that the types do not use any type that matches a predicate. Unlike the other overloads, the predicate also sees the types defined outside the loaded assemblies.

public void DoesNotDependOn(IEnumerable<TypeElement> types, Func<TypeReference, bool> predicate, string? message = null, string? typesExpression = null, string? predicateExpression = null)
Parameters
types IEnumerable<TypeElement>
The types to check.
predicate Func<TypeReference, bool>
Returns true for a forbidden type, such as t => t.IsInNamespaceOrChildren("Microsoft.EntityFrameworkCore").
message string?
The message that describes the failure.
typesExpression string?
The expression of types.
predicateExpression string?
The expression of predicate.
Exceptions
AssertionException
A type uses a forbidden type, or types is empty.

DoesNotDependOn(IEnumerable<TypeElement>, Type, string?, string?)

ArchitectureAssert.cs

Asserts that the types do not use type.

public void DoesNotDependOn(IEnumerable<TypeElement> types, Type type, string? message = null, string? typesExpression = null)
Parameters
types IEnumerable<TypeElement>
The types to check.
type Type
The forbidden type, or a generic type definition (typeof(List<>)) to forbid all its instantiations.
message string?
The message that describes the failure.
typesExpression string?
The expression of types.
Exceptions
AssertionException
A type uses type, or types is empty.

DoesNotDependOn<T>(IEnumerable<TypeElement>, string?, string?)

ArchitectureAssert.cs

Asserts that the types do not use T.

public void DoesNotDependOn<T>(IEnumerable<TypeElement> types, string? message = null, string? typesExpression = null)
Type parameters
T
The forbidden type.
Parameters
types IEnumerable<TypeElement>
The types to check.
message string?
The message that describes the failure.
typesExpression string?
The expression of types.
Exceptions
AssertionException
A type uses T, or types is empty.

DoesNotDependOnEachOther(IEnumerable<IGrouping<string, TypeElement>>, string?, string?)

ArchitectureAssert.cs

Asserts that the slices do not use each other at all. Each type that uses another slice is reported.

public void DoesNotDependOnEachOther(IEnumerable<IGrouping<string, TypeElement>> slices, string? message = null, string? slicesExpression = null)

Parameters

slices IEnumerable<IGrouping<string, TypeElement>>
The slices, such as the result of TypeElementExtensions.SliceByNamespace or of GroupBy. A type must not be in several slices.
message string?
The message that describes the failure.
slicesExpression string?
The expression of slices.

Exceptions

AssertionException
A type uses another slice, or slices is empty.
ArgumentException
Two slices have the same name, or a type is in several slices.

DoesNotHaveCycles(IEnumerable<IGrouping<string, TypeElement>>, string?, string?)

ArchitectureAssert.cs

Asserts that the slices do not depend on each other in a cycle (Orders → Billing → Orders). A slice may use another one. Each group of slices that depend on each other is reported once, with one of the shortest cycles and the dependencies that make it.

public void DoesNotHaveCycles(IEnumerable<IGrouping<string, TypeElement>> slices, string? message = null, string? slicesExpression = null)

Parameters

slices IEnumerable<IGrouping<string, TypeElement>>
The slices, such as the result of TypeElementExtensions.SliceByNamespace or of GroupBy. A type must not be in several slices.
message string?
The message that describes the failure.
slicesExpression string?
The expression of slices.

Exceptions

AssertionException
The slices depend on each other in a cycle, or slices is empty.
ArgumentException
Two slices have the same name, or a type is in several slices.

Example

var architecture = ArchitectureModel.FromAssemblyContaining<Order>();
var modules = architecture.Types.SliceByNamespace("SampleApp.Modules.{slice}");

var exception = Assert.Throws<AssertionException>(() => Assert.Architecture.DoesNotHaveCycles(modules));
Assert.StartsWith("""
    Assert.Architecture.DoesNotHaveCycles() assertion failed: 1 cycle between slices.
    Slices expression: modules
    - Billing -> Shipping -> Billing
        SampleApp.Modules.Billing.Invoice.Shipment has a property of type SampleApp.Modules.Shipping.Shipment
    """.ReplaceLineEndings("\n"), exception.Message);