Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Which should we choose between Effect and Either as a return type of our business services?

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:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

    @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?

>Solution :

There is no reason to refactor to Effect if Either fits your use case.

The question is, what’s the best approach to model our business services, the new Effect class or Either, and why?

Sadly as often in software, there is no 1-fit-all answer but to model business services I would in general recommend Either.
Effect<E, A> is semantically equivalent to suspend EffectScope<E>.() -> A, and the result of running that lambda can be Either<E, A>.

That is also why we can assign Effect to a val, and when returning Either we need to invoke the suspend lambda which requires suspend fun.

val operation: Effect<E, A> =
  effect { ... }

suspend fun operation(): Either<E, A> =
  operation.toEither()

So if you just need to model the result of an operation then using Either<E, A> is desired. If you need to pass around a lambda/operation then using Effect can be used.

You can consider Effect a lower level implementation than Either, since all computation blocks such as either { }, option { }, ior { }, result { }, etc are now all implemented through effect.

Which you can see in the Arrow implementation for either { }.

suspend fun <E, A> either(block: suspend EffectScope<E>.() -> A): Either<E, A> =
  effect(block).toEither()
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading