Jetpack構成のフレーバにおけるクリーンアーキテクチャ


クリーンアーキテクチャを採用することにより、非常に低い結合とデータベースやフレームワークなどの技術的な実装の詳細から独立したアプリケーションを設計することができます.そのように、アプリケーションを維持し、柔軟に変更することが容易になります.また、それは本質的にテスト可能になります.ここでは、クリーンアーキテクチャプロジェクトの仕組みを紹介します.今回はJetpack Composeを使用してAndroid ToDoアプリケーションを構築します.APIから取得したdosを一覧表示する場合にのみ使用します.始めましょう.
プロジェクトのパッケージ構造は次のようになります.

下から始まる.
プレゼンテーション層は、UI関連のすべてのコードを保持します.この場合、todosのリストのビューとビューモデルになります.
ドメイン層は、すべてのビジネスロジックを保持し、どのようなコードを行うのかの良いアイデアをプロジェクトに訪問者を与えます.このレイヤでは、

  • USECASE :使用中のファイル、

  • リポジトリ:リポジトリインターフェイス

  • モデルロジックビジネスケースとUI
  • で参照するtodoのようなビジネスモデル
    データ層:

  • リポジトリ:リポジトリの実装

  • DataSource :すべてのデータソースインターフェイスとデータソースエンティティ.これらの実体はドメインモデルと異なっていて、直接APIから要求と応答オブジェクトにマップします.
  • そして最後に、コア層は、定数やコンフィグや依存関係の注入のようなすべての層全体に共通のすべてのコンポーネントを保持します

    我々の最初のタスクは、常にドメインモデルとデータエンティティ
    data class Todo(
        val id: Int,
        val isCompleted: Boolean,
        val task: String
    
    )
    
    
    data class TodoAPIEntity(
        val id: Int,
        val completed: Boolean,
        val title: String
    )
    
    fun TodoAPIEntity.toTodo(): Todo {
        return Todo(
            id = id,
            isCompleted = completed,
            task = title
        )
    }
    
    さあ、ToDoDataSourceのインターフェイスを書きましょう.どのようなデータソース(API、DBなど)が振る舞う必要があるかを強制するためには、1つが必要です.
    import za.co.nanosoft.cleantodo.Domain.Model.Todo 
    interface TodoDataSource {    
        suspend fun getTodos(): List<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() }
        }
    }
    
    注意:このリポジトリのgetTodos関数は、todoのリストを返します.したがって、ToDoEntity -> ToDoをマップする必要があります.
    我々のtodorepositoryimplを書く前に、ドメイン層でそれのためにインターフェースを書きましょう
    interface TodoRepository {
        suspend fun getTodos(): List<Todo>
    
    }
    
    class TodoRepositoryImpl(private val datasource: TodoDataSource) : TodoRepository {
    
        override suspend fun getTodos(): List<Todo> {
            return datasource.getTodos()
        }
    }
    
    
    todorepositoryimplは依存関係としてデータソースを取ることができ、データソースを交換するのに最適です.
    TODOリポジトリを持っているので、getTodosユースケースをコード化できます
    class GetTodos(
        private val repository: TodoRepository
    ) {
        suspend operator fun invoke(): List<Todo> {
            return repository.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())
            }
        }
    }
    
    @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()
                        }
                    }
                }
            }
        )
    }
    
    class MainActivity : ComponentActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            val vm = TodoViewModel(
                getTodosUseCase = GetTodos(
                    repository = TodoRepositoryImpl(
                        api = TodoAPIImpl()
                    )
                )
            )
            super.onCreate(savedInstanceState)
            setContent {
                CleantodoTheme {
                    TodoListView(vm)
                }
            }
        }
    }
    
    以下に要約する.