架构整洁之道

简介:

这篇文章是翻译(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(编码整洁之道:专业程序员的行为准则)

目录
相关文章
|
Java Python
Python调用java程序
Python调用java程序
286 0
|
测试技术 开发工具
软件开发过程中常见风险和应对策略
软件开发过程中常见风险和应对策略
982 1
|
6月前
|
Java 数据库连接 微服务
Java高效学习指南:从入门到精通的科学路径与实践方法
本文系统梳理Java从入门到精通的科学学习路径,涵盖基础语法、面向对象、集合框架、并发编程、JVM原理、主流框架(Spring/Spring Boot/MyBatis)及项目实战,结合高效学习方法与优质资源推荐,助力开发者构建扎实技术体系,少走弯路,快速进阶。
|
12月前
|
监控 供应链 数据挖掘
电商API自动化运营指南:节省时间,提高效率
电商API自动化运营指南助您在竞争激烈的市场中脱颖而出。通过API实现订单、库存和数据处理的自动化,可大幅节省时间、降低错误率并提高效率。本文从基础概念到实战代码,逐步讲解如何利用Python等工具调用API,完成如订单管理、库存同步等任务。结合实际案例,展示自动化在库存管理、订单处理及数据分析中的应用,效率提升可达20-50%。立即行动,构建专属自动化系统,提升竞争力!
|
编解码 供应链 搜索推荐
虚拟现实与教育:沉浸式学习的潜力
【10月更文挑战第2天】虚拟现实(VR)技术正在革新教育领域,通过沉浸式体验提升学习效果和兴趣。本文探讨了VR在教育中的应用潜力,特别是在历史、地理、自然科学和语言教育中的案例。虽然面临设备成本和技术支持等挑战,但随着技术进步和成本降低,VR有望成为教育的重要工具,带来更丰富的学习体验。
|
机器学习/深度学习 数据采集 人工智能
智能化运维在企业IT管理中的应用与实践####
本文深入探讨了智能化运维(AIOps)的核心技术原理,通过对比传统运维模式,揭示了AIOps如何利用大数据、机器学习等先进技术提升故障预测准确性、优化资源分配及自动化处理流程。同时,文章详细阐述了智能化运维平台的实施步骤,包括数据收集与分析、模型训练与部署、以及持续监控与优化,旨在为企业IT部门提供一套切实可行的智能化转型路径。最后,通过几个典型应用案例,如某大型电商平台的智能告警系统和金融企业的自动化故障排查流程,直观展示了智能化运维在实际业务场景中的显著成效,强调了其在提升运维效率、降低运营成本方面的关键作用。 ####
483 4
|
人工智能 运维 监控
智能化运维:AI在IT基础架构管理中的应用
【6月更文挑战第8天】本文将探讨人工智能(AI)如何革新传统的IT运维领域,实现智能化的故障预测、自动化的修复流程以及高效的资源分配。我们将通过实例分析AI技术如何优化数据中心的能源使用,提升网络性能监控的准确性,并降低系统维护成本。
492 2
|
SQL 关系型数据库 MySQL
MySQL中的12个SQL编写规范
SQL良好习惯提升查询清晰度、效率和安全性,包括使用EXPLAIN分析查询计划、DELETE/UPDATE时加LIMIT限制影响范围、为表和字段添加注释、关键字大写缩进、指定INSERT字段名、先测试后执行、表含主键及时间戳字段、Update/Delete需Where条件、用InnoDB引擎、避免SELECT *,选择UTF8字符集和规范索引命名。
MySQL中的12个SQL编写规范
|
SQL 人工智能 自然语言处理
通义灵码代码大模型应用实践访谈
2024 年 6 月 26 日,中国信息通信研究院(以下简称“中国信通院”)在可信 AI·南京人工智能产业发展论坛正式发布了代码大模型评估结果。阿里云计算有限公司的通义灵码代码大模型顺利通过评估,获得目前最高等级 4+ 级。该等级代表阿里云通义灵码大模型在通用能力及专用场景能力绝大多部分达到优秀水平,同时具备较为成熟的管理机制。
【天梯赛】L1-095 分寝室
输出的方案对应女生都是 24/4=6 人间、男生都是 60/6=10 人间,人数差为 4。满足前三项要求的分配方案还有两种,即女生 6 间(都是 4 人间)、男生 4 间(都是 15 人间);同时,每间女寝人数必须都一样,每间男寝人数必须都一样,也就是女生总人数对女寝数取模为0,男生总人数对男寝数取模为0。输入在一行中给出 3 个正整数 n0​、n1​、n,分别对应女生人数、男生人数、寝室数。按题意模拟,因为知道总寝室数为n,所以可以从1~n-1暴力枚举女寝 i 的数量,那么男寝的数量则为 c-i。
523 6

热门文章

最新文章