Skip to content

ADR-001: Four-Layer Architecture

Four-Layer Architecture Foundation

The foundational architectural decision that establishes clear separation of concerns and responsibilities across our Flutter applications.

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
  • 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

Projects had inconsistent architecture with:

  • Business logic mixed in widgets and notifiers
  • No clear boundaries between concerns
  • Difficult to test and maintain code

Implement a Four-Layer Architecture with clear separation of concerns:

  1. UI Layer - Presentation state and user interface
  2. Application Logic Layer (Services) - Business logic and cross-cutting concerns
  3. Domain Layer - Pure models, interfaces, and domain rules
  4. Datasource Layer - Data access and external integrations
  1. No Formal Architecture

    • Pros: Fastest initial development
    • Cons: Becomes unmaintainable, inconsistent across team
  2. Clean Architecture (5+ Layers)

    • Pros: Very clear separation, follows established patterns
    • Cons: Too complex for Flutter apps, over-engineering
  3. Feature-First Architecture

    • Pros: Good for small apps, easy to understand
    • Cons: Doesn’t scale well, business logic duplication
  4. Four-Layer Architecture (Chosen)

    • Pros: Clear boundaries, scalable, testable, team-friendly
    • Cons: Initial learning curve, requires discipline

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

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.

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.

Data flows through three model layers:

  1. GQL Raw Models - Ferry-generated via build.yaml custom type mapping (Dart classes, not built_value)
  2. Domain Models - Clean, repository-processed models using Freezed
  3. Presentation State - Consumed by notifiers and UI

Repositories are responsible for transforming raw models into domain models using Convertor-based model convertors.