86 lines
3.1 KiB
Markdown
86 lines
3.1 KiB
Markdown
# Architecture Concepts
|
|
|
|
Core architectural principles for Flutter applications.
|
|
|
|
## Separation of Concerns
|
|
|
|
Divide application functionality into distinct, self-contained units. Separate UI logic from business logic.
|
|
|
|
In Flutter:
|
|
- Separate widgets by functionality (authentication vs search logic)
|
|
- Write reusable, lean widgets with minimal logic
|
|
- Keep widgets focused on presentation only
|
|
|
|
## Layered Architecture
|
|
|
|
Organize app into 2-3 distinct layers:
|
|
|
|
### UI Layer
|
|
Displays data to users and handles user interaction. Also called the presentation layer.
|
|
|
|
### Logic Layer (Domain Layer - Optional)
|
|
Implements core business logic, facilitates interaction between data and UI layers. Only needed for apps with complex client-side business logic.
|
|
|
|
### Data Layer
|
|
Manages interactions with data sources (databases, APIs, platform plugins). Exposes data and methods to business logic layer.
|
|
|
|
Lower layers should not depend on upper layers. ViewModels in the UI layer may
|
|
call Repositories directly for simple flows, or go through optional Domain
|
|
Use-cases when business logic is complex, reused, or spans multiple
|
|
Repositories. Views should not call Services directly.
|
|
|
|
## Single Source of Truth (SSOT)
|
|
|
|
Every data type has one authoritative source responsible for representing local or remote state. The SSOT class should be the only class that can modify the data.
|
|
|
|
Benefits:
|
|
- Reduces bugs
|
|
- Simplifies code (one copy of data)
|
|
- Prevents sync issues
|
|
|
|
In Flutter, SSOT is typically held in a **Repository** class in the data layer. One repository per data type.
|
|
|
|
Apply SSOT across layers and within classes:
|
|
- Use getters to derive values from SSOT field
|
|
- Use records to group related values instead of parallel lists
|
|
|
|
## Unidirectional Data Flow (UDF)
|
|
|
|
Decouple state from UI that displays it. State flows from data → logic → UI. User events flow from UI → logic → data.
|
|
|
|
Update cycle:
|
|
1. UI: User interaction triggers event handler
|
|
2. Logic: Logic class calls repository methods
|
|
3. Data: Repository updates data and provides new data
|
|
4. Logic: Logic class saves state, sends to UI
|
|
5. UI: UI displays new state
|
|
|
|
Data can also originate from data layer (e.g., repository polling HTTP server). Key principle: data changes always happen in SSOT (data layer).
|
|
|
|
## UI is a Function of State
|
|
|
|
Flutter is declarative - UI reflects current app state. When state changes, trigger UI rebuild.
|
|
|
|
Key principles:
|
|
- Data drives UI, not the other way
|
|
- Data should be immutable and persistent
|
|
- Views contain minimal logic
|
|
- Minimizes data loss during app closure
|
|
- Improves testability and bug resistance
|
|
|
|
## Extensibility
|
|
|
|
Each architecture piece has well-defined inputs and outputs. Example: ViewModel takes data sources (repositories) as inputs, exposes commands and formatted data for views.
|
|
|
|
Benefits:
|
|
- Swap implementations without changing consuming code
|
|
- Test components in isolation
|
|
- Easy to add new features
|
|
|
|
## Testability
|
|
|
|
Principles making software extensible also make it testable:
|
|
- Test self-contained logic (ViewModel) by mocking Repository
|
|
- Test UI logic separate from Flutter widgets
|
|
- Add new logic/UI with low risk and high flexibility
|