Why can't I set a class in Domain Layer as abstract class with Clean Architecture?

Viewed 546

I'm learning Clean Architecture with the artical.

I know Domain Layer is the most INNER part of the onion (no dependencies with other layers) and it contains Entities, Use cases & Repository Interfaces.

I think I should use abstract class or interface in Use Case in Domain Layout, you can see Image 1 in the artical above.

Image 1 enter image description here

The following code is from the project https://github.com/igorwojda/android-showcase

The internal class GetAlbumListUseCase is in Domain Layer, you can see Image 2.

I think GetAlbumListUseCase should be abstract class.

But I get "Cannot create an instance of an abstract class" error when I replace it with internal abstract class GetAlbumListUseCase(...).

1: What wrong with me? Does it cause by AlbumDomainModule.kt during Kodein ?

2: Should I use abstract class or interface in Use Case in Domain Layout?

GetAlbumListUseCase.kt

internal class GetAlbumListUseCase(
    private val albumRepository: AlbumRepository
) {

    sealed class Result {
        data class Success(val data: List<Album>) : Result()
        data class Error(val e: Throwable) : Result()
    }

    suspend fun execute(): Result {
        // Due to API limitations search with custom phrase have to be performed to get albums
        val phrase = "Jackson"

        return try {
            Result.Success(albumRepository.searchAlbum(phrase).filter { it.getDefaultImageUrl() != null })
        } catch (e: IOException) {
            Result.Error(e)
        }
    }
}

AlbumDomainModule.kt

internal val domainModule = Kodein.Module("${MODULE_NAME}DomainModule") {

    bind() from singleton { GetAlbumListUseCase(instance()) }

    bind() from singleton { GetAlbumUseCase(instance()) }
}

Image 2

enter image description here

1 Answers

You can't instantiate an abstract class, because it is... well, abstract.

You misunderstood what it means that we should use abstractions. Your components should depend on abstractions (preferably interfaces, not abstract classes), but you still need to provide implementations (normal, not abstract classes). To do this, split your existing class into interface + its implementation, use implementation when configuring DI and use interface everywhere else.

For example, rename your GetAlbumListUseCase into GetAlbumListUseCaseImpl, create an interface GetAlbumListUseCase with its API (in IntelliJ you can use refactor -> extract interface) and make GetAlbumListUseCaseImpl implement GetAlbumListUseCase. Then use GetAlbumListUseCase everywhere in your code, but configure DI with GetAlbumListUseCaseImpl. This is exactly as with PostRepository and PostRepositoryImpl in the article linked by you.

With such architecture you could create alternative implementations of GetAlbumListUseCase in the future and switch them smoothly. You could even use multiple implementations at the same time, for example different objects use different implementations GetAlbumListUseCase. Note that in your current architecture all objects directly depend on a specific implementation, so switching to another one requires to modify half of your code.

Related