Resrcify.SharedKernel.ArchitectureTesting 4.7.0

dotnet add package Resrcify.SharedKernel.ArchitectureTesting --version 4.7.0
                    
NuGet\Install-Package Resrcify.SharedKernel.ArchitectureTesting -Version 4.7.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="Resrcify.SharedKernel.ArchitectureTesting" Version="4.7.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Resrcify.SharedKernel.ArchitectureTesting" Version="4.7.0" />
                    
Directory.Packages.props
<PackageReference Include="Resrcify.SharedKernel.ArchitectureTesting" />
                    
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 Resrcify.SharedKernel.ArchitectureTesting --version 4.7.0
                    
#r "nuget: Resrcify.SharedKernel.ArchitectureTesting, 4.7.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 Resrcify.SharedKernel.ArchitectureTesting@4.7.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=Resrcify.SharedKernel.ArchitectureTesting&version=4.7.0
                    
Install as a Cake Addin
#tool nuget:?package=Resrcify.SharedKernel.ArchitectureTesting&version=4.7.0
                    
Install as a Cake Tool

Resrcify.SharedKernel.ArchitectureTesting

Architecture tests for a layered Clean Architecture service, ready to inherit. Subclass a base in the service's architecture-test project and xUnit runs its tests there; no rules to write.

What you get

Base class Checks
ConventionalLayerDependencyTests Dependencies point inward: a layer never depends on a higher tier or on a peer (Domain 0, Application 1, Persistence 2, Infrastructure and Presentation 3, Web 4), Web never reaches Domain and Presentation never reaches Persistence. Also which SharedKernel packages each layer may use (a custom layer: what the layers at its tier may use).
ConventionalDomainTests Domain events are sealed and end with Event; entities have a private constructor.
ConventionalApplicationTests Commands, queries, requests and their handlers are sealed and named by convention (…Command, …CommandHandler, …Query, …QueryHandler, …Request, …RequestHandler); handlers return Task<Result>. In every layer, a request handler doesn't depend on ISender or IMediator (below).
ConventionalConfigurationTests In every layer, no type takes IConfiguration in a constructor: services get validated options (IOptions<T>); configuration is read at registration (below).
ConventionalPresentationTests Endpoints implement IEndpoint, are sealed, expose a single map method and go through the mediator; so do controllers.

The service's layer assemblies are found by name: a test project called Titan.Shard.ArchitectureTests uses every referenced assembly named Titan.Shard.<Layer>. A layer the service doesn't have is skipped.

Install

dotnet add package Resrcify.SharedKernel.ArchitectureTesting

Reference each layer project from the architecture-test project, so its assembly is found.

Use

One class per base:

public sealed class LayerTests : ConventionalLayerDependencyTests;
public sealed class DomainTests : ConventionalDomainTests;
public sealed class ApplicationTests : ConventionalApplicationTests;
public sealed class PresentationTests : ConventionalPresentationTests;
public sealed class ConfigurationTests : ConventionalConfigurationTests;

Every test is virtual; skip one the service deliberately breaks, with the reason:

[SkippableFact]
public override void Commands_Should_HaveCommandPostfix()
    => Skip.If(true, "This service names its commands after the RPC.");

Add a layer, or move one to another tier, by overriding LayerTiers; add rules the tiers can't express with ExtraForbiddenDependencies:

using Resrcify.SharedKernel.ArchitectureTesting.Layering;   // Layers
using Resrcify.SharedKernel.ArchitectureTesting.Tests;

public sealed class LayerTests : ConventionalLayerDependencyTests
{
    protected override IReadOnlyDictionary<string, int> LayerTiers
        => new Dictionary<string, int>(base.LayerTiers) { ["Bot"] = 4 };

    protected override IEnumerable<(string Source, string Target)> ExtraForbiddenDependencies
        => [.. base.ExtraForbiddenDependencies, ("Bot", Layers.Domain)];
}

A layer added this way also gets the SharedKernel package restrictions (SharedKernelPackageRestrictions, which name only the conventional layers): those of the conventional layers at its tier. The Bot above, at tier 4, may use what Web may use; a layer at tier 3 is restricted like Infrastructure and Presentation together; one at a tier without a conventional layer, like the nearest tier's (the inner one on a tie). A failure says where the rule came from (Bot → Repository: … (restricted like Web; …)). Choose the layer yourself with PackageRestrictionsLike:

protected override IReadOnlyDictionary<string, string> PackageRestrictionsLike
    => new Dictionary<string, string> { ["Client"] = Layers.Infrastructure };

A custom layer that an overridden SharedKernelPackageRestrictions already names is left as configured there (unless PackageRestrictionsLike maps it), so an override that adds it by hand keeps working; it is no longer needed.

To check assemblies that aren't named <Root>.<Layer>, pass them to the base's constructor (ConventionalLayerDependencyTests(IReadOnlyDictionary<string, Assembly> layerAssemblies), also on ConventionalApplicationTests and BaseArchitectureTest).

Handlers don't send requests

ConventionalApplicationTests.Handlers_Should_NotDependOnSenderOrMediator fails for a mediator request handler that depends on ISender or IMediator: a constructor parameter (a primary constructor's too), a field or a property, its base classes' included, of either type, of anything that is one (the mediator itself, an interface deriving from them), or of one wrapped (Lazy<ISender>, Func<IMediator>, an array). The failure names each, by layer:

Infrastructure: Titan.Shard.Infrastructure.Sync.SyncShardCommandHandler → constructor parameter 'sender' (ISender)

A request handler is a type implementing ICommandHandler<>, ICommandHandler<,>, IQueryHandler<,>, IRequestHandler<,>, IValueTaskRequestHandler<,> or IStreamRequestHandler<,>, or a scatter-gather request handler (IScatterGatherRequestHandler<,,,>, IStreamingScatterGatherRequestHandler<,,,>), which is sent like any request.

Why: a request sent from inside a handler hides a workflow in the pipeline. The inner request runs the behaviors again inside the outer one (logging, validation, a second unit of work and transaction, caching), its failure has to be mapped back by hand, and the use case is no longer readable in one place. Only the entry points send: endpoints, Quartz command jobs, bus consumers, UI handlers (a Discord command, an autocomplete). A handler that needs another use case's work does it through that work's abstractions (a repository, a domain service, a client), or the entry point sends both requests.

What the rule allows:

  • IPublisher: publishing a notification (a domain event) isn't sending a request.
  • Integration event handlers (IIntegrationEventHandler<>), scatter-gather event handlers (IScatterGatherHandler<,,>) and request responders (IRequestResponder<,>): they are bus consumers, entry points of their own, so they may send. So may notification handlers (INotificationHandler<>, IDomainEventHandler<>): the rule is about request handlers only.
  • Pipeline behaviors: they are the pipeline, not a handler.
  • A nested type: it is a type of its own. A handler isn't failed for what a type nested in it holds (and a handler nested in a static class is checked like any other).

Every layer assembly is checked, not only Application: a handler can live in Infrastructure (a scatter-gather request handler) or in any assembly the mediator scans. The rule has no allow-list; a service that must keep a sending handler for now skips the test with the reason, as above, until it is moved.

Configuration is read at registration

ConventionalConfigurationTests.Types_Should_NotTakeIConfigurationInAConstructor fails for a class with a constructor (of any visibility) that takes IConfiguration, or anything implementing it (IConfigurationSection, IConfigurationRoot, ConfigurationManager), in any layer assembly:

Infrastructure: Titan.Shard.Infrastructure.SwgohApiClient → constructor parameter 'configuration' (IConfiguration)

A service reads configuration where it registers (Program, the Add* registration extensions) and binds it to options validated at start-up (AddOptions<T>().BindConfiguration(…).ValidateOnStart()), which its classes take as IOptions<T>: a missing or invalid setting then fails the start, not the first request that reads it, and a test hands a class its options without building a configuration. Only constructors are checked: a registration extension method taking IConfiguration as a parameter is where configuration belongs. The generated Program class of a top-level program is left out.

Sample project

samples/Resrcify.SharedKernel.WebApiExample/tests/Resrcify.SharedKernel.WebApiExample.ArchitectureTests in the repository.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  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
4.7.0 53 10/19/2026
4.6.0 61 10/19/2026
4.5.0 138 10/18/2026
4.4.1 77 10/17/2026
4.4.0 66 10/16/2026
4.3.0 74 10/16/2026
4.2.0 87 10/14/2026
4.1.0 78 10/14/2026
4.0.0 113 10/13/2026
3.0.0 151 10/3/2026
2.2.0 129 9/13/2026
2.1.1 1,336 6/8/2026
2.1.0 125 5/24/2026
2.0.0 131 5/22/2026
1.16.3 249 5/7/2026
1.16.2 121 5/7/2026
1.16.1 122 5/7/2026