FlutterClean ArchitectureSimplified Clean ArchitectureArchitectureProductivity

Simplified Clean Architecture: Cân Bằng Giữa Kiến Trúc Và Tốc Độ Delivery

Cách tôi tinh gọn Clean Architecture trong Flutter để giữ lại những ranh giới quan trọng, giảm boilerplate và tăng tốc độ phát triển ở các module thay đổi nhanh.

S
Phạm Hoàng Sang
··8 phút đọc
Simplified Clean Architecture: Cân Bằng Giữa Kiến Trúc Và Tốc Độ Delivery

Timeline bài viết

Giới thiệu

Trong bài viết trước, tôi đã chia sẻ cách tôi sử dụng Clean Architecture để xây dựng các ứng dụng Flutter dễ bảo trì và tạo ra những ranh giới rõ ràng giữa business logic, infrastructure và presentation.

Nếu bạn chưa đọc bài nền tảng này, nên xem trước để có đầy đủ ngữ cảnh:

Xem thêm: Thiết Kế Ứng Dụng Flutter Dễ Bảo Trì Với Clean Architecture

Clean Architecture đã phục vụ tôi rất tốt trong các môi trường enterprise. Nó cải thiện khả năng bảo trì, hỗ trợ testing tốt hơn và bảo vệ business logic khỏi thay đổi từ framework hoặc backend.

Tuy nhiên, sau khi áp dụng cùng một cách tiếp cận trên nhiều module và sản phẩm, tôi bắt đầu nhận ra một pattern quan trọng:

Không phải bài toán nào cũng cần cùng một mức độ kiến trúc.

Một số module thực sự hưởng lợi từ việc tách biệt chặt chẽ giữa Models, Entities, Mappers, Repositories và Use Cases.

Nhưng cũng có những module chỉ trở nên chậm phát triển hơn mà không nhận lại được lợi ích dài hạn tương xứng.

Nhận ra điều đó khiến tôi chuyển sang một phong cách kiến trúc thực dụng hơn, mà tôi gọi là Simplified Clean Architecture (SCA).

Mục tiêu không phải là thay thế Clean Architecture.

Mục tiêu là giữ lại những ý tưởng có giá trị nhất của nó trong khi cắt giảm sự phức tạp không cần thiết.


Vấn đề của Clean Architecture quá chặt chẽ

Một trong những điểm mạnh của Clean Architecture là nhấn mạnh vào separation of concerns.

Một luồng dữ liệu điển hình thường trông như sau:

text
API
 ↓
Data Model
 ↓
Mapper
 ↓
Domain Entity
 ↓
Repository
 ↓
Use Case
 ↓
Controller
 ↓
UI

Cấu trúc này tạo ra ranh giới rất tốt.

Tuy nhiên, khi ứng dụng lớn dần, nó cũng có thể tạo ra lượng boilerplate đáng kể.

Hãy tưởng tượng một module có:

  • 15+ API
  • 15+ request model
  • 15+ response model
  • 15+ entity
  • 15+ hàm mapping

Mỗi khi backend thay đổi một field, nhiều layer có thể phải cập nhật theo.

Với domain phức tạp, overhead này là chấp nhận được.

Nhưng với các feature thay đổi nhanh, nó có thể làm chậm tốc độ phát triển đi rất nhiều.

Cuối cùng, tôi bắt đầu tự hỏi:

Tất cả những layer này có thật sự tạo ra giá trị, hay một phần trong số đó chỉ là theo đuổi kiến trúc vì chính kiến trúc?

Suy nghĩ lại về điều thật sự quan trọng

Thay vì mù quáng theo đuổi sự "thuần khiết" của kiến trúc, tôi bắt đầu đánh giá phần nào của Clean Architecture mang lại tỷ lệ lợi ích cao nhất so với chi phí bỏ ra.

Một số pattern luôn chứng minh được giá trị:

  • Repository abstractions
  • Dependency inversion
  • Use Cases
  • Ranh giới business rõ ràng
  • Testability

Một số pattern khác lại thường tạo ra chi phí bảo trì đáng kể:

  • Tách riêng Models và Entities cho các cấu trúc dữ liệu đơn giản
  • Lượng lớn code mapping lặp lại
  • Phân mảnh file quá mức

Từ đó dẫn đến một ý tưởng rất đơn giản:

Giữ những ranh giới bảo vệ business. Loại bỏ những layer chủ yếu chỉ làm nhiệm vụ chuyển dữ liệu qua lại.

Đó là nền tảng của Simplified Clean Architecture.


Simplified Clean Architecture là gì?

Simplified Clean Architecture vẫn tuân theo cùng triết lý với Clean Architecture truyền thống:

  • Business logic vẫn tách khỏi UI.
  • Infrastructure vẫn có thể thay thế.
  • Dependency vẫn hướng vào trong.

Khác biệt nằm ở việc giảm bớt những layer ít tạo giá trị cho một bài toán cụ thể.

Thay vì:

text
API
 ↓
Model
 ↓
Mapper
 ↓
Entity
 ↓
Use Case
 ↓
Controller

Tôi thường dùng:

text
API
 ↓
Entity
 ↓
Use Case
 ↓
Controller

Entity sẽ chịu trách nhiệm cho việc serialization và deserialization.

Ứng dụng loại bỏ được cả một layer mapping.

Kết quả là một hệ thống vẫn có cấu trúc nhưng giảm đáng kể boilerplate.


Giữ lại những phần thật sự quan trọng

Tinh gọn không có nghĩa là từ bỏ kiến trúc.

Một số nguyên lý vẫn được giữ nguyên.

Repository Contracts

Repository interface vẫn đóng vai trò trung tâm.

Business logic phụ thuộc vào abstraction thay vì implementation cụ thể.

Điều này giữ lại:

  • Testability
  • Dependency inversion
  • Tính linh hoạt của infrastructure

Ví dụ, việc chuyển từ remote API sang local storage không nên ảnh hưởng đến business layer.

Ứng dụng vẫn hưởng lợi từ ranh giới rõ ràng giữa điều hệ thống cần và cách nó được triển khai.

Use Cases

Use Cases vẫn là một phần quan trọng của kiến trúc.

Tuy nhiên, tôi không còn tin rằng mọi action đều cần một file riêng.

Clean Architecture truyền thống thường dẫn đến các cấu trúc như:

text
create_account_use_case.dart
update_account_use_case.dart
delete_account_use_case.dart
favorite_account_use_case.dart
archive_account_use_case.dart

Về mặt kỹ thuật thì khá "sạch", nhưng cách này có thể nhanh chóng trở nên khó điều hướng.

Trong SCA, các business operation có liên quan có thể được gom lại:

text
account_management.use_cases.dart

Cách này vẫn giữ ranh giới business nhưng cải thiện khả năng khám phá code và giảm phân mảnh file.

Mục tiêu không phải là có ít Use Case hơn.

Mục tiêu là làm cho business rules dễ hiểu hơn.

Dumb Controllers

Một khái niệm khác mà tôi vẫn giữ rất chặt là Dumb Controllers.

Controller nên điều phối state thay vì sở hữu business logic.

Chúng có thể xử lý:

  • Loading states
  • Error states
  • UI sorting
  • UI filtering
  • User interactions

Chúng không nên chứa:

  • Financial calculations
  • Validation rules
  • Data transformation logic
  • Network orchestration
  • Core business decisions

Khi business rules bắt đầu phình lên trong controller, kiến trúc sẽ dần quay trở lại những vấn đề thường thấy ở các codebase MVVM lớn.


Loại bỏ Model Layer

Khác biệt lớn nhất giữa Strict Clean Architecture và SCA là việc loại bỏ dedicated Data Models.

Trong cách triển khai truyền thống:

text
Response DTO
 ↓
Mapper
 ↓
Entity

Trong SCA:

dart
@JsonSerializable()
class AssetAccountEntity {
  ...
}

Entity trực tiếp xử lý các vấn đề liên quan đến serialization.

Điều này mang lại nhiều lợi ích:

  • Ít file hơn
  • Ít trùng lặp hơn
  • Ít code mapping hơn
  • Phát triển feature nhanh hơn

Tuy nhiên, có một đánh đổi quan trọng.

Domain layer không còn hoàn toàn tách biệt khỏi serialization framework.

Kiến trúc hy sinh một phần tính "thuần khiết" để đổi lấy sự đơn giản.

Với nhiều ứng dụng business, tôi cho rằng đây là một đánh đổi hợp lý.


Phân tích đánh đổi

Mọi quyết định kiến trúc đều đi kèm thỏa hiệp.

Câu hỏi không phải là có trade-off hay không.

Câu hỏi là trade-off đó có đáng giá hay không.

Strict Clean Architecture

Điểm mạnh

  • Separation of concerns rất rõ
  • Độ linh hoạt tối đa
  • Testability rất tốt
  • Khả năng bảo trì dài hạn cao

Điểm yếu

  • Nhiều boilerplate
  • Nhiều file
  • Learning curve cao hơn
  • Tốc độ phát triển feature chậm hơn

Simplified Clean Architecture

Điểm mạnh

  • Tốc độ phát triển nhanh hơn
  • Giảm boilerplate
  • Dễ điều hướng hơn
  • Giảm tải nhận thức
  • Vẫn giữ được định hướng kiến trúc mạnh

Điểm yếu

  • Domain layer bớt "thuần" hơn một chút
  • Coupling nhiều hơn với serialization framework
  • Kém linh hoạt hơn khi cần thêm các persistence mechanism thay thế

Không có cách nào luôn tốt hơn trong mọi tình huống.

Lựa chọn tốt nhất phụ thuộc vào độ phức tạp và vòng đời kỳ vọng của sản phẩm.


Khi nào tôi chọn SCA

Tôi thường chọn Simplified Clean Architecture khi:

  • Feature đang thay đổi nhanh.
  • Team cần tốc độ delivery cao.
  • Phần lớn data structure khá giống API response.
  • Ứng dụng ở quy mô vừa thay vì cực lớn.
  • Việc giảm boilerplate cải thiện năng suất rõ rệt.

Trong những trường hợp này, lợi ích thường lớn hơn phần đánh đổi về kiến trúc.


Khi nào tôi vẫn ưu tiên Strict Clean Architecture

Tôi vẫn dùng Clean Architecture truyền thống khi:

  • Business rules rất phức tạp.
  • Offline-first support là yếu tố quan trọng.
  • Nhiều data source phải cùng tồn tại.
  • Khả năng bảo trì dài hạn là ưu tiên cao nhất.
  • Dự án được kỳ vọng tăng trưởng mạnh trong nhiều năm.

Trong những trường hợp này, các layer abstraction bổ sung vẫn rất có giá trị.


Bài học quan trọng nhất

Một trong những thay đổi lớn nhất trong tư duy engineering của tôi đến từ việc nhận ra rằng kiến trúc không phải là tối đa hóa abstraction.

Nó là giải quyết vấn đề một cách hiệu quả.

Khi mới vào nghề, tôi thường hỏi:

Làm thế nào để làm kiến trúc này "sạch" hơn?

Ngày nay, tôi hỏi một câu khác:

Bài toán này thật sự cần bao nhiêu kiến trúc?

Clean Architecture dạy tôi cách xây dựng những ranh giới mạnh.

Simplified Clean Architecture dạy tôi khi nào những ranh giới đó xứng đáng với chi phí của chúng.

Mục tiêu không phải là sự thuần khiết về kiến trúc.

Mục tiêu là tạo ra phần mềm có thể bảo trì được với một nhịp độ phát triển bền vững.

Và đôi khi, kiến trúc tốt nhất không phải là kiến trúc tinh vi nhất.

Mà là kiến trúc giúp đội ngũ tiếp tục tiến lên mà không tích lũy thêm sự phức tạp không cần thiết.

Simplified Clean Architecture: Cân Bằng Giữa Kiến Trúc Và Tốc Độ Delivery | Phạm Hoàng Sang