Structure of a Modddel
A Modddel typically looks like this :
1. Imports and Part statements
For the Modddels generator to work, you need to import modddels_annotation_fpdart
or modddels_annotation_dartz
(depending on which package you installed), and add the part statement filename.modddel.dart
, where filename
is the name of the file.
If you're using freezed for making the failures sealed classes, also import it and add the part statement filename.freezed.dart
, where filename
is the name of the file.
2. The @Modddel
Annotation
@Modddel
AnnotationThe @Modddel
annotation is where you can define the validationSteps of your modddel.
Example :
As you can see, inside the validationSteps
parameter, you provide a list of ValidationStep
s in the order that you want them to be processed.
For each ValidationStep
, you provide its name
, as well as the list of validations
it contains (the order doesn't matter here). The name
is mainly used to make the name of the invalid-step union-case class name (Ex : 'Form' → 'InvalidUsernameForm'). It must begin with an uppercase letter, and it must be made of valid dart identifier characters (alphanumeric, underscore and dollar sign).
The name
parameter of the ValidationStep is optional. (See this section about its default values for ValueObjects, and this section for Entities).
For each Validation
, you provide its name
, as well as the failureType
. The name
is used to make the name of the validation method (Ex : 'size' → 'validateSize'), as well as the name of the failure (Ex : 'size' → 'sizeFailure'). It must begin with a lowercase letter, and it must be made of valid dart identifier characters. The failureType
is the type of the failure of the validation. You can either provide it as a typeArg : FailureType<UsernameSizeFailure>()
, or you can provide it as a string : FailureType('UsernameSizeFailure')
(the latter takes precedence).
It is recommended to provide the Failure class as a typeArg, so that you avoid typos. However, if the Failure class is not available during the code generation, you'll have to provide it as a string. This situation can occur when your failure class is generated by a builder (other than freezed) which runs after the modddels generator.
3. The class declaration
When declaring your modddel class, you should extend the appropriate modddel kind, such as SingleValueObject
, MultiValueObject
, SimpleEntity
... , and provide two type arguments. The first type argument should be 'Invalid' followed by the modddel name, and the second type argument should be 'Valid' followed by the modddel name.
Your modddel class should also mix in a mixin with the name of your modddel prefixed by _$
(Like freezed).
4. The private empty constructor
The modddel should only contain one generative constructor, which should be private and empty. Example : Age._()
. You should never use this constructor to create an instance of the Modddel.
5. The factory constructor
The parameters of the factory constructor will be the list of properties the modddel contains. Parameters can be positional or named, optional or required, and can have default values.
The factory constructor should always return the result of calling the mixin's _create
static method. This method is generated by the modddels generator, and you should pass to it all the parameters of the factory.
Example :
Your factory constructor should be unnamed unless you want to create a union of modddels.
Decorators and comments
You can document/decorate a field by documenting/decorating its parameter inside the factory constructor.
Example : Documenting firstName
and annotating lastName
with @Deprecated
:
Asserts & Sanitization
The factory constructor allows you to do all sorts of data manipulation or sanitization before effectively creating the modddel.
Example :
6. The validate methods
After running the generator, your IDE will prompt you to override the "validate" method(s) :
This is the place where you can implement your validation logic. For example :
The age
parameter contains all the fields of the Modddel. You can omit its type for a cleaner look :
You should NEVER access an instance member (property, method, getter...) from within the "validate" methods. Doing so will throw a runtime error.
7. The Failure(s)
In the example, the Failure is created using freezed.
Note that the failure should always extend ValueFailure
if the modddel is a ValueObject, and it should extend EntityFailure
if the modddel is an Entity.
The name of the ValueFailure sealed class can be anything, but as a good practice it's better to name it '{ModddelName}{ValidationName}Failure' (Ex: NameLengthFailure
).
Last updated