Đặc tả base Clean Architecture
Công nghệ sử dụng :
- Quản lý state : flutter_bloc
- Kiến trúc : clean
- Dependency injection: get_it
- Generate code : injectable, retrofit, json_serializable
- Rule: lint, ci
Tài liệu tham khảo :
Kiến trúc
- https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html
- https://geekysingh.medium.com/clean-architecture-for-enterprise-flutter-application-dc254a71059
- https://viblo.asia/p/clean-architecture-Ljy5VMYzlra
- https://github.com/ResoCoder/flutter-tdd-clean-architecture-course
Thư viện
- https://pub.dev/packages/flutter_bloc
- https://pub.dev/packages/get_it
- https://pub.dev/packages/retrofit
- https://pub.dev/packages/injectable
- https://pub.dev/packages/json_serializable
Mục Lục Phần 1 : Kiến trúc clean
- Giải thích khái niệm
- Mô tả luồng kiến trúc
- Mô tả tổng quan giải thích tác dụng của các pakage
- Mô tả luồng đi của dữ liệu
Phần 2 : flutter_bloc
- Các widget cơ bản
- Mô tả cách sử dụng
- Bloc thường
- Cubit
Phần 3 : Một số library generate code được sử dụng trong base
- Danh sách các generate code và giải thích tác dụng
- Mô tả chi tiết cách sử dụng của các generate code
Phần 1 : Kiến trúc clean
1. Giải thích khái niệm kiến trúc clean là gì ? Clean Architecture được xây dựng dựa trên tư tưởng "độc lập" kết hợp với các nguyên lý thiết kế hướng đối tượng(đại diện tiêu biểu là Dependency Inversion). Độc lập ở đây nghĩa là việc project không bị phụ thuộc vào framework và các công cụ sử dụng trong quá trình kiểm thử.
Kiến trúc của Clean Architecture chia thành 4 layer với một 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. Điều này có nghĩa là nó có quan hệ phụ thuộc nên "hướng" vào bên trong. Nhìn vào hình vẽ minh họa sau đây:
2. Mô tả kiến trúc
a. Mô tả pakage và tác dụng của pakage
Với base này để phù hợp hơn với project đặc thù mobile chúng tôi áp dụng sẽ chia ra làm 4 tầng cơ bản bao gồm :
Core : ở đây sẽ chứa tất cả những gì chung nhất của của app
Config: chứa các biến môi trường khi run bằng các flavor khác nhau ( dev,beta hay product)
Data : với layer này sẽ có nhiệm vụ lấy dữ liệu từ api hay local storage
- Local: tại đây có nhiệm vụ lấy và lưu trữ thông tin lên local storage
- Networking : sẽ chứa thông tin call api ( lưu ý tại đây dùng retrofit để gen file .g triển khai các hàm call)
- Repository: sẽ chứa các hàm triển khai của các repo mà ta khai báo tại domain
Domain: có nhiệm vụ chứa các hành động của các đối tượng
- Entities: chứa các model và hàm fromJson toJson của model các model chung sẽ được đưa vào common
- Repositories: chứa các abstract class mô tả các function của đối tượng
- Usecase: chứa các hành động của đối tượng và được gọi ra tại bloc xử lý logic của model
(lưu ý các export sẽ được khai báo tại index và nhúng vào domain để gọi )
View : Tại layer này sẽ có nhiệm vụ xử lý logic của view và hiển thị ra màn hình
- Main: các hàm main sẽ run theo flavor
- App: ở đây sẽ chứa widget khởi tạo ban đầu của app
- Router : tại đây sẽ chứa các tên router và hàm quản lý router màn hình của app
- Utils: chứa các component sử dụng chung của app
- Screen: chứa các màn hình và logic xử lý giao diện
Di : tại đây chứa toàn bộ hàm khởi tạo và singleton của mỗi pakage ( tất cả sẽ được gen bằng injectable ra file .config và không sửa bằng tay file này) sau khi khai báo hết các injectable của các file cần thiết chạy lệnh flutter packages pub run build_runner build tại pakage để tiến hành tạo file và import di
b.Mô tả luồng đi dữ liệu
Hướng dẫn dùng base call api:
Domain ( lưu ý tạo export tại index để import vào domain)
- Tạo model cho dữ liệu sẽ trả về từ api ( lưu ý dùng json_serializable tạo file gen fromJson và toJson)
- Tạo repo cho đối tượng tại repositories chứa các function sẽ gọi tại domain)
- Tạo usercase chứa các hành động của đối tượng ( lưu ý khai báo di tại đây) **Data **
- Tạo file client tại networking( lưu ý sử dụng retrofit để gen file thực thi)
(Với file client cần khai báo di tại locator trong di với cú pháp)
- Tạo file thực thi của repo(domain) tại repositories
View :
Tại bloc của view tiến hành khai báo usecase và injectable như hình
Tại đây dưới hàm call api ta call hàm hành động của đối tượng tại response.when sẽ trả ra dữ liệu thành công tại success và thất bại tại error
Luồng dữ liệu : Dữ liệu sẽ được đổ từ repoImpl(data) xuống usecase(domain) tới hàm call api( view)
Phần 2 : flutter_bloc
1.Các widget cơ bản của flutter_bloc
- BlocProvider : Đây là widget được bọc ngoài cùng để khởi tạo bloc tại param create
- BlocListener: Đây là widget sẽ lắng nghe sự thay đổi của state của bloc để thực thi function tại listener hoặc sẽ thực thi theo điều kiện tại listenWhen
- BlocBuilder: Đây là widget sẽ rebuild lại mỗi khi state thay đổi hoặc sẽ rebuild theo điều kiện tại buildWhen
- BlocSelector: Đây là widget sẽ rebuild lại theo state mà bạn muốn
- BlocConsumer: Đây là sự kết hợp của blocBuilder và Bloc Listener
2.Cách sử dụng
a.Bloc thường
Cấu trúc file
Bao gồm directory bloc ( đây là khối sẽ đại diện cho việc xử lý bloc của view ) tại đây sẽ bao gồm :
- Event : chứa các sự kiện của logic ví dụ login , logout
- Bloc : đây sẽ là nơi thực thi các event
- State: đây là nới chứa trạng thái của bloc
- Provider: đây là file khởi tạo bloc trong này sẽ chứa BlocProvider
State:
Tại đây sẽ chứa các trạng thái của bloc mỗi khi ta thay đổi trạng thái này sẽ kéo các widget rebuild
Lưu ý hãy khai báo Equatable tại đây để tiện so sánh
Event:
Tại đây sẽ chứa các hành động của bloc .Ví dụ như trong ảnh ta sẽ tạo ra 1 class kế thừa LoginEvent và trong đó khai báo các thông tin cần truyền vào để thay đổi event
Bloc
Đây sẽ là nơi xử lý logic của view chúng sẽ được xử lý theo event
Như ảnh dưới ta có thể thấy trạng thái ban đầu của bloc sẽ được khai báo trong super phần LoginState(). Phía dưới sẽ là khai báo về các event của bloc(on<LoginWithUserNameAndPassEvent>(_loginWithUserNameAndPass))
Nó sẽ nghe xem bloc đang ở trạng thái nào và trả về hàm Future như bên dưới khí đó trong hàm này sẽ chịu trách nhiệm lấy dữ liệu và bắn dữ liệu đi bằng cách sử dụng hàm emit
Provider:
Class khai báo khởi tạo bloc như bên dưới ta có thể thấy ta bọc nó ngoài cùng của khối giao diện ta muốn thay đổi
b.Cubit
Để tránh sự rắc rối của bloc thường thì nhà phát hành đưa ra Cubit là một cách triển khai đơn giản và dễ tiếp cận hơn
File Cubit sẽ là phần tượng tự như file bloc như ở đây ta sẽ tạo từng hàm có emit thực thi truyền state luôn
Phía view sẽ thực hiện việc khai báo provider và lắng nghe sự thay đổi như ảnh trên
Phần 3 : Một số library generate code được sử dụng trong base
1.Danh sách các generate code và giải thích tác dụng
a.Injectable
Là một thư viện gen code giúp chúng ta tạo ra các hàm khởi tạo và singleton trong di của từng pakage
b.Retrofit
Là thư viện giúp chúng ta tạo ra các file call api và map dữ liệu json để có cái nhìn tổng quan về các api được gọi trong đối tượng
c.Json_serializable
Là thư viện giúp generate file model để tạo ra hàm fromJson và toJson
2.Mô tả chi tiết cách sử dụng của các generate code
a.Injectable
Với các cách khởi tạo khác nhau ta dùng các từ khóa ở đầu các file khác nhau ví dụ@injectable
(tạo factory) hay@singleton
(tạo Singleton) . . .
Đây sẽ là cách đánh dấu khởi tạo singleton của các đối tượng khi chúng ta call api dùng retrofit
Còn đây là cách đánh dấu với các file còn lại
Sau khi được đánh dấu xong chúng ta chạy câu lệnh flutter packages pub run build_runner build
tại terminal của pakage tất cả sẽ được gen trong file locator.config
b.Retrofit
Khi tạo api ta cần khai báo @RestApi() tại đầu các abstract class liệt kê các api cần call .Ta truyền uri của api với cú pháp @POST('/login')
sau đó truyền các param hoặc body vào trong hàm future ta chạy lệnh flutter pub run build_runner
lệnh để tạo ra file .g.dart tất cả việc call api và map Json sẽ được thực thi tại đây
All rights reserved