数据层
逐步讲解一个实现 MVVM 架构的应用的数据层。
应用的数据层在 MVVM 术语中称为 model,是所有应用数据的单一数据源。作为单一数据源,应用数据只应在此更新。
它负责从各类外部 API 消费数据、向 UI 暴露数据、处理需要更新数据的 UI 事件,并在需要时向外部 API 发送更新请求。
本指南中的数据层有两个主要组件:repository 与 service。
-
Repository 是应用数据的单一数据源,包含与该数据相关的逻辑,如响应用户事件更新数据或从 service 轮询数据。 Repository 负责在支持离线能力时同步数据、管理重试逻辑与缓存数据。
-
Service 是无状态 Dart 类,与 HTTP 服务器、平台插件等 API 交互。应用所需且非应用代码内创建的数据都应在 service 类中获取。
定义 service
#Service 类是架构组件中最明确的一类:无状态,函数无副作用,唯一职责是封装外部 API。通常每个数据源一个 service 类,如面向客户端的 HTTP 服务器或平台插件。
例如 Compass 应用中有 APIClient
service,处理面向客户端服务器的 CRUD 调用。
class ApiClient {
// Some code omitted for demo purposes.
Future<Result<List<ContinentApiModel>>> getContinents() async { /* ... */ }
Future<Result<List<DestinationApiModel>>> getDestinations() async { /* ... */ }
Future<Result<List<ActivityApiModel>>> getActivityByDestination(String ref) async { /* ... */ }
Future<Result<List<BookingApiModel>>> getBookings() async { /* ... */ }
Future<Result<BookingApiModel>> getBooking(int id) async { /* ... */ }
Future<Result<BookingApiModel>> postBooking(BookingApiModel booking) async { /* ... */ }
Future<Result<void>> deleteBooking(int id) async { /* ... */ }
Future<Result<UserApiModel>> getUser() async { /* ... */ }
}
Service 本身是一个类,每个方法封装不同 API 端点并暴露异步响应对象。延续删除已保存预订的示例,deleteBooking 返回 Future<Result<void>>。
定义 repository
#Repository 的唯一职责是管理应用数据。 Repository 是某一类应用数据的单一数据源,且应是唯一能变更该数据类型的地方。 Repository 负责从外部源轮询新数据、处理重试逻辑、管理缓存数据,并将原始数据转换为领域模型。
应用中每种不同数据类型应有一个独立 Repository。例如 Compass 有 UserRepository、BookingRepository、AuthRepository、DestinationRepository
等。
以下示例来自 Compass 的 BookingRepository,展示 Repository 的基本结构。
class BookingRepositoryRemote implements BookingRepository {
BookingRepositoryRemote({
required ApiClient apiClient,
}) : _apiClient = apiClient;
final ApiClient _apiClient;
List<Destination>? _cachedDestinations;
Future<Result<void>> createBooking(Booking booking) async {...}
Future<Result<Booking>> getBooking(int id) async {...}
Future<Result<List<BookingSummary>>> getBookingsList() async {...}
Future<Result<void>> delete(int id) async {...}
}
BookingRepository 以 ApiClient service 为输入,用于从服务器获取与更新原始数据。
Service 应为私有成员,以免 UI 层绕过 Repository 直接调用 service。
借助 ApiClient,Repository 可轮询服务器上用户已保存预订的更新,并通过 POST 请求删除预订。
Repository 转换为应用模型的原始数据可来自多个源与多个 service,因此 repository 与 service 为多对多关系:一个 service 可被任意数量 repository 使用,一个 repository 也可使用多个 service。
领域模型 (Domain Model)
#
BookingRepository 输出 Booking 与 BookingSummary 等 领域模型。所有 Repository 都输出对应的领域模型。这些数据模型与 API 模型的区别在于仅包含应用其余部分所需数据;
API 模型含常需过滤、合并或删除才有用的原始数据,Repository 精炼后以领域模型输出。
在示例应用中,领域模型通过 BookingRepository.getBooking 等方法返回值暴露。
getBooking 从 ApiClient 获取原始数据并转换为 Booking,通过合并多个 service 端点数据实现。
// This method was edited for brevity.
Future<Result<Booking>> getBooking(int id) async {
try {
// Get the booking by ID from server.
final resultBooking = await _apiClient.getBooking(id);
if (resultBooking is Error<BookingApiModel>) {
return Result.error(resultBooking.error);
}
final booking = resultBooking.asOk.value;
final destination = _apiClient.getDestination(booking.destinationRef);
final activities = _apiClient.getActivitiesForBooking(
booking.activitiesRef);
return Result.ok(
Booking(
startDate: booking.startDate,
endDate: booking.endDate,
destination: destination,
activity: activities,
),
);
} on Exception catch (e) {
return Result.error(e);
}
}
完成事件循环
#
本页你已看到用户如何删除已保存预订:从在 Dismissible 上滑动的事件开始,
view model 将实际数据变更委托给 BookingRepository。以下片段展示 BookingRepository.deleteBooking 方法。
Future<Result<void>> delete(int id) async {
try {
return _apiClient.deleteBooking(id);
} on Exception catch (e) {
return Result.error(e);
}
}
Repository 通过 _apiClient.deleteBooking 向 API 客户端发送 POST 请求并返回 Result。
HomeViewModel 消费 Result 及其数据,最终调用 notifyListeners,完成循环。
反馈
#网站本节内容仍在完善中, 欢迎提供反馈!
除非另有说明,本文档之所提及适用于 Flutter 3.44.0 版本。本页面最后更新时间:2026-06-13。查看文档源码 或者 为本页面内容提出建议。