翻译水平有限,见谅!

Over the last several years we’ve seen a whole range of ideas regarding the architecture of systems. These include:

在过去的几年里,我们已经看到了关于系统架构的一系列想法,包括:

Though these architectures all vary somewhat in their details, they are very similar. They all have the same objective, which is the separation of concerns. They all achieve this separation by dividing the software into layers. Each has at least one layer for business rules, and another for interfaces.

虽然这些架构在细节上有所不同,但也非常相似。这些架构方式有着相同的目标,即分割关注点。并且都通过软件分层来实现这种分割。每种方式都至少含有一个业务规则层和一个借口层。

Each of these architectures produce systems that are:

这些架构都像这样的去构造系统:

  1. Independent of Frameworks. The architecture does not depend on the existence of some library of feature laden software. This allows you to use such frameworks as tools, rather than having to cram your system into their limited constraints.

框架独立。架构不依赖于一些满载功能的软件库。这可以让你像使用工具一样使用这样的框架,而不是把系统塞到他们有限的约束之中。

  1. Testable. The business rules can be tested without the UI, Database, Web Server, or any other external element.

可测试性。业务规则可以在没有UI,数据库,Web服务器,或者其他外部元素的情况下完成测试。

  1. Independent of UI. The UI can change easily, without changing the rest of the system. A Web UI could be replaced with a console UI, for example, without changing the business rules.

UI独立。在不改变系统其余部分的情况下完成UI的简易修改。如,Web UI可以在不改变业务规则的基础之上替换成控制台UI。

  1. Independent of Database. You can swap out Oracle or SQL Server, for Mongo, BigTable, CouchDB, or something else. Your business rules are not bound to the database.

数据库独立。业务规则不绑定的数据库中,这样你可以更换Oracle or SQL Server, for Mongo, BigTable, CouchDB,或者其他数据库。

  1. Independent of any external agency. In fact your business rules simply don’t know anything at all about the outside world.

外部机制独立。事实上业务规则根本不知道外层的事情。

The diagram at the top of this article is an attempt at integrating all these architectures into a single actionable idea.

本文上方的图片尝试着把这些架构整合到一个单一可执行的想法中。

The Dependency Rule

依赖规则

The concentric circles represent different areas of software. In general, the further in you go, the higher level the software becomes. The outer circles are mechanisms. The inner circles are policies.

同心圆代表着软件的不同领域。一般来讲,越往里层,软件的级别就会越高。外环是机制,里环是策略。

The overriding rule that makes this architecture work is The Dependency Rule. This rule says that source code dependencies can only point inwards. Nothing in an inner circle can know anything at all about something in an outer circle. In particular, the name of something declared in an outer circle must not be mentioned by the code in the an inner circle. That includes, functions, classes. variables, or any other named software entity.

依赖规则是这个架构可以工作的首要规则。规则要求,源代码的依赖只能指向内层。内层不应该知道外层环的任何任何东西。特别的,外层声明的任何名称不应该在内存代码中出现。包括,函数,类,变量,和其他的软件实体的名称。

By the same token, data formats used in an outer circle should not be used by an inner circle, especially if those formats are generate by a framework in an outer circle. We don’t want anything in an outer circle to impact the inner circles.

同样的道理,外层使用的数据格式不应该在内环中使用,特别是当这些数据格式在外环框架形成的时候。外环的任何东西都不应当影响内环。

Entities

实体

Entities encapsulate Enterprise wide business rules. An entity can be an object with methods, or it can be a set of data structures and functions. It doesn’t matter so long as the entities could be used by many different applications in the enterprise.

实体封装了企业范围内的业务规则。实体可以是个包含有方法的对象,或者一系列的数据结构和函数。这并不重要,只要实体可以被企业范围的很多不同应用所使用。

If you don’t have an enterprise, and are just writing a single application, then these entities are the business objects of the application. They encapsulate the most general and high-level rules. They are the least likely to change when something external changes. For example, you would not expect these objects to be affected by a change to page navigation, or security. No operational change to any particular application should affect the entity layer.

如果没有企业,而是只想写一个独立的应用,那么应用的业务对象就是这些实体。他们封装了最通用的和高等级的规则。如果外部改变该他们是最不可能改变的。比如,你不期望这些实体对象呗页面导航或者安全所影响。任何特定应用的操作变化都不应该影响实体层。

Use Cases

用例

The software in this layer contains application specific business rules. It encapsulates and implements all of the use cases of the system. These use cases orchestrate the flow of data to and from the entities, and direct those entities to use their enterprise wide business rules to achieve the goals of the use case.

此层次的软件包含了应用的特定业务规则。它整合并且实现了系统中需要的所有用例。这些用例协调着来往于实体之间的数据流,并且指引实体使用它们企业范围内的业务规则实现用例的目标。

We do not expect changes in this layer to affect the entities. We also do not expect this layer to be affected by changes to externalities such as the database, the UI, or any of the common frameworks. This layer is isolated from such concerns.

我们不希望变动这层会影响到实体。也同样不希望这层会受到外层变动的影响,如数据库,UI,或者其他的通用框架。从这样的想法触发,这个层次应该是独立的。

We do, however, expect that changes to the operation of the applicationwill affect the use-cases and therefore the software in this layer. If the details of a use-case change, then some code in this layer will certainly be affected.

但是,应用操作的改变仍然会影响到用例和这个层次的软件。如果用例的细节改变了,那么这层的相关代码当然也要变。

Interface Adapters

接口适配器

The software in this layer is a set of adapters that convert data from the format most convenient for the use cases and entities, to the format most convenient for some external agency such as the Database or the Web. It is this layer, for example, that will wholly contain the MVC architecture of a GUI. The Presenters, Views, and Controllers all belong in here. The models are likely just data structures that are passed from the controllers to the use cases, and then back from the use cases to the presenters and views.

这层的软件是一系列的适配器,作用于将用例和实体方便使用的数据格式转换成如数据库或者Web等外层方便的数据格式。比如,这层包含全部的GUI的MVC架构。Presenters,Views,Controllers全部都属于这层。模型可能是一种从控制层到用例层,又从用力曾返回给persistents和views的数据结构。

Similarly, data is converted, in this layer, from the form most convenient for entities and use cases, into the form most convenient for whatever persistence framework is being used. i.e. The Database. No code inward of this circle should know anything at all about the database. If the database is a SQL database, then all the SQL should be restricted to this layer, and in particular to the parts of this layer that have to do with the database.

相似的,数据从实体和用例层方便的形式转换到持久层框架使用的方便的形式,即,数据库。这层以里不会有代码知道任何数据库相关的东西。如果数据库是SQL数据库,那么所有的SQL应当被限制在这个层次中,特别的,这个层次不得不与数据库进行交互。

Also in this layer is any other adapter necessary to convert data from some external form, such as an external service, to the internal form used by the use cases and entities.

在这个层次,任何其他的适配器都需要将数据从外部形式(如外部服务)转换成用例和实体使用的内部形式。

Frameworks and Drivers.

框架和驱动

The outermost layer is generally composed of frameworks and tools such as the Database, the Web Framework, etc. Generally you don’t write much code in this layer other than glue code that communicates to the next circle inwards.

最外层一般包含框架和工具,如数据库Web框架等等。通常,这层不需要写太多的和其他里层通信的胶水代码。

This layer is where all the details go. The Web is a detail. The database is a detail. We keep these things on the outside where they can do little harm.

这层包含所有的细节,Web是细节,数据库是细节,我们把这些东西放到外层可以减少对整体的伤害。

Only Four Circles?

只含有四个环?

No, the circles are schematic. You may find that you need more than just these four. There’s no rule that says you must always have just these four. However, The Dependency Rule always applies. Source code dependencies always point inwards. As you move inwards the level of abstraction increases. The outermost circle is low level concrete detail. As you move inwards the software grows more abstract, and encapsulates higher level policies. The inner most circle is the most general.

不,这几个环只是概要。可能你会发现你需要比这4个还要多的环。没有规则知名一定要有这4个环。但是,依赖规则总是要被应用。源码依赖总是指向内层。越往内层移动抽象的等级越高。最外层环是低抽象等级的具体细节。越往内层移动抽象的等级越高,也封装了更高等级的策略。最内层是最通用的策略。

Crossing boundaries.

跨越边界

At the lower right of the diagram is an example of how we cross the circle boundaries. It shows the Controllers and Presenters communicating with the Use Cases in the next layer. Note the flow of control. It begins in the controller, moves through the use case, and then winds up executing in the presenter. Note also the source code dependencies. Each one of them points inwards towards the use cases.

图的右下方是一个表示如何跨越环形界限的列子。它展现了Controllers和Presenters通过下一次的用户进行交互的情形。注意控制流。它开始于Controller,移动穿越用例层,然后在Persenter层执行。同样注意代码依赖规则。他们中的每个都指向里层的用例。

We usually resolve this apparent contradiction by using the Dependency Inversion Principle. In a language like Java, for example, we would arrange interfaces and inheritance relationships such that the source code dependencies oppose the flow of control at just the right points across the boundary.

我们通常使用依赖倒置规则来解决这个明显的矛盾。在一种语言中,比如Java,我们会安排接口和继承关系,这样源代码依赖可以反向控制流在恰到好处的点跨越边界。

For example, consider that the use case needs to call the presenter. However, this call must not be direct because that would violate The Dependency Rule: No name in an outer circle can be mentioned by an inner circle. So we have the use case call an interface (Shown here as Use Case Output Port) in the inner circle, and have the presenter in the outer circle implement it.

比如,用例需要调用persenter。然而,这个不能直接调用,因为会违反依赖规则:外层环的任何名字都不能在内层环提及。所以在里层环我们使用用例调用接口(这里展现为Use Case Output Port),并且在外层环实现它。

The same technique is used to cross all the boundaries in the architectures. We take advantage of dynamic polymorphism to create source code dependencies that oppose the flow of control so that we can conform to The Dependency Rule no matter what direction the flow of control is going in.

相同的技术被用于跨越架构中的所有边界。我们使用动态多态的优势创建代码依赖来反向控制流,这样保证不管控制流是从哪个方向进来都满足依赖规则。

What data crosses the boundaries.

什么数据跨越边界

Typically the data that crosses the boundaries is simple data structures. You can use basic structs or simple Data Transfer objects if you like. Or the data can simply be arguments in function calls. Or you can pack it into a hashmap, or construct it into an object. The important thing is that isolated, simple, data structures are passed across the boundaries. We don’t want to cheat and pass Entities or Database rows. We don’t want the data structures to have any kind of dependency that violates The Dependency Rule.

通常跨越边界的数据是简单的数据结构。如果喜欢,可以使用基本数据结构或者简单的数据传输对象。或者数据也可以简单的是函数调用的参数。或者你可以打包成hashmap,或者构建一个对象。最重要的是独立,简单的数据结构在边界见跨越。我们不应该欺骗性的传输实体或者数据库结果集。我们不应该让传输的数据结构含有违法依赖规则的某种依赖关系。

For example, many database frameworks return a convenient data format in response to a query. We might call this a RowStructure. We don’t want to pass that row structure inwards across a boundary. That would violateThe Dependency Rule because it would force an inner circle to know something about an outer circle.

例如,很多数据库框架返回简单的数据格式给查询。我们称之为RowStructure。我们不应该通过边界传输这样的结构到里层。这会违反依赖规则,因为它强制了内层环来知道外层的东西。

So when we pass data across a boundary, it is always in the form that is most convenient for the inner circle.

所以,我们跨界传递数据的时候,数据总是要以内层需要的最简单的方式来传递。

Conclusion

结论

Conforming to these simple rules is not hard, and will save you a lot of headaches going forward. By separating the software into layers, and conforming to The Dependency Rule, you will create a system that is intrinsically testable, with all the benefits that implies. When any of the external parts of the system become obsolete, like the database, or the web framework, you can replace those obsolete elements with a minimum of fuss.

遵循这些简单的规则并不困难,并且会为你省去很多前进中头痛的事情。将软件分层,并且遵循依赖规则,将保证你建立一个本质上可测试的系统,并且包含有我们阐述的优点。如果系统的外部模块过时了,比如数据库或者Web框架,你可以在投入最小的情况下来替换这些元素。

Uncle Bob Martin is 8th Light's Master Craftsman. He's an award winning author, renowned speaker, and über software geek since 1970.

TweetFollow @unclebobmartin

The Clean Architecture--一篇很不错的关于架构的文章的更多相关文章

  1. 关于singer elt 的几篇很不错的文章

    以下是链接来自singer 团队的实践,很不错,值得学习 参考连接 https://www.stitchdata.com/blog/100-billion-records-later-refining ...

  2. (转)一篇很不错的介绍Eclipse插件Menu及其扩展点的文章

    原文在:http://tech.ddvip.com/2010-04/1271054623150507.html 菜单是各种软件及开发平台会提供的必备功能,Eclipse 也不例外,提供了丰富的菜单,包 ...

  3. 一篇很不错的关于WPF DataGrid的文章,包含validation

    https://www.codeproject.com/Articles/30905/WPF-DataGrid-Practical-Examples

  4.   PS2: 这篇文章中的图片绘图工具使用的是Dia (sudo apt-get install dia)。据说yEd也很不错。

    SBCL编译过程 - O.Nixie的专栏 - 博客频道 - CSDN.NET PS2: 这篇文章中的图片绘图工具使用的是Dia (sudo apt-get install dia).据说yEd也很不 ...

  5. C#不用union,而是有更好的方式实现 .net自定义错误页面实现 .net自定义错误页面实现升级篇 .net捕捉全局未处理异常的3种方式 一款很不错的FLASH时种插件 关于c#中委托使用小结 WEB网站常见受攻击方式及解决办法 判断URL是否存在 提升高并发量服务器性能解决思路

    C#不用union,而是有更好的方式实现   用过C/C++的人都知道有个union,特别好用,似乎char数组到short,int,float等的转换无所不能,也确实是能,并且用起来十分方便.那C# ...

  6. 清晰架构(Clean Architecture)的Go微服务: 程序结构

    我使用Go和gRPC创建了一个微服务,并试图找出最佳的程序结构,它可以用作我未来程序的模板. 我有Java背景,并发现自己在Java和Go之间挣扎,它们之间的编程理念完全不同.我写了一系列关于在项目工 ...

  7. 清晰架构(Clean Architecture)的Go微服务: 事物管理

    为了支持业务层中的事务,我试图在Go中查找类似Spring的声明式事务管理,但是没找到,所以我决定自己写一个. 事务很容易在Go中实现,但很难做到正确地实现. 需求: 将业务逻辑与事务代码分开. 在编 ...

  8. 清晰架构(Clean Architecture)的Go微服务

    我用Go和gRPC创建了一个微服务项目,并试图找出最好的程序结构,它可以作为我其他项目的模板.我还将程序设计和编程的最佳实践应用于Go Microservice程序,例如清晰架构(Clean Arch ...

  9. 清晰架构(Clean Architecture)的Go微服务: 程序设计

    我使用Go和gRPC创建了一个微服务,并将程序设计和编程的最佳实践应用于该项目. 我写了一系列关于在项目工作中做出的设计决策和取舍的文章,此篇是关于程序设计. 程序的设计遵循清晰架构(Clean Ar ...

随机推荐

  1. 一款代码高亮插件 -- SyntaxHighlighter

    SyntaxHighlighter 是当前用得最多的一款代码高亮插件,包括本博客也用到了该插件来显示代码,大家可以看到效果了.只不过这是针对WordPress的一款代码高亮插件,而今天我要给大家介绍的 ...

  2. Wordpress 从 MySQL 获取文章链接 permalinks

    SELECT wpp.post_title, wpp.guid, wpp.post_date, REPLACE( REPLACE( REPLACE( REPLACE( wpo.option_value ...

  3. 简单的素数问题(C++)

    [问题描述] 已知三个素数的和为 n ,正整数 n 由键盘输入,计算并输出这三个素数乘积的最大值. [代码展示] # include<iostream>using namespace st ...

  4. xshell连接不到虚拟机,安装ssh服务

    刚安装的虚拟机镜像是Ubuntu 16.04版本,防火墙已经关闭. 测试: 检查虚拟机分配的ip地址. 1.虚拟机ping宿主机:可以ping通 2.宿主机ping虚拟机:可以ping通 3.检查网络 ...

  5. Gitlab自动触发Jenkins构建项目

    Gitlab自动触发Jenkins构建项目 一.前提 Gitlab已安装配置好. Jenkins已安装Gitlab plugin. 二.配置jenkins中Job 1.勾选触发器下的gitlab触发器 ...

  6. SPOJ 1812 Longest Common Substring II(后缀自动机)(LCS2)

    A string is finite sequence of characters over a non-empty finite set Σ. In this problem, Σ is the s ...

  7. Kernel Mode, User Mode

    之前关于kernel mode,user mode之间的切换,有个问题一直有些疑惑. 一个进程有没有办法,从user mode切换到kernel mode去执行自己的代码.我知道答案肯定是不行,但是为 ...

  8. Storm ui 显示异常

    今天安装storm集群的时候,各个进程也都起来,却发现Storm ui界面下无法观察Storm集群的状态 有很多地方处理不当都会造成这种现象: 1.storm.yaml配置不当 2.防火墙的问题 3. ...

  9. ai学习记录

    界面:多个预编辑区:制作图形,使用的图形放到工作区内,不使用在预编区.没有Ctrl/Alt+delete的概念,没有前后景颜色.新建:分辨率:矢量软件和分辨率无关: 新建时候不要勾选对齐到像素网格 存 ...

  10. hihocoder 1320 压缩字符串(字符串+dp)

    题解: 其实就是对应三种dp的转移方式 1.拼接类型 dp[i][j] = dp[i][c] + dp[c][j] 2.不变类型 dp[i][j] = j-i+1 3.重复类型(必须满足有k个循环节) ...