In the early evolution of multi-tenant enterprise SaaS systems, engineering teams frequently fall into the trap of tenant branching pollution:
- Code-level hardcoded pollution: To satisfy bespoke risk management requirements across different enterprise clients, developers clutter single validator classes with brittle conditionals such as
if (tenantId == "1000001") { ... }orWhen(t => ...), rapidly degrading core invariants into an unmaintainable state. - Release cycle bottlenecks: When an enterprise client mandates that cross-border arbitrations involving claims $\ge 50$ million must upload a formal Conflict of Interest Waiver, the engineering team is forced to rebuild, regression-test, and deploy a complete backend container release.
- Zero regulatory tolerance: In legal technology and financial systems, representing an adversary against an active retainer client triggers immediate statutory disciplinary action, disbarment, and severe civil liability.
BitzOrcas.Modern introduces the “4-Tier Business Validation Golden Pattern.” It strictly partitions validation responsibilities into four cohesive tiers:
- Tier 1 (Universal Base Rules): Mandatory compile-time static invariants bound to the command;
- Tier 2(A) (Composite & Cascading Code Strategies): Domain-specific cross-entity invariants enforced via code;
- Tier 2(B) (Tenant Hot-Config Strategies): Thresholds and feature flags that dynamically update via config providers without redeployment;
- Tier 3 (Field Delivery No-Code Rule Engine): Runtime JSON regex and presence rules configured directly through admin consoles.
This tutorial guides you through building a production-grade multi-tenant validation pipeline grounded in Litigation Matter Intake and Adversary Conflict Blocking.
Multi-Tenant 4-Tier Validation Architecture
Step 1: Define the Matter Intake Command Contract
The intake command encapsulates claim values, client names, adversaries, and optional conflict waiver references:
using BitzOrcas.Application.Authorization;using BitzOrcas.Domain.Abstractions;using BitzOrcas.Domain.Results;using BitzOrcas.Endpoint.Attributes;using Mediator;
namespace BitzOrcas.Modules.Legal.Application.Commands.CreateMatterIntake;
public enum MatterCategory{ Litigation = 1, // Civil/Commercial Litigation CrossBorder = 2, // International Arbitration NonLitigation = 3 // Advisory and M&A}
[GenerateEndpoint(HttpRoute.Post, "/api/legal/matters", Tag = "LegalMatter")]public sealed record CreateMatterIntakeCommand( string MatterTitle, string ClientName, string OpposingParty, decimal ClaimAmount, MatterCategory Category, string? ConflictWaiverDocumentId = null) : ICommand<Result<string>>, IAuthorizedRequest{ public ResourceDescriptor Resource { get; } = new("Legal", "Matter"); public AuthorizationAction Action { get; } = AuthorizationAction.Create;}Step 2: Implement Tier 1 Universal Base Rules
Invariants mandatory across all tenants are declared via IRequestRule<TRequest>:
using BitzOrcas.Domain.Results;
namespace BitzOrcas.Modules.Legal.Application.Commands.CreateMatterIntake.Rules;
/// <summary>/// Static validation error catalog for legal matter intake./// </summary>public static class LegalValidationErrors{ /// <summary> /// Matter title is invalid /// </summary> public static readonly Error InvalidTitle = Error.Validation("Legal.Validation.InvalidTitle", "Matter title cannot be empty or exceed 200 characters.");
/// <summary> /// Both retaining client and opposing party are mandatory /// </summary> public static readonly Error PartiesRequired = Error.Validation("Legal.Validation.PartiesRequired", "Both retaining client and opposing party are mandatory.");
/// <summary> /// Adversary conflict of interest detected /// </summary> public static readonly Error AdversaryConflict = Error.Conflict("Legal.Validation.AdversaryConflict", "Retaining client and opposing party cannot be identical.");
/// <summary> /// Claim amount must be strictly greater than zero /// </summary> public static readonly Error InvalidAmount = Error.Validation("Legal.Validation.InvalidAmount", "Claim amount must be strictly greater than zero.");
/// <summary> /// Conflict waiver document is required for high-value cross-border matters /// </summary> public static readonly Error WaiverRequired = Error.Validation("Legal.Validation.WaiverRequired", "Cross-border disputes with claim values exceeding 50M mandate an executed Conflict Waiver document.");}using BitzOrcas.Application.Validation;using BitzOrcas.Domain.Results;
namespace BitzOrcas.Modules.Legal.Application.Commands.CreateMatterIntake.Rules;
/// <summary>/// Tier 1: Invariant validation rules enforced across all tenants./// </summary>public sealed class CreateMatterIntakeBaseRule : IRequestRule<CreateMatterIntakeCommand>{ public ValueTask<Result> ValidateAsync(CreateMatterIntakeCommand request, CancellationToken cancellationToken) { if (string.IsNullOrWhiteSpace(request.MatterTitle) || request.MatterTitle.Length > 200) { return ValueTask.FromResult(Result.Failure(LegalValidationErrors.InvalidTitle)); }
if (string.IsNullOrWhiteSpace(request.ClientName) || string.IsNullOrWhiteSpace(request.OpposingParty)) { return ValueTask.FromResult(Result.Failure(LegalValidationErrors.PartiesRequired)); }
// Base Invariant: Client and opposing party cannot be identical legal entities if (string.Equals(request.ClientName.Trim(), request.OpposingParty.Trim(), StringComparison.OrdinalIgnoreCase)) { return ValueTask.FromResult(Result.Failure(LegalValidationErrors.AdversaryConflict)); }
if (request.ClaimAmount <= 0) { return ValueTask.FromResult(Result.Failure(LegalValidationErrors.InvalidAmount)); }
return ValueTask.FromResult(Result.Success()); }}Step 3: Implement Tier 2 Tenant-Specific Strategy (ITenantValidationStrategy)
In BitzOrcas.Modern, tenant-specific customizations never pollute base rules. By implementing ITenantValidationStrategy<TRequest>, the engine queries the active tenant context (ICurrentTenant) to append tenant-specific rules:
using BitzOrcas.Application.Abstractions.Tenancy;using BitzOrcas.Application.Validation;using BitzOrcas.Domain.Results;using BitzOrcas.Modules.Legal.Application.Commands.CreateMatterIntake.Rules;
namespace BitzOrcas.Modules.Legal.Application.Commands.CreateMatterIntake.Strategies;
/// <summary>/// Tier 2: Tenant-specific rule strategy dispatcher./// </summary>public sealed class TenantMatterValidationStrategy : ITenantValidationStrategy<CreateMatterIntakeCommand>{ public IReadOnlyList<IRequestRule<CreateMatterIntakeCommand>> GetTenantRules(ICurrentTenant currentTenant) { var tenant = currentTenant.Tenant; if (!tenant.IsAvailable) return [];
var rules = new List<IRequestRule<CreateMatterIntakeCommand>>();
// Tenant 1000001 (Premier International Firm): High-value matters require mandatory conflict waivers if (tenant.EffectiveTenantId == "1000001") { rules.Add(new HighValueCrossBorderWaiverRule()); }
return rules; }}
/// <summary>/// Tier 2(A): Composite cascading rule enforced for enterprise tenants./// </summary>internal sealed class HighValueCrossBorderWaiverRule : IRequestRule<CreateMatterIntakeCommand>{ public ValueTask<Result> ValidateAsync(CreateMatterIntakeCommand request, CancellationToken cancellationToken) { // Rule: Cross-border arbitration with claim >= 50M mandates an executed Conflict Waiver document if (request.Category == MatterCategory.CrossBorder && request.ClaimAmount >= 50_000_000.00m) { if (string.IsNullOrWhiteSpace(request.ConflictWaiverDocumentId)) { return ValueTask.FromResult(Result.Failure(LegalValidationErrors.WaiverRequired)); } }
return ValueTask.FromResult(Result.Success()); }}Step 4: End-to-End Multi-Tenant Isolation Testing
Assert that tenant-specific rules isolate enforcement to targeted tenants:
using System.Net;using System.Net.Http.Json;using BitzOrcas.Domain.Results;using BitzOrcas.Modules.Legal.Application.Commands.CreateMatterIntake;using BitzOrcas.Modules.Legal.Application.Commands.CreateMatterIntake.Rules;using Shouldly;using Xunit;
public sealed class MultiTenantValidationIntegrationTests : IClassFixture<CustomWebApplicationFactory>{ private readonly CustomWebApplicationFactory _factory;
public MultiTenantValidationIntegrationTests(CustomWebApplicationFactory factory) { _factory = factory; }
[Fact] public async Task StandardTenant_SubmittingHighValueMatter_WithoutWaiver_ShouldSucceed() { // 1. Dispatch high-value claim from standard regional tenant (Tenant 1000002) var client = _factory.CreateAuthenticatedClient(tenantId: "1000002", role: "SuperAdmin"); var command = new CreateMatterIntakeCommand( MatterTitle: "Commercial Construction Dispute", ClientName: "Apex Construction Corp.", OpposingParty: "Municipal Development Group", ClaimAmount: 60_000_000.00m, Category: MatterCategory.CrossBorder, ConflictWaiverDocumentId: null); // Regional tenant does not enforce Tier 2(A) waiver policy
// 2. Dispatch request var response = await client.PostAsJsonAsync("/api/legal/matters", command);
// 3. Assert success: standard tenant is exempt from Tier 2(A) rules response.StatusCode.ShouldBe(HttpStatusCode.OK); }
[Fact] public async Task RedCircleTenant_SubmittingHighValueMatter_WithoutWaiver_ShouldBeBlockedByTier2Rule() { // 1. Dispatch identical high-value claim from enterprise law firm tenant (Tenant 1000001) var client = _factory.CreateAuthenticatedClient(tenantId: "1000001", role: "SuperAdmin"); var command = new CreateMatterIntakeCommand( MatterTitle: "Cross-Border Offshore Fund Dispute", ClientName: "Global Offshore Capital Fund", OpposingParty: "Multinational Asset Group", ClaimAmount: 60_000_000.00m, Category: MatterCategory.CrossBorder, ConflictWaiverDocumentId: null); // Missing required waiver
// 2. Dispatch request var response = await client.PostAsJsonAsync("/api/legal/matters", command);
// 3. Assert failure: Tier 2 rule short-circuits with HTTP 400 Bad Request response.StatusCode.ShouldBe(HttpStatusCode.BadRequest); var result = await response.Content.ReadFromJsonAsync<Result<string>>(); result.ShouldNotBeNull(); result.IsFailure.ShouldBeTrue(); result.Error.Code.ShouldBe(LegalValidationErrors.WaiverRequired.Code); }}Architectural Review & Benefits
- Zero Branching Pollution: Universal invariants (Tier 1) and tenant strategies (Tier 2) are physically separated, eliminating fragile
if (tenantId == ...)anti-patterns. - Deterministic Execution: The pipeline evaluates universal rules first, invoking tenant strategy dispatchers only when base rules pass to maximize throughput.
- Zero-Downtime Adaptability: Coupling dynamic strategies with tenant config providers allows immediate threshold adjustments across active tenants without backend redeployments.