Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for async validations #16

Open
CodingSoot opened this issue Jun 18, 2023 · 1 comment
Open

Add support for async validations #16

CodingSoot opened this issue Jun 18, 2023 · 1 comment
Labels
enhancement New feature or request feedback wanted Suggestions and opinions are needed

Comments

@CodingSoot
Copy link
Owner

CodingSoot commented Jun 18, 2023

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 call init or 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 :
  • Disadvantages :
    • BREAKING CHANGE
    • Static methods can't be unnamed like factory constructors (Age.create(20) vs Age(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
@CodingSoot CodingSoot added enhancement New feature or request feedback wanted Suggestions and opinions are needed labels Jun 18, 2023
@CodingSoot
Copy link
Owner Author

CodingSoot commented Jun 27, 2023

Right now I really don't know which way we should go.

There will be other cleaner solutions once augmentation libraries are released, but that will take a while.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request feedback wanted Suggestions and opinions are needed
Projects
None yet
Development

No branches or pull requests

1 participant