Skip to content
bitzorcas
中EN

Concept

Vertical Slice Architecture: High Cohesion Use-Case Centricity

Master Vertical Slice Architecture in BitzOrcas.Modern: One Use Case One File, 10-stage pipeline governance, unified aggregate persistence, and retiring layered models.

Last updated

Traditional layered architectures split code by technical concern (Controllers → Services → Repositories), leading to widespread git conflicts and bloated God Services.

BitzOrcas.Modern practices Vertical Slice Architecture: organizing code along business use cases following “One Use Case, One File”.

Slice Structure & Pipeline Execution Flow

10-Stage Linear Application PipelineSingle Slice File: CreateMatterIntakeCommand.cs

1. [GenerateEndpoint] Minimal API Metadata

2. CreateMatterIntakeCommand (Contract)

3. CreateMatterIntakeCommandRule (Pure Rule)

4. CreateMatterIntakeCommandHandler (Handler)

Logging -> License -> Impersonation -> Auth

Validation (IRequestRule) -> Idempotency

Transaction -> Events -> Audit -> ReadModel


Slice Anatomy: One Use Case, One File Standard Pattern

In BitzOrcas.Modern, a complete Command vertical slice is organized in a single self-contained file:

src/Modules/Legal/BitzOrcas.Modules.Legal/Application/Matters/CreateMatterIntakeCommand.cs
using System;
using System.Threading;
using System.Threading.Tasks;
using BitzOrcas.Application.Abstractions.Tenancy;
using BitzOrcas.Application.Authorization;
using BitzOrcas.Domain.Abstractions;
using BitzOrcas.Domain.Results;
using BitzOrcas.Endpoint.Attributes;
using BitzOrcas.Modules.Legal.Contracts;
using BitzOrcas.Modules.Legal.Domain;
using Mediator;
namespace BitzOrcas.Modules.Legal.Application.Matters;
/// <summary>
/// Command contract for initiating a civil/commercial matter intake
/// </summary>
/// <remarks>
/// Mapped directly into a Minimal API endpoint at compile time by source generators.
/// </remarks>
[GenerateEndpoint(HttpRoute.Post, "/api/legal/matters", Tag = "Matters")]
public sealed record CreateMatterIntakeCommand(
string MatterTitle,
string ClientName,
string OpposingPartyName,
decimal ClaimAmount,
bool IsCrossBorder) : ICommand<Result<string>>, IAuthorizedRequest
{
/// <summary>
/// Protected resource descriptor
/// </summary>
public ResourceDescriptor Resource { get; } = new("legal", "matter");
/// <summary>
/// Required authorization action
/// </summary>
public AuthorizationAction Action { get; } = AuthorizationAction.Create;
}
/// <summary>
/// Pure validation rule executed before reaching the handler
/// </summary>
public sealed class CreateMatterIntakeCommandRule : IRequestRule<CreateMatterIntakeCommand>
{
public Result Validate(CreateMatterIntakeCommand command)
{
// 1. Enforce title length boundaries
if (string.IsNullOrWhiteSpace(command.MatterTitle) || command.MatterTitle.Length > 200)
{
return Result.Failure(LegalErrors.MatterTitleInvalid);
}
// 2. Claim amount must be strictly positive
if (command.ClaimAmount <= 0)
{
return Result.Failure(LegalErrors.ClaimAmountPositive);
}
return Result.Success();
}
}
/// <summary>
/// Command handler orchestrating business domain execution
/// </summary>
/// <remarks>
/// Focuses purely on domain orchestration; cross-cutting concerns are governed by the 10-stage pipeline.
/// </remarks>
public sealed class CreateMatterIntakeCommandHandler(
ICommandRepository<MatterIntake, string> repository,
ICurrentTenant currentTenant) : ICommandHandler<CreateMatterIntakeCommand, Result<string>>
{
public async ValueTask<Result<string>> Handle(
CreateMatterIntakeCommand request,
CancellationToken cancellationToken)
{
var tenant = currentTenant.Tenant;
if (!tenant.IsAvailable)
{
return Result.Failure<string>(LegalErrors.TenantRequired);
}
// 1. Generate sequential business matter code
var matterCode = $"MAT-{DateTimeOffset.UtcNow:yyyyMMdd}-{Random.Shared.Next(10000, 99999)}";
// 2. Validate domain invariants via factory method
var intakeResult = MatterIntake.CreateIntake(
tenantId: tenant.EffectiveTenantId,
matterCode: matterCode,
matterTitle: request.MatterTitle,
clientName: request.ClientName,
opposingPartyName: request.OpposingPartyName,
claimAmount: request.ClaimAmount,
isCrossBorder: request.IsCrossBorder);
if (intakeResult.IsFailure)
{
return Result.Failure<string>(intakeResult.Error);
}
var matter = intakeResult.GetValueOrThrow();
// 3. Commit aggregate via narrow command repository (pipeline handles transactions, outbox, and audit diffs)
var saveResult = await repository.SaveAsync(matter, cancellationToken);
return saveResult.IsFailure
? Result.Failure<string>(saveResult.Error)
: Result.Success(matterCode);
}
}

10-Stage Pipeline Responsibility Matrix

OrderPipeline BehaviorCore ResponsibilityInterception Behavior
1LoggingPipelineBehaviorRecords request latency and binds ambient TraceIdStructured logging
2RuntimeLicensePipelineBehaviorValidates commercial signed license and tenant seatsRejects with 403 on invalid license
3DelegatedSessionRestrictionPipelineBehaviorRestricts write operations during operator impersonationRejects with 403 on restricted writes
4AuthorizationPipelineBehaviorEnforces declarative RBAC/ABAC resource-action permissionsRejects with 403 on unauthorized calls
5ValidationPipelineBehaviorExecutes pure IRequestRule and tenant policy validatorsRejects with 400 on invariant failure
6IdempotencyPipelineBehaviorDistributed Redis anti-replay and concurrent execution locksReturns cached response on duplicate
7TransactionPipelineBehaviorAutomatically wraps execution in ambient DB transactionRolls back on unhandled error
8DomainEventDispatchPipelineBehaviorDispatches domain events and persists CAP outbox framesEnforces atomic delivery
9ActivityAuditPipelineBehaviorRecords operator identity, IP, and before/after state diffsAsynchronous batch persistence
10ReadModelDisplayPipelineBehaviorHydrates display text and multi-language dictionary tagsZero-reflection fast serialization

Summary

Vertical Slice Architecture maximizes velocity and simplifies refactoring:

  • Modifying a feature touches one file;
  • Decommissioning a feature is a single file delete;
  • Cross-cutting governance is handled entirely by the 10-stage pipeline.

100%

Scroll or use controls to zoom · drag when enlarged · double-click for 100% / 200%