+8

Android Clean Architecture - P2: Clean Architecture trong Android và ví dụ

Bài viết trước mình đã trình bày tổng quan về Clean Architecure rồi. Ý tưởng của Clean Architecture là có thể đáp ứng các tiêu chí:

  • Independent of Frameworks
  • Testable
  • Independent of UI
  • Independent of Database
  • Independent of any external agency

và không nhất thiết chúng ta phải sử dụng cả 4 vòng tròn layer. Chúng ta chỉ cần xem nó như là 1 sơ đồ đề follow, tuy nhiên có một điều tất yếu cần chú ý là Dependency Rule (quy tắc phụ thuộc).

Các layer bên trong không nên biết bất kỳ điều gì về các layer bên ngoài. Về cơ bản, quy tắc này nói rằng dù định dạng dữ liệu của layer ngoài cùng như thế nào thì sẽ không ảnh hưởng đến layer bên trong, hay việc thay đổi business rule ở layer bên trong cũng không ảnh hưởng đến việc hiển thị ở layer bên ngoài.

Follow idea của Clean Architecture, chúng ta cần tách biệt các layers, ví dụ như domain, storage, presentation. Tuy nhiên, Clean Architecture không có hạn chế về việc define các layers 1 cách chính xác. Chúng ta có thể sử dụng số lượng layers sao cho phù hợp với nhu cầu.

Mình có tham khảo một số nguồn, thì thấy phần mọi người hay chia thành 3 layers: domain, data, presentation. Do ví dụ demo cũng đơn giản nên mình cũng sẽ chia project thành 3 layers:

Do hình mình copy trên mạng, nhưng demo mình đang sử dụng MVVM nên mọi người cứ hiểu Presenters ở đây là ViewModels nếu sử dụng MVVM nhé.

  • View không chứa bất kì business logic nào mà chỉ tương tác với ViewModel
  • ViewModel không chứa bất kì business logic nào và chỉ tương tác với Domain layers thông qua Use cases (hoặc thi thoảng mình thấy người ta gọi là Interactors).
  • Domain layer chứa business logic, các Entities chính là các models (thông thường là POJOs hoặc cũng có thể là các object cùng với một vài logic or validation logic), data class, interfaces. Use cases class là nơi thể hiện business logic, mỗi use case nên được giới hạn bởi 1 feature (follow Single Responsibility Principle). Use cases đóng vai trò như là 1 middle-man giữa Presentation layer và Data layer nhưng lại không biết gì về 2 layers kia nhờ vào virtue of Inversion of Control (Google translate ra là: tính đảo ngược của kiểm soát. Nghe dị dị nên mình để nguyên bản tiếng Anh).
  • Data layer sẽ implement Domain layer abstractions. Mục đích là để cô lập Data layer khỏi Presentation layer để chúng ta có thể dễ dàng thay đổi nó khi cần. Ví dụ project đang sử dụng Room database ở Data layer, chúng ta có thay đổi một database khác 1 cách dễ dàng. Nói chung các hoạt động storing, fetching data or request network chúng ta sẽ thực hiện ở đây.
  • Presentation layer chứa các thứ liên quan đến UI và Android như là view architecture(MVP, MVVM...), fragment, activity,...

Tách biệt các layers bằng cách sử dụng Gradle module giúp code dễ đọc, maintain, extend và testing.

I. Multi-Project Gradle setup

Project example bao gồm 3 Gradle modules:

dependencies {
    implementation project(path: ':domain')
    implementation project(path: ':data')
    ...
}
  • data— Android library module
dependencies {
    implementation project(path: ':domain')
    ...
}
  • domain — Java library module

II. Example step by step

Code mình không add vào đây để tránh rối mắt. Mọi người follow ở Github nhé.

Bài toán: Ứng dụng note text. Sử dụng:

Project structure:

1. Domain layer

Packages: model -> repository -> usecase:

  • Note.kt: Đơn giản là 1 object bình thường có validation logic thôi.
  • NoteRepository & NoteModelMapper: là Domain layer abstractions, cung cấp các phương thức access data. Ở Data layer sẽ thực hiện implement interface này.
  • AddNoteUseCase & GetNotesUseCase: Đây là các use case của hệ thống, "middle-man giữa Presentation layer và Data layer".

2. Data layer

Packages/class: model -> local.db -> impl

3. Presentation

Ở layer này chắc ai cũng thấy rất quen thuộc rồi, ở đây chúng ta có thể sử dụng các mô hình MVP, MVVM như bình thường. Như đã trình bày ở trên, ở layer này sẽ sử dụng usecase ở domain layer như 1 middle man của Presentation layer và Data layer. package:

  • di: chính là DI - Koin mà mình nói ở phía trên.
  • Mọi người chỉ cần lưu ý 1 điểm là ở AddNoteViewModel là mình đang sử dụng các usecase thay vì repository như ở các mô hình MVP, MVVM thông thường, vd: StatisticsViewModel hoặc StatisticsPresenter của architecture-sample của Google.

Và cuối cùng là thành quả.

Bài chia sẻ này là theo ý hiểu của cá nhân mình nên không thể tránh được việc thiếu sót và những điểm chưa đúng. Rất mong nhận được nhận xét và giúp đỡ từ mọi người. Nguồn tham khảo: https://proandroiddev.com/kotlin-clean-architecture-1ad42fcd97fa

Các bạn dành chút thời gian để đọc thêm phần bình luận nhé. Rất quan trọng đó 😅


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí