I have a problem with Either errors types incompatibility.
A method to create Account entity:
static Either<AccountError,Account> create(
String userNameCandidate,
UserNameUniquenessValidator userNameUniquenessValidator,
String passwordCandidate,
PasswordEncoder passwordEncoder) {
return UserName
.create(userNameCandidate,userNameUniquenessValidator)
.flatMap(correctUserName -> Password
.create(passwordCandidate,passwordEncoder)
.map(correctPassword -> new Account(correctUserName,correctPassword)));
}
UserName and Password value objects:
@Embeddable
final class UserName implements Serializable {
public static final Integer MIN_USER_NAME_LENGTH = 3;
public static final Integer MAX_USER_NAME_LENGTH = 15;
@Column
private final String userName;
UserName(String userNam) {
this.userName = userNam;
}
//For JPA only.Don't use!
public UserName() {
this.userName = "default";
}
static Either<WrongUserNameFormatError,UserName> create(
String userNameCandidate,
UserNameUniquenessValidator userNameUniquenessValidator) {
if (userNameCandidate.isEmpty()) {
return Either.left(new WrongUserNameFormatError("Empty user name."));
}
var isUserNameCandidateLengthWrong =
userNameCandidate.length() < MIN_USER_NAME_LENGTH &&
userNameCandidate.length() > MAX_USER_NAME_LENGTH;
if (isUserNameCandidateLengthWrong) {
return Either.left(new WrongUserNameFormatError(
"Wrong user name length: " + userNameCandidate.length() +
".Min: " + MIN_USER_NAME_LENGTH +
".Max: " + MAX_USER_NAME_LENGTH));
}
if (!userNameUniquenessValidator.isUnique(userNameCandidate)) {
return Either.left(new WrongUserNameFormatError("Not unique user name: " +
userNameCandidate));
}
return Either.right(new UserName(userNameCandidate));
}
public String getUserName() {
return userName;
}
}
@Embeddable
final class Password implements Serializable {
public static final Integer MIN_PASSWORD_LENGTH = 5;
@Column
private final String password;
private Password(String password) {
this.password = password;
}
//For JPA only.Don't use!
public Password() {
this.password = "default";
}
static Either<WrongPasswordFormatError,Password> create(String passwordCandidate, PasswordEncoder passwordEncoder) {
if (passwordCandidate.length() >= MIN_PASSWORD_LENGTH) {
var encodedPassword= passwordEncoder.encode(passwordCandidate);
return Either.right(new Password(encodedPassword));
}
else {
return Either.left(new WrongPasswordFormatError(
"Wrong password length: " + passwordCandidate.length() +
". Min: " + MIN_PASSWORD_LENGTH));
}
}
public String getPassword() {
return password;
}
}
I get error: Required Either<AccountError,Account>, provided Either<WrongUserNameFormat, Object>, incompatible constraint: AccountError and WrongUserNameFormatError even though all errors extends AccountError.
I'd use
Validationhere since there you can collect (validation) errors. :Since I don't have the object types of your example I came up with this:
Validation is basically a special case of Either (there is also a
.toEitheron theValidationobject).cis the result ofUserName.createanddPassword.createThe 'trick' is to use a
Seqhere instead of the Object type directly. I can't explain why (so I realize this might sound like bad advice).In our project we use this construction extensively since we often can have multiple validation errors in our flows and then the left side is always the same and easy to carry over to other flows.