Skip to content

Usecase CQRS Specification

This specification defines the public API for Functorium’s CQRS request interfaces, FinResponse<A> discriminated union, FinT LINQ extensions, and caching/persistence contracts.

TypeNamespaceDescription
ICommandRequest<TSuccess>Functorium.Applications.UsecasesCommand request marker interface
ICommandUsecase<TCommand, TSuccess>Functorium.Applications.UsecasesCommand Handler interface
IQueryRequest<TSuccess>Functorium.Applications.UsecasesQuery request marker interface
IQueryUsecase<TQuery, TSuccess>Functorium.Applications.UsecasesQuery Handler interface
FinResponse<A>Functorium.Applications.UsecasesSucc/Fail discriminated union (supports Match, Map, Bind, LINQ)
FinResponseFunctorium.Applications.UsecasesStatic factory class (Succ, Fail)
IFinResponseFunctorium.Applications.UsecasesNon-generic base interface (IsSucc/IsFail)
IFinResponse<out A>Functorium.Applications.UsecasesCovariant generic interface
IFinResponseFactory<TSelf>Functorium.Applications.UsecasesCRTP-based Fail creation interface
IFinResponseWithErrorFunctorium.Applications.UsecasesError access interface (for Pipeline)
FinToFinResponseFunctorium.Applications.UsecasesFin<A> to FinResponse<A> conversion extension methods
FinTLinqExtensionsFunctorium.Applications.LinqFinT monad transformer LINQ extension methods
ICacheableFunctorium.Applications.UsecasesCaching contract interface
IUnitOfWorkFunctorium.Applications.PersistencePersistence transaction contract
IUnitOfWorkTransactionFunctorium.Applications.PersistenceExplicit transaction scope
CtxIgnoreAttributeFunctorium.Applications.UsecasesCtxEnricher auto-generation exclusion attribute
  • FinResponse<A> uses IsSucc/IsFail (not IsSuccess)
  • Success values can only be accessed via ThrowIfFail() or Match()
  • Static factories are FinResponse.Succ(value) and FinResponse.Fail<T>(error) (not FinResponse<T>.Succ())
  • Both Command and Query use FinResponse<TSuccess> as the return type

Having reviewed the key types and core rules in the summary, the following sections detail each type’s API.


Functorium’s CQRS interfaces are based on Mediator the Mediator library’s ICommand<T>/IQuery<T>, fixing the return type to FinResponse<TSuccess> to enforce the Result pattern.

// Request interface — implemented as record
public interface ICommandRequest<TSuccess> : ICommand<FinResponse<TSuccess>>
{
}
// Handler interface — implemented as Usecase class
public interface ICommandUsecase<in TCommand, TSuccess>
: ICommandHandler<TCommand, FinResponse<TSuccess>>
where TCommand : ICommandRequest<TSuccess>
{
}
Type ParameterConstraintDescription
TSuccessNoneData type to return on success
TCommandICommandRequest<TSuccess>Command request type (contravariant)
// Request interface — implemented as record
public interface IQueryRequest<TSuccess> : IQuery<FinResponse<TSuccess>>
{
}
// Handler interface — implemented as Usecase class
public interface IQueryUsecase<in TQuery, TSuccess>
: IQueryHandler<TQuery, FinResponse<TSuccess>>
where TQuery : IQueryRequest<TSuccess>
{
}
Type ParameterConstraintDescription
TSuccessNoneData type to return on success
TQueryIQueryRequest<TSuccess>Query request type (contravariant)
Mediator.ICommand<FinResponse<TSuccess>>
└─ ICommandRequest<TSuccess>
Mediator.ICommandHandler<TCommand, FinResponse<TSuccess>>
└─ ICommandUsecase<TCommand, TSuccess>
Mediator.IQuery<FinResponse<TSuccess>>
└─ IQueryRequest<TSuccess>
Mediator.IQueryHandler<TQuery, FinResponse<TSuccess>>
└─ IQueryUsecase<TQuery, TSuccess>

FinResponse<A> is a discriminated union representing success (Succ) and failure (Fail). It supports Match, Map, Bind, and LINQ query expressions.

public abstract record FinResponse<A> : IFinResponse<A>, IFinResponseFactory<FinResponse<A>>
{
public sealed record Succ(A Value) : FinResponse<A>;
public sealed record Fail(Error Error) : FinResponse<A>, IFinResponseWithError;
}
TypePropertiesIsSuccIsFailDescription
FinResponse<A>.SuccA ValuetruefalseSuccess case
FinResponse<A>.FailError ErrorfalsetrueFailure case
PropertyTypeDescription
IsSuccboolWhether in success state
IsFailboolWhether in failure state
MethodSignatureDescription
Match<B>B Match<B>(Func<A, B> Succ, Func<Error, B> Fail)Calls function based on state (returns value)
Matchvoid Match(Action<A> Succ, Action<Error> Fail)Calls action based on state
Map<B>FinResponse<B> Map<B>(Func<A, B> f)Transforms success value
MapFailFinResponse<A> MapFail(Func<Error, Error> f)Transforms failure value
BiMap<B>FinResponse<B> BiMap<B>(Func<A, B> Succ, Func<Error, Error> Fail)Transforms both success/failure simultaneously
Bind<B>FinResponse<B> Bind<B>(Func<A, FinResponse<B>> f)Monadic bind
BiBind<B>FinResponse<B> BiBind<B>(Func<A, FinResponse<B>> Succ, Func<Error, FinResponse<B>> Fail)Binds both success/failure simultaneously
BindFailFinResponse<A> BindFail(Func<Error, FinResponse<A>> Fail)Binds failure state
IfFailA IfFail(Func<Error, A> Fail)Alternative value function on failure
IfFailA IfFail(A alternative)Alternative value on failure
IfFailvoid IfFail(Action<Error> Fail)Executes action on failure
IfSuccvoid IfSucc(Action<A> Succ)Executes action on success
ThrowIfFailA ThrowIfFail()Throws on failure, returns value on success
Select<B>FinResponse<B> Select<B>(Func<A, B> f)LINQ select support (same as Map)
SelectMany<B,C>FinResponse<C> SelectMany<B, C>(Func<A, FinResponse<B>> bind, Func<A, B, C> project)LINQ from ... from ... select support

Static Factory Methods (FinResponse class)

Section titled “Static Factory Methods (FinResponse class)”
public static class FinResponse
{
public static FinResponse<A> Succ<A>(A value);
public static FinResponse<A> Succ<A>() where A : new();
public static FinResponse<A> Fail<A>(Error error);
}
MethodDescription
Succ<A>(A value)Creates FinResponse from success value
Succ<A>()Creates default success FinResponse via new A() (A : new() constraint)
Fail<A>(Error error)Creates failure FinResponse
// Static factory implemented on FinResponse<A> (used internally by Pipeline)
public static FinResponse<A> CreateFail(Error error);

Used when Pipeline calls TResponse.CreateFail(error). Since it is a static abstract method, it can only be called on concrete types.

OperatorDescription
implicit operator FinResponse<A>(A value)Value to Succ auto-conversion
implicit operator FinResponse<A>(Error error)Error to Fail auto-conversion
operator truetrue when IsSucc
operator falsetrue when IsFail
operator |Choice operator: returns left if Succ, otherwise returns right

is an interface hierarchy for handling FinResponse<A> in a type-safe manner in Pipeline and Observability.

IFinResponse Non-generic (IsSucc/IsFail access)
└─ IFinResponse<out A> Covariant generic
IFinResponseFactory<TSelf> CRTP-based Fail creation (for Pipeline)
IFinResponseWithError Error access (for Logger/Trace Pipeline)
FinResponse<A>
├─ implements IFinResponse<A>
├─ implements IFinResponseFactory<FinResponse<A>>
└─ Fail : implements IFinResponseWithError
public interface IFinResponse
{
bool IsSucc { get; }
bool IsFail { get; }
}

is a non-generic interface for accessing IsSucc/IsFail properties without generic types in Pipeline.

public interface IFinResponse<out A> : IFinResponse
{
}

Supports covariance (out) for read-only use in pipelines.

public interface IFinResponseFactory<TSelf>
where TSelf : IFinResponseFactory<TSelf>
{
static abstract TSelf CreateFail(Error error);
}

Uses CRTP (Curiously Recurring Template Pattern) to support type-safe Fail creation. Called as TResponse.CreateFail(error) in Pipeline’s UsecaseValidationPipeline and UsecaseExceptionPipeline.

public interface IFinResponseWithError
{
Error Error { get; }
}

is an interface for accessing Error information on failure. Used in Logger Pipeline and Trace Pipeline. Only FinResponse<A>.Fail implements this interface.


The FinToFinResponse extension method class is used for cross-layer conversion from Repository (Fin<A>) to Usecase (FinResponse<A>).

MethodSignatureDescription
ToFinResponse<A>Fin<A> → FinResponse<A>Same type conversion
ToFinResponse<A,B> (mapper)Fin<A> → Func<A, B> → FinResponse<B>Success value mapping conversion
ToFinResponse<A,B> (factory)Fin<A> → Func<B> → FinResponse<B>Calls factory on success (ignores original value)
ToFinResponse<A,B> (onSucc/onFail)Fin<A> → Func<A, FinResponse<B>> → Func<Error, FinResponse<B>> → FinResponse<B>Custom handling for both success/failure
// Basic conversion
FinResponse<Product> response = fin.ToFinResponse();
// Mapping conversion
FinResponse<ProductDto> response = fin.ToFinResponse(product => new ProductDto(product));
// Factory conversion (when original value is not needed, e.g., Delete)
Fin<Unit> result = await repository.DeleteAsync(id);
return result.ToFinResponse(() => new DeleteResponse(id));

FinTLinqExtensions is a partial class that unifies Fin<A>, IO<A>, and Validation<Error, A> types into the FinT<M, A> monad transformer, enabling LINQ query expression support.

FileSource TypeSelector Return TypeResult TypeDescription
.Fin.csFin<A>FinT<M, B>FinT<M, C>Lifts Fin to FinT then chains
.Fin.csFinT<M, A>Fin<B>FinT<M, C>Uses Fin in the middle of FinT chain
.IO.csIO<A>B (Map)FinT<IO, B>Simple IO to FinT conversion
.IO.csIO<A>FinT<IO, B>FinT<IO, C>Lifts IO to FinT then chains
.IO.csFinT<IO, A>IO<B>FinT<IO, C>Uses IO in the middle of FinT chain
.Validation.csValidation<Error, A>FinT<M, B>FinT<M, C>Validation to FinT (generic)
.Validation.csValidation<Error, A>B (Map)FinT<M, B>Simple Validation to FinT conversion
.Validation.csValidation<Error, A>FinT<IO, B>FinT<IO, C>Validation to FinT (IO specialized)
.Validation.csFinT<M, A>Validation<Error, B>FinT<M, C>Uses Validation in the middle of FinT chain
.Validation.csFinT<IO, A>Validation<Error, B>FinT<IO, C>Uses Validation in FinT chain (IO)
FileTarget TypeReturn TypeDescription
.Fin.csFin<A>Fin<A>Returns Fail when condition is not satisfied
.FinT.csFinT<M, A>FinT<M, A>Returns Fail when condition is not satisfied
// Fin Filter
Fin<int> result = FinTest(25).Filter(x => x > 20);
// FinT Filter
FinT<IO, int> result = FinT<IO, int>.Succ(42).Filter(x => x > 20);
public static FinT<M, Seq<B>> TraverseSerial<M, A, B>(
this Seq<A> seq,
Func<A, FinT<M, B>> f)
where M : Monad<M>;

Sequentially traverses Seq<A>, transforming each element into FinT<M, B>. Uses fold to ensure each operation completes fully before the next one starts.

ParameterTypeDescription
seqSeq<A>Sequence to process
fFunc<A, FinT<M, B>>Function that transforms each element into FinT

Use case: Suitable when you need to safely use resources that do not support concurrency, such as DbContext, in sequential order. If each item is independent and there is no resource sharing, use Traverse instead.

// Usage in LINQ query expressions
FinT<IO, Response> response =
from infos in GetFtpInfos()
from results in infos.TraverseSerial(info => Process(info))
select new Response(results);

Difference in IO and Validation Method Counts

Section titled “Difference in IO and Validation Method Counts”
DirectionIO.csValidation.cs
Source → FinT (Map)IO onlyGeneric M
Source → FinT<M, B>IO onlyGeneric M + IO
FinT<M, A> → SourceIO onlyGeneric M + IO

IO is itself a specific monad, so a generic M version is unnecessary. Validation is a data type rather than a monad, so it can be combined with any monad M, providing both a generic M version and an IO-specialized version.

FileContent
FinTLinqExtensions.csClass definition and documentation
FinTLinqExtensions.Fin.csFin<A> extensions (SelectMany, Filter)
FinTLinqExtensions.IO.csIO<A> extensions (SelectMany)
FinTLinqExtensions.Validation.csValidation<Error, A> extensions (SelectMany)
FinTLinqExtensions.FinT.csFinT<M, A> extensions (Filter, TraverseSerial)

Interface for applying caching to Query requests. When a record implementing IQueryRequest<TSuccess> also implements ICacheable, the Pipeline automatically handles caching.

public interface ICacheable
{
string CacheKey { get; }
TimeSpan? Duration { get; }
}
PropertyTypeDescription
CacheKeystringUnique key for the cache entry
DurationTimeSpan?Cache validity period (null applies default policy)
// Usage example: Applying caching to a Query request
public sealed record GetProductByIdQuery(ProductId Id)
: IQueryRequest<ProductDto>, ICacheable
{
public string CacheKey => $"product:{Id}";
public TimeSpan? Duration => TimeSpan.FromMinutes(5);
}

Contract for persisting changes in Command Usecases. The UsecaseTransactionPipeline automatically calls SaveChanges after Handler execution.

public interface IUnitOfWork : IObservablePort
{
FinT<IO, Unit> SaveChanges(CancellationToken cancellationToken = default);
Task<IUnitOfWorkTransaction> BeginTransactionAsync(CancellationToken cancellationToken = default);
}
MethodReturn TypeDescription
SaveChangesFinT<IO, Unit>Persists changes
BeginTransactionAsyncTask<IUnitOfWorkTransaction>Starts an explicit transaction

IUnitOfWork inherits from IObservablePort. IObservablePort defines a string RequestCategory { get; } property, enabling the observability Pipeline to automatically identify the layer and category.

public interface IUnitOfWorkTransaction : IAsyncDisposable
{
Task CommitAsync(CancellationToken cancellationToken = default);
}
MethodReturn TypeDescription
CommitAsyncTaskCommits the transaction
DisposeAsyncValueTaskUncommitted transactions are automatically rolled back (IAsyncDisposable)

Explicit transactions are used when you need to wrap immediately-executed SQL like ExecuteDeleteAsync/ExecuteUpdateAsync and SaveChanges in the same transaction.

// Explicit transaction usage example
await using var tx = await unitOfWork.BeginTransactionAsync(ct);
// ... ExecuteDeleteAsync, SaveChanges, etc.
await tx.CommitAsync(ct);
// Auto-rollback on Dispose if not committed

[AttributeUsage(
AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Parameter,
AllowMultiple = false,
Inherited = false)]
public sealed class CtxIgnoreAttribute : Attribute;

Request records, properties, or record constructor parameters with this attribute are excluded from CtxEnricher source generator auto-generation targets.

TargetEffect
ClassExcludes the entire record from CtxEnricher generation
PropertyExcludes only this property from CtxEnricher
ParameterExcludes only this record constructor parameter from CtxEnricher

DocumentDescription
Use Case and CQRS GuideCQRS pattern design intent and implementation guide
Validation System SpecificationTypedValidation, FluentValidation integration
Error System SpecificationDomainErrorType, ApplicationErrorType, etc.
Port and Adapter SpecificationIRepository, IQueryPort, etc.
Pipeline SpecificationPipeline behavior, UsecaseTransactionPipeline, etc.
Observability SpecificationField/Tag specification, Meter definitions