选择风格(Choosing a style)
我们将依照Google在《应用架构指南》中推荐的最佳实践和架构指南来构建OrderNow的架构。
这些定义包括通过各层定义组件的一些Clean Architecture原则。
层次的定义(Definition of the layers)
在应用程序中,我们将定义以下主要层次:
• 用户界面(UI)层
• 领域层
• 数据层
UI层(UI Layer)
这一层组合了UI元素,Views视图(composable functions可组合函数),ViewModels和表示层的实用程序,例如格式应用器和动画。 设计这一层的注意事项包括:
• 对于状态处理,遵循设计原则中描述的原则。
• 对于每个屏幕,将实现相应的ViewModel。
•viewmodels也将作为状态持有者,也就是状态管理器。
•导航逻辑将委托给视图,并将依赖于APP的状态。
•副作用应该报告给ViewModel。
•当配置发生变化时,Views模型应该保持它们的状态。
•鼓励使用stateless Views(无状态视图)。
领域层(Domain Layer)
尽管这一层可以是可选的,但我建议包含它,以保持与 Clean Architecture 规定的责任划分一致的设计。
这一层组合了被称为 UseCases 的组件,这些组件将管理业务逻辑和所有可由 ViewModels 重用的逻辑。
这一层还作为 UI 层(UI Layer)和数据层(Data Layer)之间的桥梁。
Models 类型的组件也属于这一层。
这些组件对表示层或领域层使用的实体或数据结构进行建模。例如,在 OrderNow 中,“产品”和“类别”都表示模型或实体。
UseCases (用例)是一种设计模式,用于定义特定的业务逻辑或应用程序功能。它们用于封装特定的应用程序操作,通常涉及从数据层获取数据、处理数据以及将结果传递给UI层。在使用清洁架构时,UseCases 通常在领域层中定义并由UI层调用。
关于这一层的设计考虑包括:
• 所有在视图中重复的展示逻辑都可以放在UseCases (用例)中。
• 属于此层的组件可以是无状态的;它们是不需要临时持久化的组件。
• UseCases (用例)执行的操作必须是主线程安全的。
• UseCases 可以相互通信,以协调用例操作。 • 每个 UseCases 负责一个且只有一个操作。
• 每个 UseCases 可能会使用一个或多个仓库(Repositories)。
数据层(Data Layer)
这一层组织了名为仓库(Repositories)的组件,它们协调和封装了与本地和远程数据源的集成逻辑。如其名称所示,它们遵循仓库模式。 这一层的其他组件包括数据源(Datasources)、映射器(Mappers)和数据传输对象(DTOs)。
• 数据源(Datasource):包含到外部或本地持久化源的逻辑集成。
• 数据传输对象(DTO):它是模型持久化实体的结构。包含持久性机制使用的定义。为了使其他层(UI和领域)不继承这些定义,这些类型的实体通过映射器转换为应用程序域的模型。
映射器:它们将 DTO 转换为领域层的模型实体。对于此层的设计,建议考虑以下事项:
• 此层可用作事实来源。
• 存储库执行的操作必须是主安全的。
• 为每个主要实体类型定义一个存储库,例如,产品存储库、类别存储库。
通用架构(General architecture)
不同集成层的通用图类似如下:
Architecture Layers 架构层 :
关于其他层
补充架构主要层次的其他辅助层将包括:
• Main(主层):包含应用程序的基础构件,例如 MainActivity、Application 和 ApplicationState 等等。
• Common(通用层):包含跨应用程序的构件,例如导航定义、用于其他层的实用工具和依赖管理器等等。
关于 PortsClean 架构的使用
建议在不同层的边界之间包括端口,这种技术允许反转控制,解耦在每层边界之间通信的组件。这种方法将为设计增加更好的可维护性和适应性。我们的示例应用程序 (OrderNow) 将在Domain Layer 和Data layer之间添加端口。
组织目录(Organizing directories)
在我们的OrderNow示例中,为了简单起见,层次将通过单个模块中的目录以单体方式组织。 我将把是否在后续的项目中决定分离各层并为每个层献出一个模块的决定留给读者自行判断。
通过两个定义来进行目录组织:
• 在UI层,将使用按功能组织。
• 在Domain Layer(领域层)和Data Layer(数据层),将使用按组件组织。
元素的命名和规范
对于组件命名,我们将使用以下规则:
使用后缀 只有在以下情况下才会在组件名中使用后缀:
• 包含包的名称没有推断出其类型。
• 需要强制组件代表的结构类型,例如,ProductRepository。
• 为了避免组件类型之间的混淆,例如,一个模型可能被命名为Category,其存储库名为 CategoryRepository。
后缀命名:
命名包
应用程序包的名称必须为小写,不能有分隔符,也不能有驼字。
命名组件
对于组件类型UseCases的名称,表示用例中的操作(do, get, update, save, send, delete, add,执行、获取、更新、保存、发送、删除、添加)的操作被用作前缀。
命名可组合函数
对于UI组件,也就是可组合函数的定义,我们将使用以下的命名规则,这些规则参考了Google在架构指南中的文档²⁷:
Screen:用于表示整个屏幕的可组合函数的后缀。 屏幕
UI:用于composables,这些composables将视图的状态(UI State)与组件的图形表示(UI Elements)结合在一起。 用户界面
Elements:用于定义UI库组件(Buttons, Layouts, Checkbox, TextFields ,如按钮、布局、复选框、文本字段等)的composables的后缀,这些组件构成了视图。 元素
Preview:用于预览视图(元素)的composables的前缀。Screen和UI composables也可以被组合预览,但由于对状态和其他变量的依赖性,它变得更加复杂。
注意:
在构建应用程序时,虽然定义所有的组件类型很重要,但也可能存在例外情况,不需要定义所有的组件类型,可以省略一些。具体取决于应用程序中屏幕的复杂程度,需要决定哪些组件类型适用,哪些不适用。
本章中,我想描述在开始实现之前要遵循的架构定义。还澄清了用于组织应用程序项目的规则。我必须澄清,本章中给出的定义是建议性的。读者可以自定义或假定适合自己的约定和规则,或者在实现中感到舒适的约定和规则。在下一章中,我们将开始实现OrderNow,首先要构建的是它的框架,即它的主要结构。