architecture

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Technology Stack

技术栈

  • Flutter for cross-platform development
  • Dart as the primary programming language
  • bloc for state management
  • injectable for dependency injection
  • Dart Mappable for immutable data models
  • Dio for HTTP networking
  • isar for local database
  • Firebase for backend services
  • Flutter用于跨平台开发
  • Dart作为主要编程语言
  • bloc用于状态管理
  • injectable用于依赖注入
  • Dart Mappable用于实现不可变数据模型
  • Dio用于HTTP网络请求
  • isar用于本地数据库
  • Firebase提供后端服务

Clean Architecture

整洁架构

  • Domain Purity: The
    domain
    layer must be pure Dart. NO
    package:flutter
    imports.
  • Layer Dependency:
    Presentation -> Domain <- Data
    . Data layer implements Domain interfaces.
  • Feature-First 2.0: Enforce strict separation of
    DataSources
    (External/Raw) vs
    Repositories
    (Domain abstraction).
  • 领域层纯净性
    domain
    层必须为纯Dart代码,禁止导入
    package:flutter
  • 层依赖规则
    表示层 -> 领域层 <- 数据层
    ,数据层实现领域层定义的接口。
  • Feature-First 2.0:严格区分
    数据源(DataSources,外部/原始数据)
    仓库(Repositories,领域层抽象)

Directory Structure

目录结构

  • Organize code according to feature-based Clean Architecture pattern (
    featureA/bloc
    ,
    featureA/models
    ,
    featureA/views
    )
  • Place cross-feature components in the
    core
    directory
  • Group shared widgets in
    core/views/widgets
    by type
  • 按照基于特性的整洁架构模式组织代码(如
    featureA/bloc
    featureA/models
    featureA/views
  • 跨特性通用组件放置在
    core
    目录下
  • 共享Widget按类型归类到
    core/views/widgets
    目录

Three-Layer Data Model Pattern

三层数据模型模式

  1. API Layer (ItemApiModel)
    • Represents the raw data structure as received from the server
    • Contains all fields provided by the API
    • Should match the API contract exactly
  2. Domain Layer (Item)
    • Represents the internal data model
    • Contains only the fields necessary for business logic
    • Strips out unnecessary API fields
  3. UI Layer (ItemUiState)
    • Represents the data model optimized for UI rendering
    • Contains parsed and formatted data ready for display
    • Handles all UI-specific transformations
  1. API层(ItemApiModel)
    • 表示从服务端获取的原始数据结构
    • 包含API返回的所有字段
    • 必须与API契约完全一致
  2. 领域层(Item)
    • 表示内部业务数据模型
    • 仅包含业务逻辑所需的字段
    • 剔除API返回的不必要字段
  3. UI层(ItemUiState)
    • 表示为UI渲染优化的数据模型
    • 包含已解析格式化、可直接展示的数据
    • 处理所有UI专属的格式转换逻辑

Data Model Rules

数据模型规则

  • Use Dart Mappable for defining immutable UI states
  • Each layer should have its own type definition
  • The UI layer should use the UI state data models, never directly the domain model or the API model
  • The UI state model should be derived from the domain model, not the API model
  • The domain model should be derived from the API model, not the UI state model
  • 使用Dart Mappable定义不可变UI状态
  • 每一层都应有独立的类型定义
  • UI层必须使用UI状态数据模型,禁止直接使用领域模型或API模型
  • UI状态模型必须从领域模型派生,不能直接从API模型派生
  • 领域模型必须从API模型派生,不能从UI状态模型派生

Repository & DataSource Pattern

仓库与数据源模式

  • Repositories orchestrate between data sources (return Domain Model)
  • Data sources access raw data (return API Model)
  • The repository should be the source of truth, and it returns the domain model
  • DataSources MUST only contain raw SDK/API calls. No mapping or business logic.
  • No direct backend SDK/API calls outside DataSources
  • 仓库负责协调多个数据源,返回领域模型
  • 数据源负责访问原始数据,返回API模型
  • 仓库作为唯一可信数据源,返回领域模型
  • 数据源必须仅包含原始SDK/API调用,不能包含映射逻辑或业务逻辑
  • 禁止在数据源之外直接调用后端SDK/API

Data Flow

数据流

UI Event → BLoC (emit Loading) → Repository → DataSource (API/SDK)
Response → Repository (map to Domain Entity) → BLoC (emit Success/Error) → UI
UI Event → BLoC (emit Loading) → Repository → DataSource (API/SDK)
Response → Repository (map to Domain Entity) → BLoC (emit Success/Error) → UI

Dart 3 Language Features

Dart 3 语言特性

  • Sealed Classes: Use
    sealed class
    for domain failures to enable exhaustive pattern matching across layers.
  • Records: Use records for lightweight multi-value returns where defining a full class is overkill (e.g.,
    (String name, int age)
    ).
  • If-Case Pattern: Prefer
    if (value case final v?)
    over
    if (value != null)
    for null checking with binding.
  • Class Modifiers: Use
    final
    ,
    interface
    ,
    base
    , and
    sealed
    class modifiers to express API intent.
  • 密封类(Sealed Classes):使用
    sealed class
    定义领域异常,实现各层的穷尽模式匹配。
  • 记录(Records):对于不需要定义完整类的轻量级多值返回场景使用记录(例如
    (String name, int age)
    )。
  • If-Case 模式:空值检查优先使用
    if (value case final v?)
    而非
    if (value != null)
    ,同时完成变量绑定。
  • 类修饰符:使用
    final
    interface
    base
    sealed
    类修饰符明确表达API设计意图。

Error Handling

错误处理

  • Functional Error Handling: Use
    Either<Failure, T>
    or
    Result<T>
    sealed classes. NEVER throw exceptions across layer boundaries.
  • Pattern Matching: Exhaustively handle all sealed class states using Dart 3.x
    switch
    expressions in UI and BLoCs.
  • Throw errors when needed, and catch them at appropriate boundaries
  • Log errors with context
  • Present user-friendly error messages in the UI
  • Avoid silent failures; always handle or propagate errors
  • 函数式错误处理:使用
    Either<Failure, T>
    Result<T>
    密封类,绝对禁止跨层边界抛出异常。
  • 模式匹配:在UI和BLoC中使用Dart 3.x的
    switch
    表达式穷尽处理所有密封类状态。
  • 必要时抛出错误,并在合适的边界捕获
  • 记录错误日志并附带上下文信息
  • 在UI中展示用户友好的错误提示
  • 避免静默失败,始终处理或向上传播错误

Platform Channels & Native Integration

平台通道与原生集成

  • Use
    MethodChannel
    for one-off calls to native code (e.g., reading device info, triggering native APIs)
  • Use
    EventChannel
    for continuous streams from native to Flutter (e.g., sensor data, connectivity changes)
  • Place all channel code in a dedicated
    platform/
    directory within the relevant feature
  • Define channel names as constants:
    static const channel = MethodChannel('com.app.feature/method')
  • Wrap all channel calls in a DataSource — never call
    MethodChannel
    directly from BLoC or UI
  • Handle
    MissingPluginException
    gracefully for platforms that don't implement the channel
  • Use
    defaultTargetPlatform
    checks to guard platform-specific behavior
  • 单次调用原生代码使用
    MethodChannel
    (例如读取设备信息、调用原生API)
  • 原生向Flutter持续发送数据流使用
    EventChannel
    (例如传感器数据、网络连接状态变化)
  • 所有通道代码放置在对应特性下的专属
    platform/
    目录中
  • 将通道名称定义为常量:
    static const channel = MethodChannel('com.app.feature/method')
  • 所有通道调用都要封装在数据源中,禁止从BLoC或UI层直接调用
    MethodChannel
  • 对于未实现对应通道的平台,优雅处理
    MissingPluginException
  • 使用
    defaultTargetPlatform
    判断来管控平台专属行为

FFI (Foreign Function Interface)

FFI(外部函数接口)

  • Use
    dart:ffi
    for performance-critical native C/C++ code
  • Define bindings in a separate class with clear documentation
  • Prefer Federated Plugins when sharing native code across multiple packages
  • 性能敏感的原生C/C++代码调用使用
    dart:ffi
  • 在独立类中定义绑定并添加清晰的文档说明
  • 跨多包共享原生代码时优先使用联邦插件(Federated Plugins)

Platform-Specific Code

平台专属代码

  • Use
    Platform.isAndroid
    /
    Platform.isIOS
    for runtime checks (import
    dart:io
    )
  • For web, use
    kIsWeb
    from
    package:flutter/foundation.dart
  • Prefer adaptive widgets (
    Switch.adaptive
    ,
    Slider.adaptive
    ) over manual platform checks where possible
  • 运行时平台判断使用
    Platform.isAndroid
    /
    Platform.isIOS
    (需导入
    dart:io
  • Web端判断使用
    package:flutter/foundation.dart
    提供的
    kIsWeb
  • 优先使用自适应Widget(
    Switch.adaptive
    Slider.adaptive
    ),而非手动编写平台判断逻辑