0xf1f1
02/17/2024, 8:27 PMRiccardo Cardin
02/18/2024, 8:28 AMEither
type on your public interface. I mean, will you ever build an instance of UserType
alone? If yes, then the factory of the type must return an Either
. If the public interface is the User
type, then the Either
must go only of the UserFactory
. The DDD purist would say you need Either
only on your aggregate.
Btw, use EitherNel
instead of Either
with a list.
Try to have a look also at the smart constructor pattern.0xf1f1
02/18/2024, 6:44 PMRiccardo Cardin
02/18/2024, 6:46 PMbind
method of arrow. Please, check this article for further details: https://blog.rockthejvm.com/functional-error-handling-in-kotlin-part-2/Riccardo Cardin
02/18/2024, 6:47 PMRiccardo Cardin
02/18/2024, 6:48 PMRiccardo Cardin
02/18/2024, 6:48 PMUlrich Schuster
02/19/2024, 4:48 PM0xf1f1
02/19/2024, 7:43 PM0xf1f1
02/19/2024, 7:46 PMRiccardo Cardin
02/19/2024, 7:47 PM0xf1f1
02/19/2024, 8:11 PMimport arrow.core.Either
import arrow.core.raise.either
import arrow.core.raise.ensure
interface Prob
sealed interface HouseRoomProblem: Prob {
data object HasNoName: HouseRoomProblem
}
object HouseRoom {
data class Room(val name: String)
fun *create*(name: String): Either<HouseRoomProblem, Room> = _either_ *{*
ensure(name._*isNotEmpty*_()) *{*
HouseRoomProblem.HasNoName
}
*Room*(name)
}
}
object House {
data class House(val room: HouseRoom)
fun *create*(): Either<List<HouseRoomProblem>, House> = _either_ *{*
*House*(
room = HouseRoom.*create*("room1").bind()
)
}
}
Riccardo Cardin
02/19/2024, 8:48 PMinterface Prob
sealed interface HouseRoomProblem: Prob {
data object HasNoName: HouseRoomProblem
}
object HouseRoom {
data class Room(val name: String)
fun create(name: String): Either<HouseRoomProblem, Room> = either {
ensure(name.isNotEmpty()) {
HouseRoomProblem.HasNoName
}
Room(name)
}
}
object House {
data class House(val room: HouseRoom.Room)
fun create(): Either<HouseRoomProblem, House> = either {
House(
room = HouseRoom.create("room1").bind()
)
}
}
Riccardo Cardin
02/19/2024, 8:49 PMList<HouseRoomProblem>
, but with the fact that you don’t use/create the list at all 😅0xf1f1
02/19/2024, 8:50 PM0xf1f1
02/19/2024, 8:53 PMRiccardo Cardin
02/19/2024, 8:55 PM0xf1f1
02/19/2024, 9:05 PMRiccardo Cardin
02/20/2024, 8:01 AMobject House {
data class House(val room: HouseRoom.Room)
fun create(): Either<List<HouseRoomProblem>, House> = either {
House(
room = HouseRoom.create("room1").mapLeft { listOf(it) }.bind()
)
}
}
Otherwise, you should use the zipOrAccumulate
function if you want to accumulate all the validation errors.0xf1f1
02/20/2024, 8:56 AMRiccardo Cardin
02/20/2024, 11:23 AMLeft
instance, you always have a non-empty list of errors. So, you can use the EitherNel
, which uses a NonEmptyList
to accumulate errors.Riccardo Cardin
02/20/2024, 11:23 AM0xf1f1
02/20/2024, 8:05 PMRiccardo Cardin
02/20/2024, 8:50 PMcontext(NonZero<T>)
fun <T : Number> T.nonZero(fieldName: String): EitherNel<ZeroFieldError, T> =
either {
ensure(nonZero()) { nonEmptyListOf(ZeroFieldError(fieldName)) }
this@nonZero
}
Riccardo Cardin
02/20/2024, 8:51 PMbind()
works like a charm with EitherNel
since this last type is only a type alias for Either<NonEmptyList<E>, A>
Riccardo Cardin
02/20/2024, 8:52 PM0xf1f1
02/20/2024, 9:19 PM