With the arrival of Arrow 1.1.x we got the new Effect class.
Up to now, my business classes returned Either to model the effect of returning an error or a value, e.g.:
@Service
class CreateDepartmentUseCaseImpl(
private val createDepartmentsDrivenPort: CreateDepartmentsDrivenPort,
private val getDepartmentByCodeDrivenPort: GetDepartmentByCodeDrivenPort
) : CreateDepartmentUseCase {
override suspend fun execute(param: Department): Either<DomainError, Department> =
Either.conditionally(getDepartmentByCodeDrivenPort.get(param.code) == null,
{ DepartmentAlreadyExistsError(param.code.value) },
{ createDepartmentsDrivenPort.create(param) })
}
With the new Effect this could be refactored to something like:
@Service
class CreateDepartmentUseCaseImpl(
private val createDepartmentsDrivenPort: CreateDepartmentsDrivenPort,
private val getDepartmentByCodeDrivenPort: GetDepartmentByCodeDrivenPort
) : CreateDepartmentUseCase {
override suspend fun execute(param: Department): Effect<DomainError, Department> = effect {
ensure(getDepartmentByCodeDrivenPort.get(param.code) == null) { DepartmentAlreadyExistsError(param.code.value) }
createDepartmentsDrivenPort.create(param)
}
}
In tests, the mocking changed from:
@Test
fun `should create department`() = runTest {
val dep = createValidDepartment()
val createDepartmentRequest = CreateDepartmentRequest(dep.code.value, dep.name.value, dep.description.value)
`when`(createDepartmentsUseCase.execute(dep)).thenReturn(dep.right())
to...
@Test
fun `should create department`() = runTest {
val dep: Department = createValidDepartment()
val createDepartmentRequest = CreateDepartmentRequest(dep.code.value, dep.name.value, dep.description.value)
`when`(createDepartmentsUseCase.execute(dep)).thenReturn(effect { dep })
The question is, what's the best approach to model our business services, the new Effect class or Either, and why?