ADR-001: Four-Layer Architecture
The foundational architectural decision that establishes clear separation of concerns and responsibilities across our Flutter applications.
1. History (The Problem Context)
Section titled “1. History (The Problem Context)”The Problem
Section titled “The Problem”Flutter applications often suffer from poor separation of concerns, leading to:
- Business logic scattered across UI components
- Tight coupling between data access and presentation
- Difficulty testing individual components
- Inconsistent project structure across team members
- Hard to maintain and scale applications
Context
Section titled “Context”- When: Establishing architecture standards for Flutter team
- Where: Multiple Flutter projects with varying complexity
- Who: Flutter development team
- Constraints:
- Need consistency across multiple projects
- Must support complex business logic
- Team members with different experience levels
- Real-time features and GraphQL integration required
Previous Approach
Section titled “Previous Approach”Projects had inconsistent architecture with:
- Business logic mixed in widgets and notifiers
- No clear boundaries between concerns
- Difficult to test and maintain code
2. Reason (Architecture Decision Record)
Section titled “2. Reason (Architecture Decision Record)”Decision
Section titled “Decision”Implement a Four-Layer Architecture with clear separation of concerns:
- UI Layer - Presentation state and user interface
- Application Logic Layer (Services) - Business logic and cross-cutting concerns
- Domain Layer - Pure models, interfaces, and domain rules
- Datasource Layer - Data access and external integrations
Alternatives Considered
Section titled “Alternatives Considered”-
No Formal Architecture
- Pros: Fastest initial development
- Cons: Becomes unmaintainable, inconsistent across team
-
Clean Architecture (5+ Layers)
- Pros: Very clear separation, follows established patterns
- Cons: Too complex for Flutter apps, over-engineering
-
Feature-First Architecture
- Pros: Good for small apps, easy to understand
- Cons: Doesn’t scale well, business logic duplication
-
Four-Layer Architecture (Chosen) ✅
- Pros: Clear boundaries, scalable, testable, team-friendly
- Cons: Initial learning curve, requires discipline
Rationale
Section titled “Rationale”The four-layer approach provides:
- Clear Responsibilities: Each layer has a specific purpose
- Testability: Each layer can be tested in isolation
- Scalability: Supports complex business logic and multiple projects
- Team Consistency: Standard structure across all projects
- Maintainability: Easy to locate and modify specific concerns
Amendments
Section titled “Amendments”Datasource Layer: Convertor Pattern (Replaces Strategy)
Section titled “Datasource Layer: Convertor Pattern (Replaces Strategy)”The datasource layer now uses the Convertor pattern exclusively. The previous Strategy/DatasourceModule pattern has been retired. The Convertor pattern provides chainable, composable data pipelines with type-safe transformations. See ADR-005: Convertor Replaces Strategy for the full decision record.
Service Layer: Complex Logic Only
Section titled “Service Layer: Complex Logic Only”The service layer follows a refined rule: services are only required for complex logic. Simple CRUD flows go directly from Notifier to Repository, bypassing the service layer entirely. Services are reserved for orchestration, cross-cutting concerns, multi-repository coordination, and complex business workflows. See ADR-006: Service Layer Refinement for criteria and rationale.
Three-Model Layer Pipeline
Section titled “Three-Model Layer Pipeline”Data flows through three model layers:
- GQL Raw Models - Ferry-generated via
build.yamlcustom type mapping (Dart classes, not built_value) - Domain Models - Clean, repository-processed models using Freezed
- Presentation State - Consumed by notifiers and UI
Repositories are responsible for transforming raw models into domain models using Convertor-based model convertors.