Target is a library for Functional Domain Modeling in Dart, the Dart implementation of target-kt, inspired by arrow-kt.
Target aims to provide a set of tools for Dart to empower users to quickly write pure, functionally
validated domain models. For this, it includes a set of atomic components: ValueFailure, ValueObject,
and ValueValidator. These components can be used on their own, or in conjunction with the
included build_runner annotation processor.
A ValueFailure is an interface representing a failure during value validation.
abstract interface class ValueFailure<T> {
abstract final T failedValue;
}A ValueObject is an interface representing a validated value. By convention, value object implementations have a
private primary constructor, so that they are not instantiated outside a ValueValidator. A value object implementation
must declare a static implementation of a value validator, of, when used in conjunction with the annotation
processor package.
abstract interface class ValueObject<T> {
abstract final T value;
}A ValueValidator is an interface defining value validation functions. The primary validation function, of, takes an
input and returns either a ValueFailure or a ValueObject. The value validator is also callable, delegating to of.
By convention, the value object's private constructor is often passed to its primary constructor as a reference.
abstract class ValueValidator<I extends Object, F extends ValueFailure<I>, T extends ValueObject<I>> {
const ValueValidator();
Either<F, T> of(I input);
// ...
}The included StringInRegexValidator class is an example of a ValueValidator implementation.
class StringRegexValidator<T extends ValueObject<String>>
extends ValueValidator<String, GenericValueFailure<String>, T> {
final T Function(String input) _ctor;
final RegExp regExp;
const StringRegexValidator(this._ctor, {required this.regExp});
@override
Either<GenericValueFailure<String>, T> of(String input) {
if (regExp.hasMatch(input)) {
return Right(_ctor(input));
} else {
return Left(GenericValueFailure(input));
}
}
}The included EmailAddress class is an example of an annotation processor compatible ValueObject implementation.
/// A W3C HTML5 email address.
final class EmailAddress extends GenericValueObject<String> {
static const of = EmailAddressValidator(EmailAddress._);
const EmailAddress._(super.value);
}This value object can then be used to validate an email address like so:
Future<Either<AccountCreateFailure, Account>> createAccount(AccountParamsDto params) {
return eitherAsync((r) async {
final emailAddress = EmailAddress.of(params.emailAddress)
.getOrElse((it) => r.raise(AccountCreateFailure.invalidEmailAddress(it)));
// ... validating other params ...
return await repositoryCreate(
AccountParams(
emailAddress: emailAddress,
// ... passing other validated params ...
),
).then(r.bind);
});
}