# Clean Architecture in Jetpack Compose


By employing [clean architecture](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html), you can design applications with very low coupling and independent of technical implementation details, such as databases and frameworks. That way, the application becomes easy to maintain and flexible to change. It also becomes intrinsically testable. Here I’ll show how I structure my clean architecture projects. This time we are going to build an Android todo application using Jetpack Compose. We’ll only illustrate one use case of listing todos retrieved from an API. Let’s get started.

The package structure of the project takes on the following form:

```
├── Core
├── Data
├── Domain
└── Presentation
```


Let’s start with the **Domain** Layer.

This layer describes **WHAT** your project/application does. Let me explain, Many applications are built and structured in a way that you cannot understand what the application does just by looking at the folder structure. Using a building of a house analogy, you can quickly identify what a building would look like and its functionality by viewing the floor plan and elevation of the building

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1635162197103/c6FGyQJJi.png)

In the same way, the domain layer of our project should specify and describe **WHAT** our application does. In this folder, we would keep use models, repository interfaces, and use cases.

```
├── Core
├── Data
├── Presentation
└── Domain
    ├── Model
    │   ├── Todo.kt
    │   └── User.kt
    ├── Repository
    │   ├── TodoRepository.kt
    │   └── UserRepository.kt
    └── UseCase
        ├── Todo
        │   ├── GetTodos.kt
        │   ├── GetTodo.kt
        │   ├── DeleteTodo.kt
        │   ├── UpdateTodo.kt
        │   └── CreateTodo.kt
        └── User
            ├── GetUsers.kt
            ├── GetUser.kt
            ├── DeleteUser.kt
            ├── UpdateUser.kt
            └── CreateUser.kt
```


1. **Model**: A model typically represents a real-world object that is related to the problem. In this folder, we would typically keep classes to represent objects. e.g. Todo, User, etc

1. **Repository**: Container for all repository interfaces. The repository is a central place to keep all model-specific operations. In this case, the Todo repository interface would describe repository methods. The actual repository implementation will be kept in the **Data** layer.

1. **UseCases: **Container to list all functionality of our application. e.g *Get, Delete, Create , Update*

The **PRESENTATION** layer will keep all of the consumer-related code as to **HOW** the application will interact with the outside world. The presentation layer can be WebForms, Command Line Interface, API Endpoints, etc. In this case, it would be the screens for a List of Todos and its accompanying view model.

```
├── Core
├── Data
├── Domain
└── Presentation
    └── Todo
        └── TodoList
            ├── TodoListViewModel.kt
            └── TodoListView.kt
```


The** DATA** layer will keep all the external dependency-related code as to **HOW **they are implemented:

```
├── Core
├── Domain
├── Presentation
├── Data
    ├── Repository
    │   ├── TodoRepositoryImpl.kt
    │   ├── TodoAPIDataSourceImpl.kt
    │   └── TodoDBDataSourceImpl.kt
    └── DataSource
        ├── API
        │   ├── TodoAPIDataSource.kt.
        │   └── Entity
        │       ├── TodoAPIEntity.kt
        │       └── UserAPIEntity.kt
        └── DB
            ├── TodoDBDataSource.kt.
            └── Entity
                ├── TodoDBEntity.kt
                └── UserDBEntity.kt
```


1. **Repository**: Repository implementations

1. **DataSource**: All data source **interfaces** and **entities**. An entity represents a single instance of your domain object saved into the database as a record. It has some attributes that we represent as columns in our DB tables or API endpoints. We can’t control how data is modeled on the external data source, so these entities are required to be mapped from entities to domain models in the implementations

and lastly, the **CORE **layer keep all the components that are common across all layers like constants or configs or dependency injection (which we won’t cover)

Our first task would be always to start with the domain models and data entities

```kotlin
data class Todo(
    val id: Int,
    val isCompleted: Boolean,
    val task: String

)
```

```kotlin
data class TodoAPIEntity(
    val id: Int,
    val completed: Boolean,
    val title: String
)

fun TodoAPIEntity.toTodo(): Todo {
    return Todo(
        id = id,
        isCompleted = completed,
        task = title
    )
}
```

Let’s now write an interface for the TodoDatasource. We need one to enforce how any data source (API, DB, etc) needs to behave.

```

interface TodoDataSource {    
    suspend fun getTodos(): List<Todo>
}
```


We have enough to write an implementation of this interface and we’ll call it TodoAPIImpl:

```kotlin
import toTodo
import za.co.nanosoft.cleantodo.Data.DataSource.TodoAPI
import za.co.nanosoft.cleantodo.Data.DataSource.TodoDataSource
import za.co.nanosoft.cleantodo.Domain.Model.Todo


interface TodoApi {

    @GET("todos")
    suspend fun getTodos(): List<TodoAPIEntity>

    companion object {
        var todoApi: TodoApi? = null
        fun getInstance(): TodoApi {
            if (todoApi == null) {
                todoApi = Retrofit.Builder()
                    .baseUrl(BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build().create(TodoApi::class.java)
            }
            return todoApi!!
        }
    }
}

class TodoAPIImpl : TodoDataSource {

    override suspend fun getTodos(): List<Todo> {
        return TodoAPI.getInstance().getTodos().map { it.toTodo() }
    }
}
```

**Note:** this repository’s getTodos function returns a list of Todo. So, we have to map TodoAPIEntity -&gt; Todo:

Before we write our TodoRepositoryImpl let’s write the interface for that in the Domain layer

```kotlin
import za.co.nanosoft.cleantodo.Domain.Model.Todo

interface TodoRepository {
    suspend fun getTodos(): List<Todo>

}
```


We can now see that the TodoRepositoryImpl can take any datasource as a dependency, great for swapping out datasources.

Now that we have our todo repository, we can code up the GetTodos use case

```kotlin
import za.co.nanosoft.cleantodo.Domain.Model.Todo
import za.co.nanosoft.cleantodo.Domain.Repository.TodoRepository

class GetTodos(
    private val repository: TodoRepository
) {
    suspend operator fun invoke(): List<Todo> {
        return repository.getTodos()
    }
}
```

and then in turn we can write our presentation’s view model and view

```kotlin
import androidx.compose.runtime.mutableStateListOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch
import za.co.nanosoft.cleantodo.Domain.Model.Todo
import za.co.nanosoft.cleantodo.Domain.UseCase.GetTodos

class TodoViewModel constructor(
    private val getTodosUseCase: GetTodos
) : ViewModel() {
    private val _todos = mutableStateListOf<Todo>()

    val todos: List<Todo>
        get() = _todos


    suspend fun getTodos() {
        viewModelScope.launch {
            _todos.addAll(getTodosUseCase())
        }
    }
}
```

```kotlin

@Composable
fun TodoListView(vm: TodoViewModel) {

    LaunchedEffect(Unit, block = {
        vm.getTodos()
    })

    Scaffold(
        topBar = {
            TopAppBar(
                title = {
                    Text("Todos")
                }
            )
        },
        content = {
            Column(modifier = Modifier.padding(16.dp)) {
                LazyColumn(modifier = Modifier.fillMaxHeight()) {
                    items(vm.todos) { todo ->
                        Row(modifier = Modifier.padding(16.dp)) {
                            Checkbox(checked = todo.isCompleted, onCheckedChange = null)
                            Spacer(Modifier.width(5.dp))
                            Text(todo.task)
                        }
                        Divider()
                    }
                }
            }
        }
    )
}
```


So to recap:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1635162198715/9DSCo9-A_.png)

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1635162200449/Ythc_BSPW.png)
