Skip to content

Use-Case Pipeline Constraints

A practical guide from C# generic variance to solving Mediator Pipeline constraints


Fin<T> is a sealed struct and cannot be used as a constraint. To achieve type-safe response handling in a Mediator Pipeline without reflection, you need an interface hierarchy design that works around this single-line constraint.

This tutorial is a practical guide designed for step-by-step learning, starting from the fundamentals of C# generic variance (covariance/contravariance) and progressing through designing the IFinResponse interface hierarchy and applying Pipeline constraints. Through 20 hands-on projects, you will systematically learn the complete path from variance fundamentals to Fin<T> limitations to IFinResponse hierarchy to Pipeline constraints to real-world use cases.

Fin<T> is a sealed struct and cannot be used as a constraint — experience the entire process of IFinResponse interface hierarchy design that began from this single-line constraint.

LevelAudienceRecommended Scope
BeginnerDevelopers who know basic C# syntax and want to learn generic variancePart 1
IntermediateDevelopers interested in Mediator Pipeline and type constraint designParts 1—3
AdvancedDevelopers who want to apply Pipeline architecture and functional patterns in productionParts 4—5 + Appendix

After completing this tutorial, you will be able to:

  1. Understand the principles and application conditions of C# generic variance (covariance, contravariance, invariance)
  2. Master the sealed struct constraint limitations and design patterns to work around them with interface hierarchies
  3. Design the IFinResponse interface hierarchy yourself and implement the CRTP factory pattern
  4. Design minimal constraint conditions per Pipeline for type-safe Pipelines without reflection
  5. Integrate the full Pipeline flow with Command/Query Use Cases based on FinResponse

Introduce why type-safe pipelines are needed and provide an overview of the architecture.

Learn the core concepts of C# generic variance through code.

ChTopicKey Learning
1Covariance (out)IEnumerable<out T>, output position, Dog->Animal assignment
2Contravariance (in)Action<in T>, IHandler<in T>, handler substitution
3Invariance and ConstraintsList<T> invariance, sealed struct constraint impossibility, where constraints
4Interface Segregation and Variance CompositionRead(out)/Write(in)/Factory separation, ISP+variance

Part 2: Problem Definition — Fin and Mediator Collision

Section titled “Part 2: Problem Definition — Fin and Mediator Collision”

Analyze the constraint collision between Fin<T> sealed struct and Mediator Pipeline.

ChTopicKey Learning
1Mediator Pipeline Behavior StructureIPipelineBehavior, MessageHandlerDelegate, constraint roles
2Limitations of Using Fin<T> Directlysealed struct constraint impossibility, reflection needed in 3 places
3Limitations of IFinResponse WrapperDual interface, reflection in 1 place, CreateFail impossible
4Requirements Summary4 requirements, Pipeline capability matrix

Part 3: Solution — IFinResponse Hierarchy Design

Section titled “Part 3: Solution — IFinResponse Hierarchy Design”

Design the IFinResponse interface hierarchy that enables type-safe Pipelines without reflection.

ChTopicKey Learning
1IFinResponse Non-Generic MarkerIsSucc/IsFail, Pipeline read-only access
2IFinResponse<out A> Covariant Interfaceout applied, covariant Pipeline access
3IFinResponseFactory CRTP Factorystatic abstract, CRTP, CreateFail
4IFinResponseWithError Error AccessError property, implemented only on Fail, pattern matching
5FinResponse<A> Discriminated UnionSucc/Fail sealed records, Match/Map/Bind, implicit conversion

Part 4: Pipeline Constraint Pattern Application

Section titled “Part 4: Pipeline Constraint Pattern Application”

Apply minimal constraint conditions to each Pipeline using the IFinResponse hierarchy.

ChTopicKey Learning
1Create-Only Constraintwhere TResponse : IFinResponseFactory<TResponse>
2Read+Create Constraintwhere TResponse : IFinResponse, IFinResponseFactory<TResponse>
3Transaction/Caching PipelineCommand/Query branching, ICacheable conditional
4Fin -> FinResponse BridgeToFinResponse() extension method, cross-layer conversion

Complete Command/Query Use-Case examples integrating the full Pipeline.


[Part 1] Generic Variance Foundations Ch 1: Covariance (out) -> Ch 2: Contravariance (in) -> Ch 3: Invariance and Constraints -> Ch 4: Interface Segregation and Variance Composition

[Part 2] Problem Definition — Fin and Mediator Collision Ch 1: Mediator Pipeline Behavior Structure -> Ch 2: Limitations of Using Fin<T> Directly -> Ch 3: Limitations of IFinResponse Wrapper -> Ch 4: Requirements Summary

[Part 3] IFinResponse Hierarchy Design Ch 1: IFinResponse Non-Generic Marker -> Ch 2: IFinResponse<out A> Covariant Interface -> Ch 3: IFinResponseFactory CRTP Factory -> Ch 4: IFinResponseWithError Error Access -> Ch 5: FinResponse<A> Discriminated Union

[Part 4] Pipeline Constraint Pattern Application Ch 1: Create-Only Constraint -> Ch 2: Read+Create Constraint -> Ch 3: Transaction/Caching Pipeline -> Ch 4: Fin -> FinResponse Bridge

[Part 5] Practical Use-Case Examples Ch 1: Command Use-Case Complete Example -> Ch 2: Query Use-Case Complete Example -> Ch 3: Full Pipeline Integration


IFinResponse Non-generic marker (IsSucc/IsFail)
├── IFinResponse<out A> Covariant interface (read-only)
IFinResponseFactory<TSelf> CRTP factory (CreateFail)
IFinResponseWithError Error access (Error property)
FinResponse<A> Discriminated Union
├── : IFinResponse<A> Covariant interface implementation
├── : IFinResponseFactory<FinResponse<A>> CRTP factory implementation
├── sealed record Succ(A Value) Success case
└── sealed record Fail(Error Error) Failure case
└── : IFinResponseWithError Error access only on Fail
Pipeline TResponse Constraint Capability
────────────────────────── ───────────────────────────────────── ────────────
Metrics Pipeline IFinResponse, IFinResponseFactory<...> Read + Create
Tracing Pipeline IFinResponse, IFinResponseFactory<...> Read + Create
Logging Pipeline IFinResponse, IFinResponseFactory<...> Read + Create
Validation Pipeline IFinResponseFactory<TResponse> CreateFail
Caching Pipeline IFinResponse, IFinResponseFactory<...> Read + Create
Exception Pipeline IFinResponseFactory<TResponse> CreateFail
Transaction Pipeline IFinResponse, IFinResponseFactory<...> Read + Create
Custom Pipeline (User-defined) Varies

  • .NET 10.0 SDK or later
  • VS Code + C# Dev Kit extension
  • Basic knowledge of C# syntax
  • Basic understanding of generics (type parameters, where constraints)

usecase-pipeline/
├── Part0-Introduction/ # Part 0: Introduction (3)
├── Part1-Generic-Variance-Foundations/ # Part 1: Generic Variance Foundations (4)
│ ├── 01-Covariance/
│ ├── 02-Contravariance/
│ ├── 03-Invariance-And-Constraints/
│ └── 04-Interface-Segregation-And-Variance/
├── Part2-Problem-Definition/ # Part 2: Problem Definition (4)
│ ├── 01-Mediator-Pipeline-Structure/
│ ├── 02-Fin-Direct-Limitation/
│ ├── 03-IFinResponse-Wrapper-Limitation/
│ └── 04-pipeline-requirements-summary.md
├── Part3-IFinResponse-Hierarchy/ # Part 3: IFinResponse Hierarchy (5)
│ ├── 01-IFinResponse-Marker/
│ ├── 02-IFinResponse-Covariant/
│ ├── 03-IFinResponseFactory-CRTP/
│ ├── 04-IFinResponseWithError/
│ └── 05-FinResponse-Discriminated-Union/
├── Part4-Pipeline-Constraint-Patterns/ # Part 4: Pipeline Constraints (4)
│ ├── 01-Create-Only-Constraint/
│ ├── 02-Read-Create-Constraint/
│ ├── 03-Transaction-Caching-Pipeline/
│ └── 04-Fin-To-FinResponse-Bridge/
├── Part5-Practical-Usecase-Examples/ # Part 5: Practical Examples (3)
│ ├── 01-Command-Usecase-Example/
│ ├── 02-Query-Usecase-Example/
│ └── 03-Full-Pipeline-Integration/
├── Appendix/ # Appendix
└── README.md # This document

All example projects in every Part include unit tests. Tests follow the Unit Testing Guide.

Terminal window
# Test the entire tutorial
dotnet test --solution usecase-pipeline.slnx

Part 1: Generic Variance Foundations (4)

ChTest ProjectKey Test Content
1Covariance.Tests.UnitCovariance, IEnumerable<out T> assignment
2Contravariance.Tests.UnitContravariance, Action<in T> handler substitution
3InvarianceAndConstraints.Tests.UnitInvariance, sealed struct constraint impossibility
4InterfaceSegregationAndVariance.Tests.UnitISP + variance composition

Part 2: Problem Definition (3)

ChTest ProjectKey Test Content
1MediatorPipelineStructure.Tests.UnitIPipelineBehavior structure verification
2FinDirectLimitation.Tests.UnitFin<T> direct usage limitation verification
3FinResponseWrapperLimitation.Tests.UnitIFinResponse wrapper limitation verification

Part 3: IFinResponse Hierarchy Design (5)

ChTest ProjectKey Test Content
1FinResponseMarker.Tests.UnitIFinResponse marker IsSucc/IsFail
2FinResponseCovariant.Tests.UnitIFinResponse<out A> covariant interface
3FinResponseFactoryCrtp.Tests.UnitCRTP factory CreateFail
4FinResponseWithError.Tests.UnitIFinResponseWithError error access
5FinResponseDiscriminatedUnion.Tests.UnitFinResponse<A> DU, Match/Map/Bind

Part 4: Pipeline Constraint Patterns (4)

ChTest ProjectKey Test Content
1CreateOnlyConstraint.Tests.UnitIFinResponseFactory constraint verification
2ReadCreateConstraint.Tests.UnitIFinResponse + Factory constraint verification
3TransactionCachingPipeline.Tests.UnitTransaction/Caching Pipeline branching
4FinToFinResponseBridge.Tests.UnitFin->FinResponse conversion bridge

Part 5: Practical Use-Case Examples (3)

ChTest ProjectKey Test Content
1CommandUsecaseExample.Tests.UnitCommand Use Case full Pipeline
2QueryUsecaseExample.Tests.UnitQuery Use Case full Pipeline
3FullPipelineIntegration.Tests.UnitFull Pipeline flow integration

Follows the T1_T2_T3 naming convention:

// Method_ExpectedResult_Scenario
[Fact]
public void Assign_Succeeds_WhenCovarianceApplies()
{
// Arrange
IEnumerable<Animal> animals;
// Act
animals = new List<Dog>();
// Assert
animals.ShouldNotBeNull();
}

All example code for this tutorial can be found in the Functorium project:

  • IFinResponse interfaces: Src/Functorium/Applications/Usecases/IFinResponse.cs
  • FinResponse implementation: Src/Functorium/Applications/Usecases/IFinResponse.Impl.cs
  • Static factory: Src/Functorium/Applications/Usecases/IFinResponse.Factory.cs
  • Fin->FinResponse conversion: Src/Functorium/Applications/Usecases/IFinResponse.FinConversions.cs
  • Command/Query interfaces: Src/Functorium/Applications/Usecases/ICommandRequest.cs, IQueryRequest.cs
  • Pipeline implementation: Src/Functorium.Adapters/Observabilities/Pipelines/

This tutorial is more effective when studied together with:


This tutorial was written based on the IFinResponse interface hierarchy design experience in the Functorium project.