翻译水平有限,见谅!
- Uncle Bob
- 13 Aug 2012
- Architecture
- Craftsmanship
Over the last several years we’ve seen a whole range of ideas regarding the architecture of systems. These include:
在过去的几年里,我们已经看到了关于系统架构的一系列想法,包括:
- Hexagonal Architecture (a.k.a. Ports and Adapters) by Alistair Cockburn and adopted by Steve Freeman, and Nat Pryce in their wonderful bookGrowing Object Oriented Software
- Onion Architecture by Jeffrey Palermo
- Screaming Architecture from a blog of mine last year
- DCI from James Coplien, and Trygve Reenskaug.
- BCE by Ivar Jacobson from his book Object Oriented Software Engineering: A Use-Case Driven Approach
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:
这些架构都像这样的去构造系统:
- 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.
框架独立。架构不依赖于一些满载功能的软件库。这可以让你像使用工具一样使用这样的框架,而不是把系统塞到他们有限的约束之中。
- Testable. The business rules can be tested without the UI, Database, Web Server, or any other external element.
可测试性。业务规则可以在没有UI,数据库,Web服务器,或者其他外部元素的情况下完成测试。
- 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。
- 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,或者其他数据库。
- 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.