数据层
应用程序的数据层,在 MVVM 术语中称为模型,是所有应用程序数据的真相来源。作为真相来源,它是唯一应该更新应用程序数据的地方。
它负责从各种外部 API 获取数据,将这些数据暴露给 UI,处理 UI 中需要更新数据的事件,并在需要时向这些外部 API 发送更新请求。
- 存储库是应用程序数据的真相来源,并包含与该数据相关的逻辑,例如响应新的用户事件更新数据或轮询服务的 数据。存储库负责在支持离线功能时同步数据、管理重试逻辑和缓存数据。
- 服务是与 API(如 HTTP 服务器和平台插件)交互的无状态 Dart 类。应用程序所需的任何不在应用程序代码本身内部创建的数据都应该从服务类中获取。
定义服务
#服务类是所有架构组件中最明确的。它是无状态的,其函数没有副作用。它的唯一工作是包装外部 API。通常每个数据源(例如客户端 HTTP 服务器或平台插件)都有一个服务类。
例如,在 Compass 应用程序中,有一个 APIClient
服务来处理对面向客户端的服务器的 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 { /* ... */ }
}
服务本身是一个类,其中每个方法都包装了一个不同的 API 端点并公开了异步响应对象。继续前面删除已保存预订的示例,deleteBooking
方法返回一个 Future<Result<void>>
。
定义存储库
#存储库的唯一职责是管理应用程序数据。存储库是单个类型应用程序数据的真相来源,并且应该是唯一更改该数据类型的地方。存储库负责轮询来自外部来源的新数据、处理重试逻辑、管理缓存数据以及将原始数据转换为领域模型。
您应该为应用程序中的每种不同类型的数据创建一个单独的存储库。例如,Compass 应用程序具有名为 UserRepository
、BookingRepository
、AuthRepository
、DestinationRepository
等的存储库。
以下示例是 Compass 应用程序中的 BookingRepository
,并展示了存储库的基本结构。
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
服务作为输入,它使用该服务从服务器获取和更新原始数据。服务是一个私有成员非常重要,这样 UI 层就不能绕过存储库并直接调用服务。
使用 ApiClient
服务,存储库可以轮询服务器上可能发生的用户的已保存预订的更新,并发出 POST
请求以删除已保存的预订。
存储库转换为应用程序模型的原始数据可以来自多个来源和多个服务,因此存储库和服务之间存在多对多的关系。任何数量的存储库都可以使用一个服务,并且一个存储库可以使用多个服务。
域模型
#BookingRepository
输出 Booking
和 BookingSummary
对象,它们是领域模型。所有存储库都输出相应的领域模型。这些数据模型与 API 模型的不同之处在于,它们仅包含应用程序其余部分所需的数据。API 模型包含通常需要过滤、组合或删除才能对应用程序的 UI 有用的原始数据。存储库会优化原始数据并将其输出为领域模型。
在示例应用程序中,领域模型通过 BookingRepository.getBooking
等方法的返回值公开。getBooking
方法负责从 ApiClient
服务获取原始数据,并将其转换为 Booking
对象。它通过组合来自多个服务端点的 数据来实现这一点。
// 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
小部件上滑动。视图模型通过将实际数据变异委托给 BookingRepository
来处理该事件。以下代码段显示了 BookingRepository.deleteBooking
方法。
Future<Result<void>> delete(int id) async {
try {
return _apiClient.deleteBooking(id);
} on Exception catch (e) {
return Result.error(e);
}
}
存储库使用 _apiClient.deleteBooking
方法向 API 客户端发送 POST
请求,并返回一个 Result
。HomeViewModel
使用 Result
及其包含的数据,并最终调用 notifyListeners
,完成循环。
反馈
#由于本网站的这一部分正在不断发展,我们 欢迎您的反馈!
除非另有说明,否则本网站上的文档反映了 Flutter 的最新稳定版本。页面上次更新于 2024-12-04。 查看源代码 或 报告问题。