Refactor β
this concept is Combination pattern style between KauΓͺ and Arktekko thats i applied on open source project by mani53-dev. For a detailed explanation, check out the Medium article: Implementing Clean Architecture with GetX and MVVM in Flutter. write by the owner of code starterpack thats i modified.
Purpose & Why β
The goals make the project code is testability, more clean, and readable easy scaleable and maintained.
π΅πΌββοΈ Before After π‘
BEFORE
Clean Architecture using GetX and MVVM, adhering to SOLID principles. project follows a structured approach to building scalable Flutter applications with GetX for state management and dependency injection. It implements Clean Architecture to separate concerns and improve maintainability.
- GetX for State Management
- MVVM Architecture
- Dio for API Calls
- Interceptor for API Authorization
- Caching with DioCacheInterceptor
- Shared Preferences for Local Storage
- SOLID Principles Implementation
π Project Structure (BEFORE Modified)
lib/
β
βββ common/ # Shared utilities, themes, constants
β
βββ app/
β βββ exceptions/ # Custom exception handling
β βββ global_widgets/ # Reusable UI components
β βββ middlewares/ # Route guards and middlewares
β βββ models/ # Entity definitions (business models)
β βββ modules/ # Feature-based segregation
β β βββ auth/
β β β βββ bindings/ # Dependency bindings
β β β βββ controllers/ # State management using GetX
β β β βββ services/ # Business logic and use cases
β β β βββ views/ # UI components
β β β βββ widgets/ # Reusable widgets on module level
β β βββ dashboard/
β β βββ root/ # Root navigation & app entry point
β βββ providers/ # API services and data sources
β βββ routes/ # Navigation and route management
β βββ services/ # Global services (e.g., Auth, Storage)
β
βββ main.dart # Application entry point
AFTER
Clean Architecture with GetX (DDD-style), adhering strictly to SOLID principles and testability. A Flutter project implementing Clean Architecture with GetX, following SOLID and Onion principles (Domain-Driven), with clear separation of Data, Domain, and Presentation layers.
π Project Structure (AFTER Modified)
lib/
β
βββ common/ # Shared utilities, themes, constants (colors, styles, helpers)
β
βββ app/
β βββ exceptions/ # Custom exception classes (e.g., AuthException, NetworkException)
β βββ global_widgets/ # Reusable UI widgets used across modules (e.g., Button, TextField)
β βββ middlewares/ # GetX middlewares (auth guard, onboarding guard, etc.)
β βββ models/ # Pure business models/entities (domain-level extends, yes/no JSON here depends on requirement, mapping)
β βββ modules/ # Feature-based separation (DDD style)
β β βββ auth/ # Authentication module
β β β βββ bindings/ # Dependency Injection for controllers & usecases
β β β βββ data/ # Data layer (API, DB, RepositoryImpl, DTOs)
β β β β βββ datasources/ # Remote/local sources (e.g., AuthRemoteDataSource)
β β β β βββ dto/ # Data Transfer Objects (login_result_dto.dart)
β β β β βββ payload/ # Request payloads (login_payload.dart)
β β β β βββ repositories/ # Repository implementations
β β β βββ domain/ # Domain layer (rules, contracts)
β β β β βββ entities/ # Business entities (User, no JSON here)
β β β β βββ repositories/ # Repository interfaces (AuthRepository)
β β β β βββ usecase/ # Use cases (LoginUseCase, RegisterUseCase)
β β β βββ presentation/ # Presentation layer
β β β β βββ controllers/ # GetX Controllers (LoginController, RegisterController)
β β β β βββ views/ # UI screens (LoginView, RegisterView)
β β β β βββ widgets/ # Module-specific reusable widgets (LoginForm, RegisterForm)
β β β βββ services/ # Local business services (optional helpers, e.g., AggregateService)
β β β βββ controllers/ # GetX Controllers (LoginController, RegisterController)
β β β βββ views/ # UI screens (LoginView, RegisterView)
β β βββ dashboard/ # Example: Dashboard module with same structure
β β βββ root/ # Root module (main navigation, splash, app entry)
β β
β βββ providers/ # Global API clients, interceptors, and HTTP providers
β βββ routes/ # App routes & navigation configuration
β βββ services/ # App-wide services (LocalStorageService, UserService, NotificationService, CrashReportingService)
β
βββ main.dart # Application entry point (init DI, runApp, setup services)
β
integration_test/ # Full app flows (needs device/emulator)
β βββ login_flow_test.dart # E2E test for login journey
test/ # Unit & widget tests (run in memory, fast)
β
βββ unit/ # Business logic only (no UI, no Flutter engine)
β βββ login_usecase_test.dart # Tests domain logic of login usecase
β
βββ widget/ # UI components in isolation (with fake dependencies)
βββ login_view_test.dart # Tests rendering & interactions of login view
π¦ Comparison Overview (Testability)
Folder | Runs On | Scope | Speed | Example |
---|---|---|---|---|
integration_test/ | Emulator/Device | Full app flows | Slow | Login end-to-end |
test/unit/ | Dart VM | Single class/function | Fast | UseCase logic |
test/widget/ | Dart VM | Widget in isolation | Medium | LoginView UI rendering |
π Alur Data (contoh login flow)
[ UI Layer ] [ Domain Layer ] [ Data Layer ] [ External ]
βββββββββββββββββ ββββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββ
β LoginView β β LoginUseCase β β AuthRepository β β API / Storage β
β (presentation)βββββββββΆβ (business logic) βββββββββββΆβ Impl βββββββββΆβ (Dio / DB) β
βββββββββββββββββ ββββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββ
β² β² β²
β β β
β β β
βββββββββββββββββ ββββββββββββββββββββ βββββββββββββββββββ
β LoginControllerβ β AuthRepository β β DataSources β
β (GetX State) βββββββββΆβ (interface) βββββββββββΆβ Remote/Local β
βββββββββββββββββ ββββββββββββββββββββ βββββββββββββββββββ
π Penjelasan Flow
-
User input di UI
- User isi email & password di
LoginView
. LoginController
(state management) handle event tombol login.
- User isi email & password di
-
Controller memanggil UseCase
controller.login()
β panggilLoginUseCase.execute(LoginPayload)
.
-
UseCase panggil Repository Interface
LoginUseCase
butuh kontrak dariAuthRepository
.- Interface ini ada di domain/repositories/.
-
Repository Impl (Data Layer)
AuthRepositoryImpl
(didata/repositories/
) implement interface tadi.- Dia yang menentukan data diambil dari RemoteDataSource (API) atau LocalDataSource (cache).
-
Datasource
AuthRemoteDataSource
β call API pakaiDioProvider
.AuthLocalDataSource
β simpan ambil token diSharedPreferences/Hive
.
-
Balik ke atas
- Response API β diubah ke DTO β di-mapping ke Entity.
- Entity dikirim ke UseCase β Controller β update
Rx<User>
β UI otomatis refresh (Obx
).
π§© Layer Responsibility
- UI (Presentation) = Tampilan & interaksi user.
- Controller (GetX) = State & event handler.
- Domain (Entities & UseCase) = Business logic murni.
- Repository Interface = Kontrak data.
- Repository Impl = Bridge antara Domain β Data.
- DataSource = Tempat asli ambil data (API, DB, cache).
flowchart TD
A[LoginView<br>(UI Layer)] --> B[LoginController<br>(GetX State)]
B --> C[LoginUseCase<br>(Domain)]
C --> D[AuthRepository<br>(Interface)]
D --> E[AuthRepositoryImpl<br>(Data Layer)]
E --> F1[AuthRemoteDataSource<br>(API)]
E --> F2[AuthLocalDataSource<br>(Storage)]
F1 --> G[External API<br>(Dio/HTTP)]
F2 --> H[Local Storage<br>(SharedPref/Hive)]
%% Return flow
G --> F1
H --> F2
F1 --> E
F2 --> E
E --> D
D --> C
C --> B
B --> A
Thanks for being here π Stay sharp, ship clean code, and donβt forget to take a break.
β just for eat developer π½οΈ π¨βπ³ π
Iβll be publishing an open-source project soon! Iβll also clean up the old code and put it in the navigation menu under Open Source Projects. Stay tune https://github.com/yogithesymbian
Source Also From
π Referensi
- Uncle Bob-Clean Architecture (2012) β Entities ada di paling dalam, paling stabil, bebas framework.
- ResoCoder-Flutter TDD Clean Architecture Course β implementasi di Flutter yang populer.
- Very Good Ventures (VGV)-Flutter Best Practices β banyak dipakai buat production apps.
- Stacked architecture (FilledStacks) β agak mirip tapi lebih pragmatis.
- from www.yogiveloper.com
- old blog https://scodeid.blogspot.com/