Skip to content

FinT/FinResponse Reference

This is a reference document for the functional types used in Functorium CQRS. It covers the Repository layer’s FinT<IO, T>, the Usecase layer’s FinResponse<T>, and the ToFinResponse() conversion that connects them.


Functorium’s functional types are built on top of LanguageExt. Check each type’s affiliation in the hierarchy below.

LanguageExt (Library)
├── Fin<T> Represents success(T) or failure(Error)
├── FinT<M, T> Monad transformer: wraps M<Fin<T>>
└── IO Pure functional IO effect
Functorium (Framework)
├── FinResponse<T> Usecase return type: Fin<T> + IFinResponseFactory
└── ToFinResponse() Fin<T> -> FinResponse<T> conversion extension method

LanguageExt library’s Result type. Represents success or failure.

// Creation
Fin<int> success = Fin.Succ(42);
Fin<int> failure = Fin.Fail<int>(Error.New("An error occurred"));
// Pattern matching
var result = fin.Match(
Succ: value => $"Success: {value}",
Fail: error => $"Failure: {error.Message}");
// State checking
if (fin.IsSucc) { /* success */ }
if (fin.IsFail) { /* failure */ }
// Value access (throws if not success)
var value = fin.ThrowIfFail();

FinT is a monad transformer that wraps IO<Fin<T>>. It is the return type of Repository methods.

// Repository methods return FinT<IO, T>
FinT<IO, Order> result = repository.GetById(orderId);
// Execution (runs the IO effect to obtain Fin<T>)
Fin<Order> fin = await result.RunAsync();

FinT can be composed using LINQ’s from...select syntax.

// Sequentially compose multiple Repository operations
var pipeline =
from order in repository.GetById(orderId)
from _ in guard(order.CanCancel(), Error.New("Cannot cancel"))
from updated in repository.Update(order.Cancel())
select updated.Id;
// If any step fails, the entire pipeline fails
Fin<OrderId> fin = await pipeline.RunAsync();

Fails the pipeline if the condition is not met.

// guard(condition, error on failure)
from _ in guard(order.CanCancel(), Error.New("Cannot cancel status"))
// map: transform the success value
FinT<IO, OrderId> orderId = repository.Create(order).Map(o => o.Id);
// bind (SelectMany): chain to another FinT
FinT<IO, Order> result = repository.GetById(orderId)
.Bind(order => repository.Update(order.Cancel()));

The return type of the Usecase layer. Compatible with the Mediator pipeline (validation, transaction, logging, etc.).

// Creation
FinResponse<OrderId> success = FinResponse.Succ(orderId);
FinResponse<OrderId> failure = FinResponse.Fail<OrderId>(Error.New("Failed"));
// State checking
if (response.IsSucc) { /* success */ }
if (response.IsFail) { /* failure */ }

The two types are used in different layers and for different purposes.

CharacteristicFin<T>FinResponse<T>
LayerRepository/DomainUsecase/Application
PurposeFunctional composition (FinT)Mediator response
FactoryFin.Succ / Fin.FailFinResponse.Succ / FinResponse.Fail
PipelineLINQ from…selectMediator Pipeline

An extension method for converting from the Repository layer (Fin) to the Usecase layer (FinResponse). It provides four overloads for different use cases.

Passes the success value as-is.

// Fin<A> -> FinResponse<A>
Fin<Order> fin = await repository.Create(order).RunAsync();
FinResponse<Order> response = fin.ToFinResponse();

Transforms the success value to a different type.

// Fin<A> -> FinResponse<B> (success value transformation)
Fin<Order> fin = await repository.Create(order).RunAsync();
FinResponse<OrderId> response = fin.ToFinResponse(order => order.Id);

Ignores the success value and creates a new instance.

// Fin<A> -> FinResponse<B> (ignore success value, create new instance)
Fin<int> fin = await repository.Delete(orderId).RunAsync();
FinResponse<DeleteResult> response = fin.ToFinResponse(() => new DeleteResult(orderId));

Handles both success and failure with custom logic.

// Fin<A> -> FinResponse<B> (custom handling for both success/failure)
Fin<Order> fin = await repository.GetById(orderId).RunAsync();
FinResponse<OrderDto> response = fin.ToFinResponse(
onSucc: order => FinResponse.Succ(order.ToDto()),
onFail: error => FinResponse.Fail<OrderDto>(error));

public async ValueTask<FinResponse<OrderId>> Handle(
CreateOrderCommand command, CancellationToken ct)
{
var order = Order.Create(OrderId.New(), command.CustomerId);
var fin = await repository.Create(order).RunAsync();
return fin.ToFinResponse(o => o.Id);
}

Pattern 2: Monadic Composition Then Conversion

Section titled “Pattern 2: Monadic Composition Then Conversion”
public async ValueTask<FinResponse<OrderId>> Handle(
CancelOrderCommand command, CancellationToken ct)
{
var pipeline =
from order in repository.GetById(command.OrderId)
from _ in guard(order.CanCancel(), Error.New("Cannot cancel"))
from __ in repository.Update(order.Cancel())
select order.Id;
var fin = await pipeline.RunAsync();
return fin.ToFinResponse();
}
public async ValueTask<FinResponse<PagedResult<OrderDto>>> Handle(
SearchOrdersQuery request, CancellationToken ct)
{
var fin = await query.Search(spec, request.Page, request.Sort).RunAsync();
return fin.ToFinResponse();
}

// Simple error
Error.New("Order not found")
// With code
Error.New(404, "Order not found")
// Exception wrapping
Error.New(exception)

In a FinT pipeline, if any step fails, subsequent steps are skipped and the error propagates.

var pipeline =
from order in repository.GetById(orderId) // If fails, skips steps below
from _ in guard(order.CanCancel(), ...) // If fails, skips steps below
from __ in repository.Update(order.Cancel())
select order.Id;

Functorium provides structured error types beyond Error.New("message"). The Pipeline layer automatically maps HTTP status codes based on error type.

Error TypePurposeHTTP Mapping
DomainErrorDomain rule violation422 Unprocessable Entity
ApplicationErrorApplication-level error400 Bad Request
NotFoundErrorResource not found404 Not Found
// DomainError creation example
DomainError.ForContext<Order>("Order status is not cancellable")
// Combined with Guard
from _ in guard(order.CanCancel(),
DomainError.ForContext<Order>("Cannot cancel in current status"))

Let’s review common design mistakes and their correct alternatives when applying CQRS.

-> Appendix D: CQRS Anti-Patterns