Result Pattern
1. Requirements Document
1.1 Overview
KResult<T> is a type-safe, sealed result abstraction for Dart/Flutter applications that standardizes success and failure handling without throwing exceptions across layers (data, domain, UI).
The system introduces:
- A sealed
KResult<T>to represent operation outcomes. - A unified
KFailuremodel to encapsulate all failure information. - A flexible mechanism for developers to define domain-specific failures.
- Automatic exception-to-user-friendly message mapping.
- Optional UI-aware failure representation without coupling business logic to UI.
The goal is to provide predictable error handling, clear separation of concerns, and strong extensibility without inheritance abuse.
1.2 User Stories
- US-1: As an application developer, I want to return a single result type from functions so I don’t need to mix try/catch, nulls, and custom error objects.
- US-2: As a library user, I want consumers of my API to handle failures explicitly and consistently.
- US-3: As a domain developer, I want to define my own failure types (e.g.,
AuthFailure) without modifying core framework code. - US-4: As a UI developer, I want access to both user-friendly messages and optional UI widgets when displaying errors.
- US-5: As a platform integrator, I want all exceptions to be safely captured and mapped to non-crashing, user-friendly failures.
1.3 Functional Requirements
- Result Abstraction:
KResult<T>must be sealed with exactly two states:SuccessandFailure. - Success Handling: Success must contain a non-null value
Tand an optional message. - Failure Handling: Failure must contain a
KFailureinstance. No exception should escape the public API surface. - Unified Failure Creation: Failures must be creatable from:
Exception,String,KFailureMixin, or fallback objects. - Exception Mapping: Known exceptions map to friendly messages; unknown map to "Internal error".
- Developer Extensibility: define domain-specific failures via Mixins, not direct inheritance.
- UI Support: Failures may optionally expose a Flutter Widget for UI rendering.
1.4 Non-Functional Requirements
- Type Safety: Enforced at compile time via sealed classes.
- API Clarity: Minimal and self-explanatory public API.
- Performance: Lightweight construction, no reflection.
- Maintainability: Internal failure types must be private/final.
- Testability: Logic testable without Flutter bindings where possible.
2. Technical Document
2.1 Architecture
System Architecture
The architecture enforces a strict boundary between Public API and Internal Implementation. `KResult<T>` serves as the single point of return. `KFailure` abstracts away the messy details of Exceptions versus Strings versus Custom Errors, presenting a unified face to the UI consumers.
2.2 API Design
Result Structure
sealed class KResult<T> {
const KResult();
// Factory Constuctors
factory KResult.value(T value, {String? message}) = KSuccess<T>;
factory KResult.error(Object error) = KError<T>;
}
final class KSuccess<T> extends KResult<T> {
final T value;
final String? message;
const KSuccess(this.value, {this.message});
}
final class KError<T> extends KResult<T> {
final KFailure failure;
// Private constructor to enforce factory usage
const KError._(this.failure);
factory KError(Object error) {
return KError._(KFailure.from(error));
}
}Failure & Mixin
sealed class KFailure {
final String message;
final Widget? messageUI; // Optional UI representation
// Factory automatically resolves input type
factory KFailure.from(Object error);
}
mixin KFailureMixin {
String? get message;
Widget? get messageUI => null;
}2.3 Error Resolution Flow
When KResult.error(obj) is called, the obj is processed in this order:
- KFailureMixin: Uses developer-defined message/UI.
- Exception: Passed to internal
ExceptionMapper. - String: Used directly as the error message.
- Fallback: "Internal error" or similar safe default.
2.4 Extensibility Strategy
Developers should extend behavior via the KFailureMixin, not by subclassing KResult or KFailure directly. This keeps the core invariant (success vs failure) protected while allowing infinite domain specificity.
class AuthFailure with KFailureMixin {
@override
String get message => "Invalid Credentials";
}
// Usage
return KResult.error(AuthFailure());