在构建复杂的应用程序时,选择正确的应用程序架构是至关重要的,因为它可以使你的代码结构化,并在你的代码库增长时支持你的代码库。
好的架构应该能帮助你处理复杂的问题,而不至于妨碍到你。但要做好它并不容易。
- 缺少架构会使你的代码整体上缺乏组织性
- 过度使用架构会导致过度工程化,使得即使是简单的修改也很难进行。
在实践中,事情可能是相当微妙的,需要一些实践和经验来获得正确的平衡。
所以在这篇文章中,我将介绍一个基于Riverpod包的参考架构,它非常适用于Flutter应用开发。
而且我还会提供一个与其他流行架构的比较,因为我从他们那里借鉴了很多概念和想法。
现有架构概述
如果你研究这个话题,你可能会遇到诸如MVC、MVP、MVVM和Clean Architecture等术语。这些都是流行的应用程序架构,很早以前就被引入,以解决与我们今天使用Flutter所面临的类似问题。
严格来说,MVC、MVP和MVVM是设计模式,而清洁架构定义了一套规则和原则,帮助你架构任何复杂的软件系统。
虽然它们所依据的原则在今天仍然非常重要,但它们并不是为Flutter应用开发量身定做的。
尽管如此,仍有许多人试图将它们引入Flutter世界,并取得了不同程度的成功。
流行的 Flutter 应用程序架构。块状和堆叠式
最值得注意的是,在 Bloc 架构已经获得了一些很好的采用,这主要是由于 块状库,它被各种大公司所使用。它非常有主见,给我们提供了一套严格的规则,让我们如何构建我们的Flutter应用程序(这是件好事!)。
另一个有前途的是Stacked Architecture,它是基于 堆叠式包,其灵感主要来自于MVVM。
它们都依赖于 供应商,这是Flutter官方文档中推荐的用于状态管理的包。
虽然Bloc和Stacked都没有什么问题,但Provider的作者创建这个 河豚(Riverpod包是为了 "做出一些改进,否则就不可能"(用他自己的话说)。
随着时间的推移,我开始欣赏Riverpod作为依赖性注入和状态管理的完整解决方案的力量。所以我决定在它上面建立我自己的架构。👇
我对Flutter应用架构的看法(使用Riverpod)
在构建不同复杂度的Flutter应用时,我对应用架构进行了大量的实验,并对什么是好的,什么是不好的有了深刻的理解。
其结果是我在所有最新的项目中使用了一个参考架构。
由于缺乏一个更好的名字,我称之为Riverpod架构--不过请记住,这只是我对它的看法,而不是Remi Rousselet(Riverpod的作者)认可的 "官方 "架构。
这个架构由四层组成**(数据**、领域、应用、展示)。
这里有一个预览。
使用数据、领域、应用和表现层的Flutter应用架构。箭头显示各层之间的依赖关系
这些层中的每一层都有自己的责任,并且有一个明确的合同,规定了跨边界的通信方式。
在上图中,箭头表示各层之间的依赖关系。
如果您对细节感兴趣,这里有一系列的文章,分别介绍每个单独的层。
但现在,我只想提供一个与其他架构的有益比较。
与清洁架构的比较
清洁架构是由Robert C. Martin提出的,用这个图表示。
清洁架构。资料来源及出处:清洁代码博客
虽然事物的名称不同,但我们可以确定一些类似的概念。
- 实体 → 模型
- 用例 → 服务
由于Clean Architecture的设计是独立于UI的,所以它没有考虑到数据源到UI的单向流动,而这是许多现代应用架构(Flutter和Web都一样)的核心设计原则。
与清洁架构的比较
相反,它使用了一个包含控制器、网关和演示器的接口适配器层。而在Riverpod架构中,存储库属于数据层,控制器属于表现层。
同样地,被称为框架和驱动的外层包含了UI和DB。但在Riverpod架构中,这些都是处于两端。
这并不是说Clean Architecture不好。但我发现它增加了一些精神上的开销,我宁愿使用一个更接近于我日常用于Flutter应用开发的概念模型。
与MVC的比较
模型-视图-控制器(MVC)是一种由3种类型的组件组成的软件设计模式。
- 模型:直接管理应用程序的数据、逻辑和规则
- 视图。UI组件(小部件)
- 控制器。接受输入并将其转换为模型或视图的命令
MVC架构
MVC通常会给模型带来过多的逻辑负担,因此通常会增加一个额外的服务层:MVC+S。
模型应该是反应式的(例如,使用ChangeNotifier
),这样,当它发生变化时,小部件就会重新构建。
而用户的输入总是通过控制器,而不是直接通过模型。
与Riverpod架构相比,vanilla MVP在各层之间的通信方式上有很大不同。
与MVC架构的比较
与MVVM的比较
模型-视图-视图模型(MVVM)模式的引入是为了将应用程序的业务和表现逻辑与用户界面(UI)分开。
它通常是这样表示的。
MVVM架构
当使用MVVM时,视图不能直接访问模型。
相反,视图模型处理用户的输入,并将数据从模型中转换出来,这样就可以很容易地呈现出来。
视图模型通过使用观察者设计模式的数据绑定连接到视图。
当从后端返回的数据不能很好地映射到你需要展示的用户界面时,拥有一个中间视图模型就特别有用。
与MVVM架构的比较
在这方面,很容易将MVVM与Riverpod架构进行比较。
- 视图→小工具
- 视图模型→控制器
- 模型→模型+存储库+数据源
MVVM只是一种设计模式,上图中并不包括任何数据的后期概念。
在实践中,服务和存储库被添加到封装数据访问和缓存中,使得MVVM与Riverpod架构相当相似。
与Bloc架构的比较
Bloc架构页面包含这个定义了三层的图。
- 表现形式
- 业务逻辑
- 数据(存储库和数据提供者)
资料来源及出处:Bloc架构
这与这里介绍的Riverpod架构是一致的。
- 表现(UI) → 小部件
- 业务逻辑(Bloc)→控制器和服务
- 数据(存储库)→存储库
- 数据(数据提供者)→数据源
主要的区别是Bloc可以依赖其他Bloc以对其状态变化做出反应,而Riverpod架构在控制器(管理小部件状态)和服务(管理应用程序状态)之间有更明确的区别。
注意:当使用Bloc时,状态变化总是被表示为数据流。但是Riverpod使得观察不同种类的数据成为可能(数据流、期货、StateNotifiers、ChangeNotifiers等)。
总的来说,我发现Bloc架构是最接近这里提出的方法的一种。
与Stacked架构的比较
Stacked架构(基于Stacked包)与MVVM相似(但不完全相同),由3个主要部分组成,很容易映射到Riverpod架构。
- 视图→小部件
- ViewModel → Controller
- 服务→服务
它还定义了一些关于你能做什么和不能做什么的规则(更多信息在这里)。
Stacked提供了一个基础架构和大量有用的部件(如ViewModelBuilder
),你可以用它来 "绑定 "ViewModel和View,使你的生活更容易,这样你就不必重新发明轮子了。
ViewModels本身是扩展了ChangeNotifier
的Dart类,它们有反应性和非反应性的变体。
主要的区别是Stacked是基于Provider的,而Riverpod架构使用Riverpod(显然😅)。
但正如我们所说,Riverpod给了我们建立一个强大的架构所需要的一切。
与Android应用程序架构的比较
我到现在还没有提到这一点,但Android文档中也有一个有用的应用架构指南,与我使用的架构非常相似。
安卓应用架构
虽然这个架构参考了许多常见的Android组件(如活动、片段等),但这些组件与Flutter世界中的widget相同,而且各层之间也有密切的匹配。
与安卓应用架构的比较
在Android应用架构中,UI层和数据层总是必需的,而领域层是可选的。这是因为并不是所有的应用程序都有复杂的业务逻辑,或需要被多个视图模型重用的简单业务逻辑。同样,在Riverpod架构中,如果不需要应用层,省略它也是合理的。
总的来说,我鼓励你阅读整个Android指南,因为它详细地描述了每一层的作用。
其他流行的架构
应用程序架构是基于使用设计模式实现的抽象,而不是API。因此,你可以在其他流行的状态管理包(如redux、MobX等)之上创建自己的应用架构。
你甚至可以使用Flutter开箱即给你的东西(InheritedWidget、ChangeNotifier、ValueListenableBuilder和朋友们)。
归根结底,最重要的是你在你的应用程序中定义了清晰的契约和边界。
在这方面,我非常喜欢eBay工程博客中的这句话。
我们相信,选择正确的工具或应用单一的设计模式,远不如在应用程序的独特功能和组件之间建立明确的契约和界限重要。如果没有界限,就很容易写出无法维护的代码,而这些代码又不能随着应用的复杂程度的增加而扩展。
总结
正如我们所看到的,Riverpod架构与其他流行的架构有许多共同之处(也有一些不同之处)。
时至今日,我已经非常乐意在各种Flutter应用中使用它,我还写了一系列的文章,更详细地介绍它。