分离是为了更好的结合

分离是为了更好的结合

写出高质量软件是困难和复杂的:不仅仅是为了满足需求,还应该是健壮的,可维护的,可测试的,并且足够灵活以适应成长和变化。这就是洋葱架构出现的原因,它代表一组优秀的开发实践,用来开发任何的软件应用都是一个不错的方式。

洋葱架构,也成为整洁架构(The Clean Architecture),用来构建具有如下特点的系统:

1.    独立的Frameworks
2.    可测试
3.    独立的UI
4.    独立的数据库
5.    独立的任意外部服务(代理)

看到这张图,你应该能理解为什么称其为洋葱架构了:D, 没错,这就是它的原理图。注意,并不是只能使用4个圆环,重点在于这里的依赖原则:代码依赖是从外向内的,内环中的代码不应该知道外环中的任何东西。

这里有一些相关的词汇可以帮助更好的理解和熟悉这种方式:

Entities:应用的业务对象。

Use Casess: Use Casess协调(Orchestrate)数据从Entities的流入和流出,也被称为Interactors。

Interface Adapters:这个Adapter集为Use Casess和Entities把数据转换为方便使用的格式(如渲染展示在页面上),Presenters和Controllers属于这里。

Frameworks and Drivers:这是实现所有细节的地方:UI,Tools,Frameworks等。

下面用一张更生动图来辅助说明它的原理:

依赖原则(The dependency rules)

上面的同心圆代表软件的不同部分。总得来说,越往里面,代码级别越高。外层的圆是(实现)机制,而内层的圆是原则(Policies)。

让这个架构起作用的最主要原则是依赖原则。这个原则要求源码依赖只能指向内部。内部的圆不能知道外圆的任何事情。一般来说,外圆的声明(包括方法、类、变量或任何软件实体)不能被内圆引用。

同样的,外圆使用的数据格式不能被内圆使用,尤其是外圆中的Framework产生的格式。我们不想让外圆的任何东西影响内圆。

越往里面抽象级别越高,最外层的圆是低级别的具体细节。越往里面内容越抽象,并且封装更高级别的原则(Policies)。最里面的圆是最通用的。

Entities

Entities封装了企业级的业务规则。一个Entity可以是一个带方法的对象,也可以是一个数据结构和方法集。Entities可以被用于企业的其他应用。

如果你没有加入企业,而是仅仅在写一个简单的应用,那么这些Entities就是这个应用的业务对象。它们封装了最通用、最上层的原则。它们是最不容易改变的,即使外部的东西改变了。例如,你不想让这些对象受到页面导航、安全的影响。应用的任何操作变化都不应该影响Entities Layer。

Use Casess

这一层包含了应用特有的业务规则。它封装和实现了系统的所有用例。这些用例协调数据从entities的流入和流出,并且指导entities使用它们的企业级业务规则来达到用例的目标。

我们不希望这一层的改变影响到Entities,同时也不希望这一层被外层的改变影响,如外层的数据库,UI或者任何Frameworks的改变,这一层独立于这些关注点。

当然,我们确实期望应用的操作变化影响用例层。如果一个用例的细节改变,那么这一层的部分代码确实会受到影响。

Interface Adapters

这一层包含一个adapters set(数据适配器集),它们把适用于Use Casess和entities的数据转换为适用于外部服务(external agency,如Database或Web)的格式。 例如,这一层可以完全包括GUI的MVX架构,Presenters, Views和Controllers都属于这里。Models可能仅仅是从Controllers传到Use Casess的数据结构,然后从Use Casess返回给Presenters和Views。

这一层的数据会被转换,从适用于entities和Use Casess的格式转换到适用于所使用的持久化框架的格式(如数据库)。这个圆以内的代码不应该知道关于数据库的任何东西。如果是一个SQL数据库,那么所有的SQL应该被限制到这一层,并且通常来说是被限制到层中跟数据库有关的部分。

同样,这一层也需要一些其他必要的Adapter来把外部的数据格式(如来自于外部服务的格式),转换为适用于Use Casess和entities的格式。

Frameworks and Drivers.

最外面的一层通常由Frameworks和Tools组成,如Database,Web Framework等。一般来说,除了用于和内层圆交互的连接代码,你不会在这一层写很多代码。

这一层是实现所有细节的地方。Web和Database都是需要实现的细节。我们把这些东西放在外面以减轻来自于它们的伤害(即减轻对他们的依赖)。

跨界

在图的右下角是一个我们应该如何跨界的例子。它展示了Controllers、Presenters与下一层的Use Casess的交互。注意控制的流向,它开始于Controller,经过Use Casess,最终在Presenter中执行。同时也请注意Source Code依赖,它们每一个都指向内部的Use Casess。

我们通常用依赖倒置原则来解决这个明显的矛盾。比如,在Java这样的语言里,我们可以使用接口和继承关系在合适的地方让源码依赖与控制流反向来跨界。

例如,假设Use Cases需要访问Presenter,当然,不能是直接访问,不然会违反依赖原则,所以我们让内圆的Use Cases访问一个接口(如图中的Use Cases output port),然后外圆的Presenter实现这个接口。

在这个架构中,同样的技术也被用于跨越其他的边界。我们利用运行时多态来创建与控制流相反的SourceCode依赖以满足依赖原则,无论控制流是如何流向的。

通常跨界的数据都是简单的数据结构。你可以使用简单的结构或数据传输对象(Data Transfer Object)。这个数据可以简单的是方法调用的参数,你也可以把它包装到一个HashMap或者一个对象。最重要的是独立的、简单的数据结构才能跨越边界。不要投机取巧,如传输Entites或者Database rows。我们不想让这个数据结构有任何违反依赖原则的依赖。

例如,很多的数据库框架对于query返回一个方便的数据格式,我们可以称之为Row Structure,我们不想向内部传递这个row structure。这会让内圆知道外圆的内容而违反了依赖原则。

所以,我们应该以最适用于内圆使用的格式来传递跨界的数据。

总结

满足这些简单的原则并不难,并且会减少项目进程中很多头疼的问题。通过把软件分成几层,并且满足依赖原则,你将会创建一个本身就可测试的系统,同时还有其他的好处。当系统的任何外层部分(如Database,Web 框架)废弃的时候,你可以轻松的替换这些废弃的元素。

代码实践可以参考:https://github.com/android10/Android-CleanArchitecture/releases

本文大部分内容译自The-Clean-Architecture,其中加入了自己学习该架构时的理解,如有意见和建议,欢迎交流!

参考:

The-Clean-Architecture:http://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html

Architecting-Android-The-Clean-Way:http://fernandocejas.com/2014/09/03/architecting-android-the-clean-way/

Architecting Android…The evolution:http://fernandocejas.com/2015/07/18/architecting-android-the-evolution/

时间: 2024-11-08 13:09:29

分离是为了更好的结合的相关文章

[iOS]将DataSource分离并构建更轻量的UIViewController

在objccn.io中看到一篇文章,构建更轻量的View Controllers,在此自己实践一下加深理解. 新疆项目,learn--tableview,类前缀为LT,开始我们的实验. 首先需要在StoryBoard中拖拽一个UITableView,在头文件中申明tableView变量并建立连接: 新建ArrayDataSource类,作为TableView的DataSource.目的是将DataSource从原本的ViewController中分离出来: // // ArrayDataSour

java结合node.js非对称加密,实现密文登录传参——让前后端分离的项目更安全

前言   在参考互联网大厂的登录.订单.提现这类对安全性操作要求较高的场景操作时发现,传输的都是密文.而为了目前项目安全,我自己负责的项目也需要这方面的技术.由于,我当前的项目是使用了前后端分离技术,即node.js做前端,spring boot做后端.于是,我开始搜索有关node.js与java实现非对称加密的资料,然而,我却没有得到一个满意的答案.因此,我有了写本篇博客的想法,并希望给用到这类技术的朋友提供帮助. 一.明文密码传输对比 首先. 构建spring boot 2.0项目 引入we

foreman架构的引入4-安装Foreman1.6.3架构(foreman与puppetmaster分离)

零基础学习Puppet自动化配置管理系列文档 注意:本实验是在离线情况下安装的,所以需要在本地创建自己的yum仓库,创建方法可参考<如何根据版本制作属于自己的puppet yum源>,如何你实在是比较懒或者搞不定rpm包之间的依赖关系,那就去我的github上下载吧:https://github.com/kisspuppet/foreman-repo 更多安装细节请参考官网:http://theforeman.org/manuals/1.6/index.html 之前讲的all-in-one方

模型分离(选做)

模型分离--让代码更方便管理 新建models.py,将模型定义全部放到这个独立的文件中. 新建exts.py,将db = SQLAlchemy()的定义放到这个独立的文件中. models.py和主py文件,都从exts.py中导入db. 在主py文件中,对db进行始化,db.init_app(app). models.py from datetime import datetime from werkzeug.security import generate_password_hash,ch

模型分离

模型分离--让代码更方便管理 新建models.py,将模型定义全部放到这个独立的文件中. from datetime import datetime from werkzeug.security import generate_password_hash,check_password_hash from exits import db class User(db.Model): __tablename__ = 'User' id = db.Column(db.Integer,primary_k

分库分表、主从、读写分离

每天学习一点点 编程PDF电子书.视频教程免费下载: http://www.shitanlife.com/code 1. 漫谈 在进入正题之前,我想先随意谈谈对架构的拓展周期的想法(仅个人观点).首先,我认为初期规划不该太复杂或者庞大,无论项目的中长期可能会发展地如何如何,前期都应该以灵活为优先,像分库分表等操作不应该在开始的时候就考虑进去.其次,我认为需求变更是非常正常的,这点在我等开发的圈子里吐槽的最多,其中自然有 "领导们" 在业务方面欠缺整体考虑的因素,但我们也不该局限在一个观

DBA 小记 — 分库分表、主从、读写分离

前言 我在上篇博客 "Spring Boot 的实践与思考" 中比对不同规范的 ORM 框架应用场景的时候提到过主从与读写分离,本篇随笔将针对此和分库分表进行更深入地探讨. 1. 漫谈 在进入正题之前,我想先随意谈谈对架构的拓展周期的想法(仅个人观点).首先,我认为初期规划不该太复杂或者庞大,无论项目的中长期可能会发展地如何如何,前期都应该以灵活为优先,像分库分表等操作不应该在开始的时候就考虑进去.其次,我认为需求变更是非常正常的,这点在我等开发的圈子里吐槽的最多,其中自然有 &quo

[原创]基于VueJs的前后端分离框架搭建之完全攻略

首先请原谅本文标题取的有点大,但并非为了哗众取宠.本文取这个标题主要有3个原因,这也是写作本文的初衷: (1)目前国内几乎搜索不到全面讲解如何搭建前后端分离框架的文章,讲前后端分离框架思想的就更少了,而笔者希望在本文中能够全面.详细地阐述我们团队在前后端分离的摸索中所得到的搭建思路.最佳实践以及架构思想: (2)我们团队所搭建的前后端分离框架,并非只是将网上传播的知识碎片简单拼装,而是一开始就从全局出发,按照整个系统对前后端分离框架的最高期望进行设计,到目前为止,可以说我们的框架完全实现了对我们

浅谈前后端分离与实践(一)

一.起源 (故事纯属虚构,如有雷同,纯属巧合)传说在很久很久以前,我们有志之士有了个创业的想法,于是乎开始了自己的创业之梦,但是人手不足啊,于是乎所有角色老子一个人全包了: Roles: PM, DBA, RD, FED, Designer, ... Skills: Linux, MySQL, JAVA, JavaScript, HTML, CSS, ... Tools: phpmyadmin, photoshop, powerpoint, ... 我们用 express 应用生成器来模拟一下传