架构整洁之道

简介:

这篇文章是翻译(Uncle Bob Martin)的文章。原文在这:http://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html。这篇文章作者尝试使用简单的观点将各种架构的共通之处和最终目标说清楚。全文要说清楚的就是一件事“如何写出整洁的架构”。作者希望在架构系统的时候只需要秉持最简单的两个观点(分层和依赖规则)开发,就能开发出干净整洁的系统架构。

以下是译文

clip_image001

 

过去几年间有许多关于系统架构的观点。比如:

1 六角架构(Hexagonal Architecture )。 这种架构是由Alistair Cockburn提出的,并由Steve Freeman和Nat Pryce在他们的书《Growing Object Oriented Software 》中提到。

2 洋葱架构(Onion Architecture )。提出者是Jeffrey Palermo。

3 尖叫架构(Screaming Architecture)。提出者是Uncle Bob(就是这篇文章的作者)。

DCI 架构。提出者是James Coplien和Trygve Reenskaug。

BCE 架构。提出者是Ivar Jacobson。在他的书《Object Oriented Software Engineering: A Use-Case Driven Approach》中有大量提及对这种架构的说明。

 

虽然这些架构在细节处都有一些变化,但是实际上,它们是非常相似的。它们的目标是一样的,将各种实体间的关系进行分离。它们分离操作的方法也是一样的,采用软件分层的方式。它们分的层中至少有一个业务逻辑层,并且有其他的接口层。

 

每一种架构一定能在写系统的业务逻辑的时候有以下特征:

1 与框架的分离。

框架决不能依赖一些有限制特征的库。这样就能保证你能像使用手边的工具一样简单地使用这些框架,而不会让你的系统业务逻辑在使用前就有一些强制性的约束。

2 可测试性。

业务逻辑必须能独立测试,不需要UI,数据库,Web服务器或者一些其他的外部条件。

3 与UI的分离。

UI必须能非常容易独立地修改。而不能在改变UI的同时需要改变系统其他部分。比如当把系统的UI从Web UI改成控制台UI,你并不需要改变任何业务逻辑的代码。

4 与数据库的分离。

能很方便地在Oracle,SQL Server,Mongo DB,BigTable,CouchDB或者其他数据库中进行切换和改变。业务逻辑决不能依赖这些数据库。

5 与外部结构的分离。

系统的业务逻辑并不需要知道任何外部的结构。

 

文章最上面的图就是将所有这些架构进行统一概括,提取出统一的观点。

依赖规则(The Dependency Rule)

同心圆代表的是不同层级的软件代码。通常当你更深一步思考构造你的系统的时候,你的系统就会在更高的层级。最外层的圈代表的是机制级别的系统。最内层的代表的是策略级别的系统。

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

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

实体层(Entities)

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

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

用户实例层(Use Cases)

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

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

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

接口适配层(Interface Adapters)

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

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

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

框架和驱动层(Frameworks and Drivers)

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

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

 

只有四层?

答案是否定的。这个分层模型是纲要性的。在具体实现中,你会发现你可能需要到比四层更多的层级。并没有任何规定说你必须只能实现这四个层级。但是,依赖规则(The Dependency Rule)是必须遵守的:代码依赖只能使由外向内。越向内,抽象程度越高,越向外,细节信息越多。当你越向内设计的时候,你需要设计越高层面的策略。内部层级比外部层级更抽象。

跨层调用

图片的右下角是一个如何跨层调用的例子。它演示了控制器和表现器如何和用户实例层进行交互。注意下流向,从控制器开始,通过用户实例层,然后向上执行到表现器。这里也需要注意它们的代码依赖,必须是向内依赖的。

我们通常使用依赖倒置原则(Dependency Inversion Principle)来处理这种情况。比如在JAVA中,我们会使用接口或者继承关系来保证代码会在制定的地方依赖倒置。

举例说明,假设用户实例需要调用表现器。但是这种调用是违背了依赖规则的:外部的逻辑名称不应该被内部提到。所以我们让用户实例层调用一个接口(这里叫做用户实例输出端口Use Case Output Port),然后让表现层在外部实现这个接口。

相同的方法可以使用在架构的所有跨层调用上。因此实际上我们能使用多种形式的代码依赖达到我们能让控制流按照依赖规则的反方向流动。

什么数据能跨层调用?

一般跨层调用的数据是简单的数据结构。你可以使用数据结构或者是简单的数据传输流,又或者可以通过函数的参数来进行传递。你也可以将数据封装到一个hashmap结构,或者一个对象中。数据传输最重要的事情是无依赖,简单。我们并不希望跨层传递的数据是实体,或者是数据表的行数据,理由是我们不希望数据有任何形式的违反依赖规则。

例如,许多数据库框架对query查询返回行结构,我们叫它RowStructure。我们并不希望将这个RowStructure传递给内层结构,因为这个违反了依赖规则,它会强制让内层结构了解外层的结构。

总结

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

作者简介

clip_image002

Robert C. Martin (Uncle Bob) , 8th Light公司的Master Craftsman, 获奖作家,著名的演说家,uber software geek。

它的著作有:

Designing Object Oriented c++ Applications using the Booch Method

Patterns Languages of Program Design 3(程序设计的模式语言)

More c++ Gems

Extreme Programming in Practice(解析极限编程)

Agile Software Development: Principles, Patterns, and Practices(敏捷软件开发:原则、模式与实践)

uml for java Programmers(UML:JAVA程序员指南)

Clean Code (代码整洁之道)

The Clean Coder(编码整洁之道:专业程序员的行为准则)

目录
相关文章
|
7月前
|
程序员 开发者
代码之禅:在软件开发中寻求简化之道
当面对错综复杂的系统和冗长的代码时,程序员们往往渴望找到一种简洁而高效的解决方式。本文探讨了如何在软件工程实践中追求简洁性,介绍了几种提升代码质量、优化开发流程的策略,并强调了持续学习与反思的重要性。通过一系列实用的技术感悟,文章旨在引导读者思考如何将“简化”作为编程哲学的一部分,以期达到技术上的精进和心智上的清晰。
|
前端开发 测试技术 数据库
软件架构编年史:整洁架构
软件架构编年史:整洁架构
软件架构编年史:整洁架构
|
消息中间件 运维 前端开发
代码整洁之道
我们在做系统开发编码时,无论是对于系统响应及时性没有前端系统要求那么高,却有业务复杂、数据严谨的性质。还是面对高并发多线程,海量业务,分布式事务,一致性等要求很高的情况。良好的代码质量是保障系统和业务稳定的基础,要求我们从每一个代码、每一个变量、每一个方法做起
670 0
代码整洁之道
|
前端开发 开发者
架构整洁之道-01 设计与架构
设计:实现业务时的详细的设计,比如模块的依赖设计,业务逻辑设计,前端页面的设计。更偏向于细节内容的设计 - 架构:类似搭建房子一样,先有房子的形状、布局等,一个项目的架构那就是各个模块依赖关系,或者说结构。未来设计方向的一个框架。
149 0
|
设计模式 运维 分布式计算
《架构师修炼之道》第七章--架构模式
端口适配器模式可以确保核心业务逻辑不变,在多种环境下使用,以及在隔离其他组件(负责提供数据和事件的)的状态下进行测试
291 0
|
消息中间件 存储 缓存
一文读懂架构整洁之道
相信大家都非常清楚,如何编写可读性强的代码是一个合格程序员的必修课。 我在之前的文章《谈谈什么是好的代码》中谈了一些自己对整洁代码的感悟,代码并不是独立存在的,成百上千个类的系统在企业应用中非常常见,如何将代码进行有效的组织,保持高可读性,高可维护性,则是一个好的架构需要考虑的事情。本文从原则切入,聊聊组件的分层和解耦,浅谈下Bob大叔提出的整洁架构,感兴趣的同学也可以发表下自己的看法。
6601 0
一文读懂架构整洁之道
|
设计模式 测试技术 程序员
代码整洁之道(一)最佳实践小结
Any fool can write code that a computer can understand. Good programmers write code that humans can understand. 普通的工程师堆砌代码,优秀的工程师优雅代码,卓越的工程师简化代码。如何写出优雅整洁易懂的代码是一门学问,也是软件工程实践里重要的一环。前段时间通读了三本经典书籍《代码整洁之道 》、《编写可读代码的艺术》、《重构:改善既有代码的设计》,本文将重点从注释、命名、方法、异常、单元测试等方面总结了一些代码整洁最佳实践。
352 0
|
设计模式 算法 程序员
代码大全2札记:软件架构中的设计
代码大全2札记:软件架构中的设计
161 0
|
Java 程序员
代码整洁之道札记:代码为什么要整洁
代码整洁之道札记:代码为什么要整洁
317 0
|
存储 数据库
架构整洁之道, 看这一篇就够了!
程序的世界飞速发展,今天所掌握的技能可能明年就过时了,但有些知识历久弥新,掌握了它们,你在程序的海洋中就不会迷路,架构思想就是这样的知识。
16262 0