Skip to main content
Welcome. This site supports keyboard navigation and screen readers. Press ? at any time for keyboard shortcuts. Press [ to focus the sidebar, ] to focus the content. High-contrast themes are available via the toolbar.
serard@dev00:~/cv

Layer 3: MyApp.Implementation -- Compiler-Enforced Domain Code

This project references MyApp.Specifications (and transitively, MyApp.Requirements). The domain implements spec interfaces. The colon operator IS the guarantee. Every class and method is decorated with [ForRequirement] for full IDE navigability.

namespace MyApp.Domain.Auth;

using MyApp.Specifications;
using MyApp.Requirements.Features;

/// <summary>
/// Domain service implementing user roles.
/// The compiler FORCES this class to implement all three IUserRolesSpec methods.
/// If a new AC is added to the specification interface, this class
/// will not compile until the new method is implemented.
/// </summary>
[ForRequirement(typeof(UserRolesFeature))]
public class AuthorizationService : IUserRolesSpec
{
    private readonly IUserRepository _users;
    private readonly IRoleRepository _roles;
    private readonly IPermissionCache _cache;

    public AuthorizationService(
        IUserRepository users, IRoleRepository roles, IPermissionCache cache)
    {
        _users = users;
        _roles = roles;
        _cache = cache;
    }

    [ForRequirement(typeof(UserRolesFeature), nameof(UserRolesFeature.AdminCanAssignRoles))]
    public Result AssignRole(User actingUser, User targetUser, Role role)
    {
        if (!actingUser.Roles.Any(r => r.Name == "Admin"))
            return Result.Failure("Only admins can assign roles");

        if (role is null)
            return Result.Failure("Role cannot be null");

        // Domain logic: persist the assignment
        _users.AssignRole(targetUser.Id, role.Id);
        return Result.Success();
    }

    [ForRequirement(typeof(UserRolesFeature), nameof(UserRolesFeature.ViewerHasReadOnlyAccess))]
    public Result EnforceReadOnlyAccess(User viewer, Resource resource, Operation operation)
    {
        var isViewer = viewer.Roles.Any(r => r.Name == "Viewer");
        if (isViewer && operation != Operation.Read)
            return Result.Failure($"Viewer cannot perform {operation} on {resource.Id}");

        return Result.Success();
    }

    [ForRequirement(typeof(UserRolesFeature), nameof(UserRolesFeature.RoleChangeTakesEffectImmediately))]
    public Result VerifyImmediateRoleEffect(User user, Role previousRole, Role newRole)
    {
        _cache.Invalidate(user.Id);

        var currentPermissions = _cache.GetPermissions(user.Id);
        var hasOldPermissions = currentPermissions.Intersect(previousRole.Permissions)
            .Except(newRole.Permissions).Any();

        if (hasOldPermissions)
            return Result.Failure("Old permissions still cached after role change");

        return Result.Success();
    }
}

What makes Layer 3 work:

  1. AuthorizationService : IUserRolesSpec -- the compiler forces all three AC methods to exist with the correct signatures.
  2. [ForRequirement(typeof(UserRolesFeature))] -- type reference, not string. Ctrl+Click jumps to the feature. Refactoring tools track it.
  3. Each method is linked to its specific AC via nameof(UserRolesFeature.AdminCanAssignRoles) -- compiler-checked.
  4. If someone adds AC4 to IUserRolesSpec, AuthorizationService fails to compile until AC4 is implemented. This is the entire point.

Layer 4: MyApp.Tests -- Type-Linked Verification

This project references MyApp.Implementation and MyApp.Specifications (and transitively, MyApp.Requirements). Test classes link to requirements through type references.

namespace MyApp.Tests.Auth;

using MyApp.Requirements.Features;
using MyApp.Specifications;
using MyApp.Domain.Auth;

[TestFixture]
[TestsFor(typeof(UserRolesFeature))]
public class UserRolesTests
{
    private AuthorizationService _authService;
    private User _admin;
    private User _viewer;
    private Role _editorRole;

    [SetUp]
    public void Setup()
    {
        _authService = new AuthorizationService(
            new InMemoryUserRepository(),
            new InMemoryRoleRepository(),
            new InMemoryPermissionCache());

        var adminRole = new Role(new RoleId("admin"), "Admin",
            new HashSet<Permission> { new("manage-roles") });
        var viewerRole = new Role(new RoleId("viewer"), "Viewer",
            new HashSet<Permission> { new("read") });

        _admin = new User(new UserId(Guid.NewGuid()), "Alice",
            new HashSet<Role> { adminRole });
        _viewer = new User(new UserId(Guid.NewGuid()), "Bob",
            new HashSet<Role> { viewerRole });
        _editorRole = new Role(new RoleId("editor"), "Editor",
            new HashSet<Permission> { new("read"), new("write") });
    }

    [Test]
    [Verifies(typeof(UserRolesFeature), nameof(UserRolesFeature.AdminCanAssignRoles))]
    public void Admin_can_assign_role_to_another_user()
    {
        var target = new User(new UserId(Guid.NewGuid()), "Charlie", new HashSet<Role>());
        var result = _authService.AssignRole(_admin, target, _editorRole);
        Assert.That(result.IsSuccess, Is.True);
    }

    [Test]
    [Verifies(typeof(UserRolesFeature), nameof(UserRolesFeature.AdminCanAssignRoles))]
    public void Non_admin_cannot_assign_roles()
    {
        var target = new User(new UserId(Guid.NewGuid()), "Charlie", new HashSet<Role>());
        var result = _authService.AssignRole(_viewer, target, _editorRole);
        Assert.That(result.IsSuccess, Is.False);
        Assert.That(result.Reason, Does.Contain("Only admins"));
    }

    [Test]
    [Verifies(typeof(UserRolesFeature), nameof(UserRolesFeature.ViewerHasReadOnlyAccess))]
    public void Viewer_cannot_write()
    {
        var resource = new Resource(new ResourceId("order-123"), "Order");
        var result = _authService.EnforceReadOnlyAccess(_viewer, resource, Operation.Write);
        Assert.That(result.IsSuccess, Is.False);
    }

    [Test]
    [Verifies(typeof(UserRolesFeature), nameof(UserRolesFeature.ViewerHasReadOnlyAccess))]
    public void Viewer_can_read()
    {
        var resource = new Resource(new ResourceId("order-123"), "Order");
        var result = _authService.EnforceReadOnlyAccess(_viewer, resource, Operation.Read);
        Assert.That(result.IsSuccess, Is.True);
    }

    [Test]
    [Verifies(typeof(UserRolesFeature), nameof(UserRolesFeature.RoleChangeTakesEffectImmediately))]
    public void Role_change_invalidates_cache_immediately()
    {
        var previousRole = new Role(new RoleId("viewer"), "Viewer",
            new HashSet<Permission> { new("read") });
        var result = _authService.VerifyImmediateRoleEffect(_admin, previousRole, _editorRole);
        Assert.That(result.IsSuccess, Is.True);
    }
}

What makes Layer 4 work:

  1. [TestsFor(typeof(UserRolesFeature))] -- type reference to the requirement. Source generators enumerate all tests for any feature.
  2. [Verifies(typeof(UserRolesFeature), nameof(UserRolesFeature.AdminCanAssignRoles))] -- the nameof() is compiler-checked. Rename the AC method: this updates. Delete the AC method: compile error.
  3. Multiple tests can verify the same AC (positive and negative cases). The coverage report knows AdminCanAssignRoles has 2 tests.
  4. Tests call domain code through the specification interface -- the same interface the domain was forced to implement.
⬇ Download