-
Notifications
You must be signed in to change notification settings - Fork 2
Open
Labels
enhancementNew feature or requestNew feature or requestfeedback wantedSuggestions and opinions are neededSuggestions and opinions are needed
Description
Right now, all validation methods are synchronous, and there is no way to make them async.
@Modddel(
// ...
)
class Age extends SingleValueObject<InvalidAge, ValidAge> with _$Age {
Age._();
factory Age(int value) {
return _$Age._create(
value: value,
);
}
// ⚠️ This can't be async
@override
Option<AgeLegalFailure> validateLegal(age) {
if (age.value < 18) {
return some(const AgeLegalFailure.minor());
}
return none();
}
}Given that factory constructors can't be async, I only see three options :
Option 1 : Use the builder pattern
We generate a "Builder" that you should instantiate and then call an async method (for example runValidations) to get your modddel instance.
final builder = AgeBuilder(20);
final age = await builder.runValidations();- Advantages :
- Ability to lazily validate a modddel (Although this can be easily accomplished by the user using a package like lazy_evaluation or any other way).
- Disadvantages :
- Boilerplate code +++
- Different syntax for instantiating a modddel with sync validations versus async validations
Option 2 : Initialize the modddel manually
We add an init method to the modddel that the user should call, and its returned value is the initialized modddel.
final age = await Age(20).init();- Advantages :
- Less boilerplate code
- Disadvantages :
⚠️ RUNTIME ERRORS : If the user forgets to callinitor accidentally uses the instance created with the factory constructor (Age(20)) , there will be runtime errors that may be hard to debug- Again : different syntax for instantiating a sync modddel versus async
Option 3 : Ditch the factory constructors for async methods
We replace the factory constructors with static methods :
// Instead of a factory constructor :
factory Age(int value) {
return _$Age._create(
value: value,
);
}
// We use a static method
static Age create(int value) {
// To accompany this change, this static method is no longer private
// (it never needed to be private since the mixin `_$Age` is private)
return _$Age.create(
value: value,
);
}The name of the method should be create for solo modddels, and create{UnionCaseName} for unions (ex for a Weather union : createSunny, createRainy...).
Then, these static methods can easily be made async :
// Change the return type to a future, and optionally add the async keyword if needed
static Future<Age> create(int value) {
return _$Age.create(
value: value,
);
}And then for making an instance of the modddel :
// If sync :
final age = Age.create(20);
// If async :
final age = await Age.create(20);- Advantages :
- Same syntax for instantiating sync and async modddels (you only add await)
- Solves "variable set but not used" error during build of VM dart-lang/sdk#9 , because static methods have a return type
- Devs can freely use factory constructors for other purposes
- The forwarding method (
_$Age.create) has the same name as the static method
- Disadvantages :
- BREAKING CHANGE
- Static methods can't be unnamed like factory constructors (
Age.create(20)vsAge(20)). - Static methods don't preserve generic type information (so if you have generics, you need to forward them in a verbose way)
- Usually, you create an instance with a constructor, and not a static method, so this might be a little less elegant
nosadaniel
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or requestfeedback wantedSuggestions and opinions are neededSuggestions and opinions are needed