Models that are always valid / invalid

Models that are always valid

Sometimes, you may want to create a model that is always valid. Such model is not a modddel, because the latter by definition should have both a valid and an invalid state.

As an alternative, you can create a dataclass that implements ValidValueObject or ValidEntity.

Example 1 : Creating a ValidValueObject using Freezed

@freezed
class ValidId with _$ValidId implements ValidValueObject {
  const factory ValidId(String id) = _ValidId;
}

Example 2 : Creating a ValidEntity using Freezed

@freezed
class ValidPerson with _$ValidPerson implements ValidEntity {
  const factory ValidPerson({
    required ValidFullName validFullName,
    required bool isAdult,
  }) = _ValidPerson;
}

While implementing ValidEntity or ValidValueObject doesn't offer any extra functionality to these dataclasses, it is a good practice to do so because it makes your code and intentions clear. It is also a good practice to start the names of these dataclasses with "Valid".

NB 1 : You can use whatever approach you want to make these dataclasses (Freezed, Equatable...).

NB 2 : When using the dataclass as a parameter inside an Entity, don't forget to annotate the parameter with @validParam.

Models that are always invalid

Sometimes, you may want to create a model that is always invalid. Such model is not a modddel, because the latter by definition should have both a valid and an invalid state.

As an alternative, you can create a dataclass that implements InvalidValueObject or InvalidEntity, and then override the failures getter.

Example 1 : Creating an InvalidValueObject using Freezed.

@freezed
class InvalidId with _$InvalidId implements InvalidValueObject {
  const factory InvalidId(String id) = _InvalidId;

  const InvalidId._();

  /// These are the failures that [InvalidId] holds
  @override
  List<ValueFailure> get failures => [const IdFailure.invalid()];
}

@freezed
class IdFailure extends ValueFailure with _$IdFailure {
  const factory IdFailure.invalid() = _Invalid;
}

Example 2 : Creating an InvalidEntity using Freezed.

In this example, we would like to have an Entity that would always be invalid, because it holds a non-nullable invalid parameter invalidId.

@freezed
class InvalidPerson with _$InvalidPerson implements InvalidEntity {
  const factory InvalidPerson({
    required InvalidId invalidId,
    required Username username,
  }) = _InvalidPerson;

  const InvalidPerson._();

  /// [InvalidPerson] always holds a ContentFailure. The invalidMembers are :
  ///
  /// - [invalidId] because it's always invalid
  /// - [username] only when it's invalid
  @override
  List<EntityFailure> get failures => [
        ContentFailure([
          ModddelInvalidMember(member: invalidId, description: 'invalidId'),
          if (!username.isValid)
            ModddelInvalidMember(
                member: username as InvalidUsername, description: 'username'),
        ])
      ];
}

As you can see, you have full control over what failures the invalid model holds. Just make sure the failures getter never returns an empty list.

NB 1 : You can use whatever approach you want to make these dataclasses (Freezed, Equatable...).

NB 2 : When using the dataclass as a parameter inside an Entity, don't forget to annotate the parameter with @invalidParam.