Skip to content

Architecture Test Suites

In Parts 1—4, we learned how to write architecture rules one by one. Whether an Entity is sealed, whether a ValueObject is immutable, whether a DomainService is stateless — understanding and writing each rule is important.

But what if you had to write 21 domain rules from scratch for every real project? Functorium provides pre-built test suites. Just inherit an abstract class and override two properties, and verified rules are instantly applied.

“Understanding rules and writing rules every time are different things. Suites are the fastest way to instantly apply rules you understand.”

  1. DomainArchitectureTestSuite inheritance

    • Override only Architecture and DomainNamespace for automatic application of 21 rules
    • Includes rules for Entity, ValueObject, DomainEvent, Specification, DomainService
  2. Adding custom rules

    • After inheriting the Suite, add project-specific unique rules as [Fact] methods
    • Existing Suite rules and new rules run together
  3. Customizing behavior with virtual properties

    • ValueObjectExcludeFromFactoryMethods: Exclude specific ValueObjects from factory method verification
    • DomainServiceAllowedFieldTypes: Specify allowed field types for DomainService
  • DomainArchitectureTestSuite inheritance: Verify DomainLayerRules domain code with the Suite
  • Adding custom rules: Add AggregateRoot inheritance rules as project-specific rules
05-Architecture-Test-Suites/
├── ArchitectureTestSuites.Tests.Unit/
│ ├── ArchitectureTestSuites.Tests.Unit.csproj # References DomainLayerRules project
│ ├── xunit.runner.json
│ └── ArchitectureTests.cs # Suite inheritance tests
└── index.md

This chapter does not create a separate domain project. It references the DomainLayerRules project from Part 4-01 to apply Suite-based verification to the same domain code.

Suite inheritance takes two steps:

Step 1: Override abstract properties

public sealed class DomainArchitectureRuleTests : DomainArchitectureTestSuite
{
protected override Architecture Architecture { get; } =
new ArchLoader()
.LoadAssemblies(typeof(Order).Assembly)
.Build();
protected override string DomainNamespace { get; } =
typeof(Order).Namespace!;
}

This alone automatically runs 21 [Fact] tests.

CategoryTest CountVerification Content
Entity7AggregateRoot/Entity — public sealed, Create/CreateFromValidated factory, GenerateEntityId attribute, private constructors
ValueObject4public sealed + private constructors, immutability (ImmutabilityRule), Create -> Fin<T>, Validate -> Validation<Error, T>
DomainEvent2sealed record, “Event” suffix
Specification3public sealed, Specification<T> inheritance, domain layer location
DomainService5public sealed, stateless (no instance fields), IObservablePort dependency prohibition, public methods return Fin<T>, not record
PropertyTypeDescription
ArchitectureArchitectureAssembly architecture loaded with ArchLoader
DomainNamespacestringRoot namespace where domain types reside
PropertyDefaultDescription
ValueObjectExcludeFromFactoryMethods[]ValueObject types to exclude from Create/Validate factory method verification
DomainServiceAllowedFieldTypes[]Field types to allow in DomainService’s RequireNoInstanceFields

Customization example:

public sealed class DomainArchTests : DomainArchitectureTestSuite
{
protected override Architecture Architecture { get; } = ...;
protected override string DomainNamespace { get; } = ...;
// UnitOfMeasure is enumeration-style and has no Create/Validate
protected override IReadOnlyList<Type> ValueObjectExcludeFromFactoryMethods =>
[typeof(UnitOfMeasure)];
// Allow ILogger fields in DomainService
protected override string[] DomainServiceAllowedFieldTypes =>
["ILogger"];
}

After inheriting the Suite, add project-specific rules as [Fact] methods. They run alongside the Suite’s 21 rules.

public sealed class DomainArchitectureRuleTests : DomainArchitectureTestSuite
{
protected override Architecture Architecture { get; } = ...;
protected override string DomainNamespace { get; } = ...;
// Project-specific additional rule
[Fact]
public void AggregateRoot_ShouldInherit_AggregateRootBase()
{
ArchRuleDefinition.Classes()
.That()
.ResideInNamespace(DomainNamespace)
.And().AreAssignableTo(typeof(AggregateRoot<>))
.And().AreNotAbstract()
.ValidateAllClasses(Architecture, @class => @class
.RequireInherits(typeof(AggregateRoot<>)),
verbose: true)
.ThrowIfAnyFailures("AggregateRoot Inheritance Rule");
}
}

ApplicationArchitectureTestSuite (4 tests)

Section titled “ApplicationArchitectureTestSuite (4 tests)”

A Suite that verifies the Command/Query pattern structure of the application layer.

public sealed class ApplicationArchitectureRuleTests : ApplicationArchitectureTestSuite
{
protected override Architecture Architecture { get; } =
new ArchLoader()
.LoadAssemblies(typeof(CreateOrderCommand).Assembly)
.Build();
protected override string ApplicationNamespace { get; } =
"MyApp.Application";
}
TestVerification Content
Command_ShouldHave_ValidatorNestedClassIf a Command has a Validator, it must be sealed + implement AbstractValidator
Command_ShouldHave_UsecaseNestedClassCommand must have a Usecase, sealed + implement ICommandUsecase
Query_ShouldHave_ValidatorNestedClassIf a Query has a Validator, it must be sealed + implement AbstractValidator
Query_ShouldHave_UsecaseNestedClassQuery must have a Usecase, sealed + implement IQueryUsecase
PropertyTypeDescription
ArchitectureArchitectureAssembly architecture loaded with ArchLoader
ApplicationNamespacestringRoot namespace where application types reside
AspectManual Rules (Part 4-01~04)Suite Inheritance (This Chapter)
Rule authoringImplement rules one by oneInstantly applied through inheritance
Learning valueUnderstand how each rule worksRapid application to real projects
CustomizationFull freedomVirtual properties + additional [Fact]
MaintenanceManual updates on framework changesAutomatic reflection on framework updates
Recommended scenarioRule learning, special verification needsNew projects, team standard application

Projects where you can see how the Suite pattern is used in practice.

ProjectPathTest Count
LayeredArch HostTests.Hosts/01-SingleHost/Tests/LayeredArch.Tests.Unit/Architecture/42+
ECommerce DDD ExampleDocs.Site/src/content/docs/samples/ecommerce-ddd/Tests/ECommerce.Tests.Unit/Architecture/26+

Both projects inherit DomainArchitectureTestSuite and ApplicationArchitectureTestSuite and add project-specific custom rules.

Q1: Can Suites and manual rules be used together?

Section titled “Q1: Can Suites and manual rules be used together?”

A: Yes, Suite inheritance and manual rule authoring can be combined in the same test project. Rules not provided by the Suite (e.g., layer dependencies, Adapter rules) are written as separate test classes. In practice, it is common to use Suites + manual rules + ArchUnitNET native rules together.

Q2: Can specific tests in a Suite be disabled?

Section titled “Q2: Can specific tests in a Suite be disabled?”

A: xUnit’s [Fact(Skip = "reason")] cannot be applied to inherited tests. To skip specific tests, use virtual properties. For example, exclude specific ValueObjects with ValueObjectExcludeFromFactoryMethods or specify allowed field types with DomainServiceAllowedFieldTypes.

Q3: Does using Suites make learning Parts 1—4 unnecessary?

Section titled “Q3: Does using Suites make learning Parts 1—4 unnecessary?”

A: No. Suites are tools for rapidly applying verified rules, but understanding how each API works is essential for diagnosing the cause when a rule is violated. Learning Parts 1—4 is essential regardless of whether Suites are used.

Q4: In what order should architecture tests be introduced to a new project?

Section titled “Q4: In what order should architecture tests be introduced to a new project?”

A: 1) Inherit DomainArchitectureTestSuite for instant domain rule application, 2) Inherit ApplicationArchitectureTestSuite for application rules, 3) Add project-specific custom rules, 4) Add layer dependency rules with ArchUnitNET native API. Starting with Suites secures maximum verification with minimal code.


Using Suites significantly reduces the cost of introducing architecture rules to new projects. Understanding the rules (Parts 1—4), instantly applying them with Suites (this chapter), and extending as needed is the most effective pattern in practice.

-> Part 5 Ch 1: Best Practices