| title | 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.
| Level | Audience | Recommended Scope |
|---|---|---|
| Beginner | Developers who know basic C# syntax and want to learn generic variance | Part 1 |
| Intermediate | Developers interested in Mediator Pipeline and type constraint design | Parts 1--3 |
| Advanced | Developers who want to apply Pipeline architecture and functional patterns in production | Parts 4--5 + Appendix |
After completing this tutorial, you will be able to:
- Understand the principles and application conditions of C# generic variance (covariance, contravariance, invariance)
- Master the sealed struct constraint limitations and design patterns to work around them with interface hierarchies
- Design the IFinResponse interface hierarchy yourself and implement the CRTP factory pattern
- Design minimal constraint conditions per Pipeline for type-safe Pipelines without reflection
- 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.
| Ch | Topic | Key Learning |
|---|---|---|
| 1 | Covariance (out) | IEnumerable<out T>, output position, Dog->Animal assignment |
| 2 | Contravariance (in) | Action<in T>, IHandler<in T>, handler substitution |
| 3 | Invariance and Constraints | List<T> invariance, sealed struct constraint impossibility, where constraints |
| 4 | Interface Segregation and Variance Composition | Read(out)/Write(in)/Factory separation, ISP+variance |
Analyze the constraint collision between Fin<T> sealed struct and Mediator Pipeline.
| Ch | Topic | Key Learning |
|---|---|---|
| 1 | Mediator Pipeline Behavior Structure | IPipelineBehavior, MessageHandlerDelegate, constraint roles |
| 2 | Limitations of Using Fin<T> Directly | sealed struct constraint impossibility, reflection needed in 3 places |
| 3 | Limitations of IFinResponse Wrapper | Dual interface, reflection in 1 place, CreateFail impossible |
| 4 | Requirements Summary | 4 requirements, Pipeline capability matrix |
Design the IFinResponse interface hierarchy that enables type-safe Pipelines without reflection.
| Ch | Topic | Key Learning |
|---|---|---|
| 1 | IFinResponse Non-Generic Marker | IsSucc/IsFail, Pipeline read-only access |
| 2 | IFinResponse<out A> Covariant Interface | out applied, covariant Pipeline access |
| 3 | IFinResponseFactory CRTP Factory | static abstract, CRTP, CreateFail |
| 4 | IFinResponseWithError Error Access | Error property, implemented only on Fail, pattern matching |
| 5 | FinResponse<A> Discriminated Union | Succ/Fail sealed records, Match/Map/Bind, implicit conversion |
Apply minimal constraint conditions to each Pipeline using the IFinResponse hierarchy.
| Ch | Topic | Key Learning |
|---|---|---|
| 1 | Create-Only Constraint | where TResponse : IFinResponseFactory<TResponse> |
| 2 | Read+Create Constraint | where TResponse : IFinResponse, IFinResponseFactory<TResponse> |
| 3 | Transaction/Caching Pipeline | Command/Query branching, ICacheable conditional |
| 4 | Fin -> FinResponse Bridge | ToFinResponse() extension method, cross-layer conversion |
Complete Command/Query Use-Case examples integrating the full Pipeline.
- 5.1 Command Use-Case Complete Example
- 5.2 Query Use-Case Complete Example
- 5.3 Full Pipeline Integration
- A. IFinResponse Interface Hierarchy Complete Reference
- B. Pipeline Constraints vs Alternatives Comparison
- C. Railway Oriented Programming Reference
- D. Glossary
- E. References
[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.
# Test the entire tutorial
dotnet test --solution usecase-pipeline.slnxPart 1: Generic Variance Foundations (4)
| Ch | Test Project | Key Test Content |
|---|---|---|
| 1 | Covariance.Tests.Unit |
Covariance, IEnumerable<out T> assignment |
| 2 | Contravariance.Tests.Unit |
Contravariance, Action<in T> handler substitution |
| 3 | InvarianceAndConstraints.Tests.Unit |
Invariance, sealed struct constraint impossibility |
| 4 | InterfaceSegregationAndVariance.Tests.Unit |
ISP + variance composition |
Part 2: Problem Definition (3)
| Ch | Test Project | Key Test Content |
|---|---|---|
| 1 | MediatorPipelineStructure.Tests.Unit |
IPipelineBehavior structure verification |
| 2 | FinDirectLimitation.Tests.Unit |
Fin<T> direct usage limitation verification |
| 3 | FinResponseWrapperLimitation.Tests.Unit |
IFinResponse wrapper limitation verification |
Part 3: IFinResponse Hierarchy Design (5)
| Ch | Test Project | Key Test Content |
|---|---|---|
| 1 | FinResponseMarker.Tests.Unit |
IFinResponse marker IsSucc/IsFail |
| 2 | FinResponseCovariant.Tests.Unit |
IFinResponse<out A> covariant interface |
| 3 | FinResponseFactoryCrtp.Tests.Unit |
CRTP factory CreateFail |
| 4 | FinResponseWithError.Tests.Unit |
IFinResponseWithError error access |
| 5 | FinResponseDiscriminatedUnion.Tests.Unit |
FinResponse<A> DU, Match/Map/Bind |
Part 4: Pipeline Constraint Patterns (4)
| Ch | Test Project | Key Test Content |
|---|---|---|
| 1 | CreateOnlyConstraint.Tests.Unit |
IFinResponseFactory constraint verification |
| 2 | ReadCreateConstraint.Tests.Unit |
IFinResponse + Factory constraint verification |
| 3 | TransactionCachingPipeline.Tests.Unit |
Transaction/Caching Pipeline branching |
| 4 | FinToFinResponseBridge.Tests.Unit |
Fin->FinResponse conversion bridge |
Part 5: Practical Use-Case Examples (3)
| Ch | Test Project | Key Test Content |
|---|---|---|
| 1 | CommandUsecaseExample.Tests.Unit |
Command Use Case full Pipeline |
| 2 | QueryUsecaseExample.Tests.Unit |
Query Use Case full Pipeline |
| 3 | FullPipelineIntegration.Tests.Unit |
Full 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:
- Separating Commands and Queries with the CQRS Pattern: From CQRS pattern fundamentals to use-case integration. Learn the CQRS architecture where this tutorial's Pipeline constraints are applied.
This tutorial was written based on the IFinResponse interface hierarchy design experience in the Functorium project.