Clean架构

Clean Architecture (清晰的架构) 是由Uncle Bob在2012年发表的, 针对Web应用的架构设计. Bob大叔总结了各种系统架构的共通之处以及最终目标, 并提出只需秉持分层与依赖规则的原则, 就能开发出干净清晰的架构.

原文 http://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html

译文 http://www.cnblogs.com/yjf512/archive/2012/09/10/2678313.html

分层

Clean Architecture认为系统最少要分为但不局限于四层:

  1. 实体层(Entities)

    实体是用来封装公司的业务规则的。一个实体可以是一个带方法的对象,也可以是一些数据结构和函数。只要实体能被公司的不同业务逻辑部件使用,实体的具体表现形式是无所谓的。

    或许你并不是想写公司级的架构,而只是想写一个简单的应用,那么这里实体就是指的应用的业务逻辑对象。它们封装了最通用的规则,并且当外部环境变化的时候,这些实体是最不需要被变化的。举例来说,比如在增加翻页需求或者是安全需求的时候,这些实体是最不应该被改变的。没有任何具体的应用需要改变实体层。

  2. 用户实例层(Use Cases)

    这一层的软件结构包含了具体的应用业务逻辑。它实现了所有的用户实例。这些用户的实例由流入实体的数据流和流出实体的数据流实现,这些用户实例使得内层的实体能依靠实体内定义的业务逻辑规则来完成系统的用户需求。

    我们不希望用户实例层的任何改变会影响到实体层。我们同样也不希望用户实例层会被外部的结构层,比如UI、数据库或者任何公共的框架,的改变而影响。这层应该是独立于这些概念的。

    当然,必然发生的是应用的业务逻辑被修改会影响到用户实例层的代码和结构。如果用户的需求改变了,这层的部分当然会被修改。

  3. 接口适配层(Interface Adapters)

    这一层的软件结构的目的就是进行数据的转换,将便于用户实例和实体层操作的数据结构变化成为最便于外部结构(比如数据库或者Web)操作的数据结构。比如GUI的MVC结构,表现器、视图器、控制器都是属于这个结构的。这层很可能是通过控制器将数据结构传给用户实例层,并且返回数据给表现器,视图器。

    数据在这层会被转换,将便于实体层和用户实例层使用的数据转化成为持久层能使用的数据,比如数据库。这一层的代码并不需要知道任何数据库的信息。如果数据库是SQL数据库,那么,所有的SQL语言应当在这层被限制使用,特别是在这一层中与数据库有交互的代码部分。

    当一些外部的服务需要与用户实例层和实体层进行交互的时候,这时候需要的数据转换也理所当然地放在这一层了。

  4. 框架和驱动层(Frameworks and Drivers)

    最外层是由框架和使用工具组成的。比如数据库,Web框架等。通常你并不需要写很多代码就能达到与内层进行交互的行为。

    这层表达的是所有的数据应该具体最终到达的地方。Web是数据的最终到达地,数据库也是数据的最终到达地。我们把这些东西放在最外层,它们几乎对整个系统的架构造不成什么影响。

依赖规则

代码依赖只能使由外向内。换句话说,内层结构的代码不能包含有任何外层结构的信息。尤其是一些外层结构的名称不应该被内层结构的代码提到,比如函数名,类名,变量名,或者其他的系统实体的名称。

同样的,外层的数据结构不应该被内层代码使用,特别是那些由外部框架生成的数据结构。我们并不希望外部结构的任何东西会影响到内部结构。

总结

Clean架构中最重要的一层是用户实例层, 即业务逻辑层. 这一层决定了软件有什么功能, 最终目标是什么, 以及解决了什么现实问题.

遵守这些简单的规则并不难, 但是它们会解决你很多头疼的问题. 将系统分层, 遵守依赖规则, 你就会自然写出可测试的系统了, 所有附带的好处也会实现了. 当任何外部的系统是独立的, 比如数据库, web框架, 你就能以最小的代价将它们进行替换.

Clean架构在Android中的应用

Clean架构清晰, 干净, 自然也会在Android中有所应用, 其中影响最大的是来自Fernando Cejas的 Architecting Android…The clean way? . 推荐阅读 译文 以及 Android设计架构 — 进化.

Fernando Cejas将整个应用分为三个层次: 展示层, 领域层, 数据层.

与Clean架构在Web应用中一样, 在Android中最为重要的一层同样是业务逻辑层, 即领域层. 这一层是纯粹的Java层, 与任何Framework均无关联.

展示层在架构中又被Fernando Cejas使用MVP细分. 当然你也使用MVC或者MVVM. 不管是Controller, Presenter还是ViewModel都只处理UI逻辑以及一些简单的应用逻辑, 不涉及核心业务逻辑.

数据层则是使用由Martin Fowler提出的 仓储模式. 这样领域层则无需关心数据来自何处, 只需对数据进行处理之后交由展示层即可.

其实此架构思想很类似于DDD领域驱动设计. DDD最核心的是在进入开发之前由领域专家设计该应用的领域模型. 更多关于DDD的介绍可以参考 DDD领域驱动设计基本理论知识总结 (预警: 纯理论型).

关于Clean架构的思考

架构没有好与不好, 只有适合与不适合.

在这里需要明白一点, MVP是一种设计的范例, 仅仅提供一些基本的指导方针, 没有固定是实现, 也没有组织来维护这种架构, 缺少文档说明. 并且MVP经过这么多年的发展, 诞生之初的一些设计思想也早已不适用于如今的场景. 引入了MVP不会立即拯救你的应用, 也不要以为使用了MVP就能让代码更容易维护, 更少的Bug, 添加新功能会更容易. 对MVP理解不深刻同样会造成项目难以维护.

要理解MVx这些架构并不容易, 特别是因为他们中的很多都已经改变了或者死掉了. 追溯这些思想的传播会更困难, 因为人们会从同一种架构中读出不同的理解.

重要的是理解这种分层的思想并划定每个层次之间的职责界限.

Clean架构也如MVP一样, 是一种指导思想. Clean架构中最核心的两个概念 - 分层依赖规则是非常值得学习借鉴的. 但是Clean架构对于Android应用来说太过重量级了. 不复杂的项目也很难体会复杂架构的好处, 还会使类的数量大爆炸.

Clean架构中外层(如展示层)都是围绕着业务逻辑层来展开的. 业务逻辑层是整个应用中的核心. 但是通常来说, 移动应用大多数都没有多少业务逻辑. 移动应用通常要比Web应用简单. 移动应用更多的只是从REST API获取数据, 向用户展示数据. 偏向于展示阅读. 因此在大多数简单(相对来说)的移动应用中使用Clean架构是不合适的.

移动应用有其特殊的需要处理的问题: 内存, 存储, 重新创建, 网络, 地理位置等等。但是这些并不是业务逻辑. 就算是一些比较简单的Web应用也只需要使用MVC+Service+Dao就适用了.

使用Clean架构需要在编码前做大量的设计工作, 花费人力物力, 开发起来也没那么快,而快对于互联网行业很重要, 可能最后结果是弊大于利. 这就类似于大型企业需要对人员组织分层, 决策自上而下有序的传达与执行. 出现问题也能及时定位是哪一层出现了问题. 但对于刚起步的企业或者小型企业来说则没必要过度分层, 耗时又耗力.

CoreLibs中的架构选择

结合目前公司项目以及开发人员水平来看, Clean架构在大部分情况下是不适合的.

  • 公司项目很多都是比较简单的, 只有少量业务逻辑, 一部分项目会有部分的业务逻辑, 只有极少数的项目会有较多的业务逻辑.

  • 项目从进入开发到最后验收时间一般是三个月, 周期较短.

  • 开发人员水平有限, 没有面向接口编程的经验会较难理解这么复杂的架构.

因此项目具体是只使用MVP还是Clean是需要视综合情况而定的.

简单的项目 (少量业务逻辑)

对于简单的项目, 我认为使用MVP对应用分层即可. 简单的APP项目仅仅只是对从Server端API拿到的数据做显示, 而无需对数据做一些业务处理. 简单的APP可以视作Server端的展现层的延展. 针对展现层使用MVP模式就足够了.

这种情况下, 可以按照实体类, 将REST API封装成不同的Model, 如UserManager, ProductManager等. Presenter则根据页面来调用不同的Model并处理少量的业务逻辑, 如数据边界的验证, 存储简单的SharedPreferences数据等. View则尽可能的简单与被动.

遵守Clean架构中的依赖规则.

  • Model

    | - Entity

    | - Model

  • Presenter

  • View

普通的项目 (部分业务逻辑)

对于有部分业务逻辑的项目, 同样只使用MVP对应用分层即可. 但是需要对Model层再细分.

Model层一般分为实体层与REST API层, 在此我们需要将Model再延伸一层出来, 专门用于处理业务逻辑. REST API就成了数据来源之一, 还有一部分数据可能来自DB或SharedPreferences等手机储存. 我们可以将其他数据来源封装成Helper类以供业务逻辑层使用. 业务逻辑层则主要是从API或Helper中拿到数据, 并做一些业务处理, 然后传递给Presenter. 至于Presenter, 只需要负责UI逻辑以及数据边界验证等. View则尽可能的简单与被动.

遵守Clean架构中的依赖规则.

  • Model

    | - Entity

    | - Api

    | - Helper

    | - Model

  • Presenter

  • View

复杂的项目 (大量业务逻辑)

目前还未遇到非常复杂的项目, 因此只能有一个大概的规划, 还无法实施. 我更倾向于使用Clean架构, 具体可以参考前文中的 Clean架构在Android中的应用 一节.

Last updated