Sixteen. Large-Scale Structure
Thousands of people worked independently to create the AIDS Quilt.
数千人分工合作来制作“艾滋病纪念拼被”(AIDS Quilt)
A small Silicon Valley design firm had been contracted to create a simulator for a satellite communications system. Work was progressing well. A MODEL-DRIVEN DESIGN was developing that could express and simulate a wide range of network conditions and failures.
硅谷一家小设计公司签了一份为卫星通信系统创建模拟器的合同。工作进展得很顺利,他们正在开发一个 MODEL-DRIVEN DESIGN,这个设计能够表示和模拟各种网络条件和故障。
But the lead developers on the project were uneasy. The problem was inherently complex. Driven by the need to clarify the intricate relationships in the model, they had decomposed the design into coherent MODULES of manageable size. Now there were a lot of MODULES. Which package should a developer look in to find a particular aspect of functionality? Where should a new class be placed? What did some of these little packages really mean? How did they all fit together? And there was still more to build.
但开发团队的领导者却有点不安。问题本身太复杂了。为了澄清模型中的复杂关系,他们已经把设计分解为一些在规模上便于管理的内聚 MODULE,于是现在便有了的很多 MODULE。在这种情况下,开发人员要想查找某个功能,应该到哪个 MODULE 中去查呢?如果有了一个新类,应该把它放在哪里?这些小软件包的实际意义是什么?它们又是如何协同工作的呢?而且以后还要创建更多的 MODULE。
The developers communicated well with one another and could still figure out what to do from day to day, but the project leaders were not content to skirt the edge of comprehensibility. They wanted some way of organizing the design so that it could be understood and manipulated as it moved to the next level of complexity.
开发人员互相之间仍然能够进行很好的沟通,而且也知道每天都要做什么工作,但项目领导者却不满足这种一知半解的状态。他们需要某种组织设计的方式,以便在项目进入到更复杂的阶段时能够理解和掌控它。
They brainstormed. There were a lot of possibilities. Alternative packaging schemes were proposed. Maybe some document could give an overview of the system, or some new views of the class diagram in the modeling tool could guide a developer to the right MODULE. But the project leaders weren’t satisfied with these gimmicks.
他们进行了头脑风暴活动,发现了很多潜在的办法。开发人员提出了不同的打包方案。有一些文档给出了系统的全貌,还有一些使用建模工具绘制的类图——新视图可以用来指引开发人员找到正确的模块。但项目领导者对这些小花招并不满意。
They could tell a simple story of their simulation, of the way data would be marshaled through an infrastructure, its integrity and routing assured by layers of telecommunications technology. Every detail of that story was in the model, yet the broad arc of the story could not be seen.
他们可以用模型把模拟器的工作流程简单地描述出来,也可以说清楚基础设施是如何序列化数据的,以及电信技术层怎样保证数据的完整性和路由选择。模型中包含了所有细节,却没有一条清楚的主线。
Some essential concept from the domain was missing. But this time it was not a class or two missing from the object model, it was a missing structure for the model as a whole.
领域的一些重要概念丢失了。但这次丢失的不是对象模型中的一两个类,而是整个模型的结构。
After the developers mulled over the problem for a week or two, the idea began to jell. They would impose a structure on the design. The entire simulator would be viewed as a series of layers related to aspects of the communications system. The bottom layer would represent the physical infrastructure, the basic ability to transmit bits from one node to another. Then there would be a packet-routing layer that brought together the concerns of how a particular data stream would be directed. Other layers would identify other conceptual levels of the problem. These layers would outline their story of the system.
经过一两周的仔细思考之后,开发人员有了思路。他们打算把设计放到一个结构中。整个模拟器将被看作由一系列层组成,这些层分别对应于通信系统的各个方面。最下面的层用来表示物理基础设施,它具有将数据位从一个节点传送到另一个节点的基本能力。它的上面是封包路由层,与数据流定向有关的问题都被集中到这一层中。其他的层则表示其他概念层次的问题。这些层共同描述了系统的大致情况。
They set out to refactor the code to conform to the new structure. MODULES had to be redefined so as not to span layers. In some cases, object responsibilities were refactored so that each object would clearly belong to one layer. Conversely, throughout this process the definitions of the conceptual layers themselves were refined based on the hands-on experience of applying them. The layers, MODULES, and objects coevolved until, in the end, the entire design followed the contours of this layered structure.
他们开始按照新的结构来重构代码。为了不让 MODULE 跨越多个层,必须对它们重新定义。在一些情况下,还需要重构对象职责,以便明确地让每个对象只属于一个层。另一方面,藉由应用这些新思路的实际经验,概念层本身的定义也得到了精化。层、MODULE 和对象一起演变,最后,整个设计都符合了这种分层结构的大体轮廓。
These layers were not MODULES or any other artifact in the code. They were an overarching set of rules that constrained the boundaries and relationships of any particular MODULE or object throughout the design, even at interfaces with other systems.
这些层并不是 MODULE,也不是任何其他的代码工件。它们是一种全局性的规则集,用于约束整个设计中的任何 MODULE 或对象(甚至包括与其他系统的接口)的边界和关系。
Imposing this order brought the design back to comfortable intelligibility. People knew roughly where to look for a particular function. Individuals working independently could make design decisions that were broadly consistent with each other. The complexity ceiling had been lifted.
实施了这种分层级别之后,设计重新变得易于理解了。人们基本上知道到哪里去寻找某个特定功能。分工不同的开发人员所做的设计决策可以大体上互相保持一致。这样就可以处理更加复杂的设计了。
Even with a MODULAR breakdown, a large model can be too complicated to grasp. The MODULES chunk the design into manageable bites, but there may be many of them. Also, modularity does not necessarily bring uniformity to the design. Object to object, package to package, a jumble of design decisions may be applied, each defensible but idiosyncratic.
即使将 MODULE 分解,一个大模型的复杂性也可能会使它变得很难掌握。MODULE 确实把设计分解为更易管理的小部分,但 MODULE 的数量可能会很多。此外,模块化并不一定能够保证设计的一致性。对象与对象之间,包与包之间,可能应用了一堆的设计决策,每个决策看起来都合情合理,但总的来看却非常怪异。
The strict segregation imposed by BOUNDED CONTEXTS prevents corruption and confusion, but it does not, in itself, make it easier to see the system as a whole.
严格划分 BOUNDED CONTEXT 可能会防止出现破坏和混淆,但其本身对于从整体上审视系统并无任何助益。
Distillation does help by focusing attention on the CORE DOMAIN and casting other subdomains in their supporting roles. But it is still necessary to understand the supporting elements and their relationships to the CORE DOMAIN—and to each other. And while the CORE DOMAIN would ideally be so clear and easily understood that no additional guidance would be needed, we are not always at that point.
精炼可以帮助我们把注意力集中于 CORE DOMAIN,并将子领域分离出来,让它们承担支持性的职责。但我们仍然需要理解这些支持性元素,以及它们与 CORE DOMAIN 的关系,还有它们互相之间的关系。理想的情况是,整个 CORE DOMAIN 非常清楚和易于理解,因此不再需要额外的指导,但我们并不总能处于这样好的境况中。
On a project of any size, people must work somewhat independently on different parts of the system. Without any coordination or rules, a confusion of different styles and distinct solutions to the same problems arises, making it hard to understand how the parts fit together and impossible to see the big picture. Learning about one part of the design will not transfer to other parts, so the project will end up with specialists in different MODULES who cannot help each other outside their narrow range. CONTINUOUS INTEGRATION breaks down and the BOUNDED CONTEXT fragments.
无论项目的规模如何,人们总需要有各自的分工,来负责系统的不同部分。如果没有任何协调机制或规则,那么相同问题的各种不同风格和截然不同的解决方案就会混杂在一起,使人们很难理解各个部分是如何组织在一起的,也不可能看到整个系统的统一视图。从设计的一个部分学到的东西并不适用于这个设计的其他部分,因此项目最后的结果是开发人员成为各自 MODULE 的专家,一旦脱离了他们自己的小圈子就无法互相帮助。在这种情况下,CONTINUOUS INTEGRATION 根本无法实现,而 BOUNDED CONTEXT 也使项目变得支离破碎。
In a large system without any overarching principle that allows elements to be interpreted in terms of their role in patterns that span the whole design, developers cannot see the forest for the trees. We need to be able to understand the role of an individual part in the whole without delving into the details of the whole.
在一个大的系统中,如果因为缺少一种全局性的原则而使人们无法根据元素在模式(这些模式被应用于整个设计)中的角色来解释这些元素,那么开发人员就会陷入“只见树木,不见森林”的境地。我们需要理解各个部分在整体中的角色,而不必去深究细节。
A “large-scale structure” is a language that lets you discuss and understand the system in broad strokes. A set of high-level concepts or rules, or both, establishes a pattern of design for an entire system. This organizing principle can guide design as well as aid understanding. It helps coordinate independent work because there is a shared concept of the big picture:
“大型结构”是一种语言,人们可以用它来从大局上讨论和理解系统。它用一组高级概念或规则(或两者兼有)来为整个系统的设计建立一种模式。这种组织原则既能指导设计,又能帮助理解设计。另外,它还能够协调不同人员的工作,因为它提供了共享的整体视图,让人们知道各个部分在整体中的角色。
Devise a pattern of rules or roles and relationships that will span the entire system and that allows some understanding of each part’s place in the whole—even without detailed knowledge of the part’s responsibility.
设计一种应用于整个系统的规则(或角色和关系)模式,使人们可以通过它在一定程度上了解各个部分在整体中所处的位置(即使是在不知道各个部分的详细职责的情况下)。
Structure may be confined to one BOUNDED CONTEXT but will usually span more than one, providing the conceptual organization to hold together all the teams and subsystems involved in the project. A good structure gives insight into the model and complements distillation.
这种结构可以被限制在一个 BOUNDED CONTEXT 中,但通常情况下它会跨越多个 BOUNDED CONTEXT,并通过提供一种概念组织把项目涉及的所有团队和子系统紧密结合到一起。好的结构可以帮助人们深入地理解模型,还能够对精炼起到补充作用。
You can’t represent most large-scale structures in UML, and you don’t need to. Most large-scale structures shape and explain the model and design but do not appear in it. They provide an extra level of communication about the design. In the examples of this chapter, you’ll see many informal UML diagrams on which I’ve superimposed information about the large-scale structure.
大部分大型结构都无法用 UML 来表示,而且也不需要这样做。这些大型结构是用来勾画和解释模型及设计的,但在设计中并不出现,它们只是用来表达设计的另外一种方式。在本章的示例中,你将看到许多添加了大型结构信息的非正式的 UML 图。
When a team is reasonably small and the model is not too complicated, decomposition into well-named MODULES, a certain amount of distillation, and informal coordination among developers can be sufficient to keep the model organized.
当团队规模较小而且模型也不太复杂时,只需将模型分解为合理命名的 MODULE,再进行一定程度的精炼,然后在开发人员之间进行非正式的协调,以上这些就足以使模型保持良好的组织结构了。
Large-scale structure can save a project, but an ill-fitting structure can severely hinder development. This chapter explores patterns for successfully structuring a design at this level.
大型结构可以节省项目的开发费用,但不适当的结构会严重妨碍开发的进展。本章将探讨一些能成功构建这种设计结构的模式。
Some patterns of large-scale structure
Many developers have experienced the cost of an unstructured design. To avoid anarchy, projects impose architectures that constrain development in various ways. Some technical architectures do solve technical problems, such as networking or data persistence, but when architectures start venturing into the arena of the application and domain model, they can create problems of their own. They often prevent the developers from creating designs and models that work well for the specifics of the problem. The most ambitious ones can even take away from application developers the familiarity and technical power of the programming language itself. And whether technical or domain oriented, architectures that freeze a lot of up-front design decisions can become a straitjacket as requirements change and as understanding deepens.
很多开发人员都亲身经历过由于设计结构混乱而产生的代价。为了避免混乱,项目通过架构从各个方面对开发进行约束。一些技术架构确实能够解决技术问题,如网络或数据持久化问题,但当我们在应用层和领域模型中使用架构时,它们可能会产生自己的问题。它们往往会妨碍开发人员创建适合于解决特定问题的设计和模型。一些要求过高的架构甚至会妨碍编程语言本身的使用,导致应用程序开发人员根本无法使用他们在编程语言中最熟悉的和技术能力很强的一些功能。而且,无论架构是面向技术的,还是面向领域的,如果其限定了很多前期设计决策,那么随着需求的变更和理解的深入,这些架构会变得束手束脚。
While some technical architectures (such as J2EE) have become prominent over the years, large-scale structure in the domain layer has not been explored much. Needs vary widely from one application to the next.
近年来,一些技术架构(如 J2EE)已经成为主流技术,而人们对领域层中的大型结构却没有做多少研究,这是因为应用程序不同,其各自的需求也大为不同。
An up-front imposition of a large-scale structure is likely to be costly. As development proceeds, you will almost certainly find a more suitable structure, and you may even find that the prescribed structure is prohibiting you from taking a design route that would greatly clarify or simplify the application. You may be able to use some of the structure, but you’re forgoing opportunities. Your work slows down as you try workarounds or try to negotiate with the architects. But your managers think the architecture is done. It was supposed to make this application easy, so why aren’t you working on the application instead of dealing with all these architecture problems? The managers and architecture teams may even be open to input, but if each change is a heroic battle, it is too exhausting.
在项目前期使用大型结构可能需要很大的成本。随着开发的进行,我们肯定会发现更适当的结构,甚至会发现先前使用的结构妨碍了我们采取一种使应用程序更清晰和简化的路线。这种结构的一部分是有用的,但却使你失去了其他很多机会。你的工作会慢下来,因为你要寻找解决的办法或试着与架构师们进行协商。但经理会认为架构已经定下来了,当初选这个架构就是因为它能够使应用程序变得简单一些,那为什么不去开发应用程序,却在这些架构问题上纠缠不清呢?即使经理和架构团队能够接受这些问题,但如果每次修改都像是一场攻坚战,那么人们很快就会疲乏不堪。
Design free-for-alls produce systems no one can make sense of as a whole, and they are very difficult to maintain. But architectures can straitjacket a project with up-front design assumptions and take too much power away from the developers/designers of particular parts of the application. Soon, developers will dumb down the application to fit the structure, or they will subvert it and have no structure at all, bringing back the problems of uncoordinated development.
一个没有任何规则的随意设计会产生一些无法理解整体含义且很难维护的系统。但架构中早期的设计假设又会使项目变得束手束脚,而且会极大地限制应用程序中某些特定部分的开发人员/设计人员的能力。很快,开发人员就会为适应结构而不得不在应用程序的开发上委曲求全,要么就是完全推翻架构而又回到没有协调的开发老路上来。
The problem is not the existence of guiding rules, but rather the rigidity and source of those rules. If the rules governing the design really fit the circumstances, they will not get in the way but actually push development in a helpful direction, as well as provide consistency.
问题并不在于指导规则本身应不应该存在,而在于这些规则的严格性和来源。如果这些用于控制设计的规则确实符合开发环境,那么它们不但不会阻碍开发,而且还会推动开发在健康的方向上前进,并且保持开发的一致性。
Therefore:
因此:
Let this conceptual large-scale structure evolve with the application, possibly changing to a completely different type of structure along the way. Don’t overconstrain the detailed design and model decisions that must be made with detailed knowledge.
让这种概念上的大型结构随着应用程序一起演变,甚至可以变成一种完全不同的结构风格。不要依此过分限制详细的设计和模型决策,这些决策和模型决策必须在掌握了详细知识之后才能确定。
Individual parts have natural or useful ways of being organized and expressed that may not apply to the whole, so imposing global rules makes these parts less ideal. Choosing to use a large-scale structure favors manageability of the model as a whole over optimal structuring of the individual parts. Therefore, there will be some compromise between unifying structure and freedom to express individual components in the most natural way. This can be mitigated by selecting the structure based on actual experience and knowledge of the domain and by avoiding over-constrictive structures. A really nice fit of structure to domain and requirements actually makes detailed modeling and design easier, by helping to quickly eliminate a lot of options.
有时个别部分具有一些很自然且有用的组织和表示方式,但这些方式并不适用于整体,因此施加全局规则会使这些部分的设计不够理想。在选择大型结构时,应该侧重于整体模型的管理,而不是优化个别部分的结构。因此,在“结构统一”和“用最自然的方式表示个别组件”之间需要做出一些折中选择。根据实际经验和领域知识来选择结构,并避免采用限制过多的结构,如此可以降低折中的难度。真正适合领域和需求的结构能够使细节的建模和设计变得更容易,因为它快速排除了很多选项。
The structure can also give shortcuts to design decisions that could, in principle, be found by working on the individual object level, but would, in practice, take too long and have inconsistent results. Of course, continuous refactoring is still necessary, but this will make it a more manageable process and can help make different people come up with consistent solutions.
大型结构还能够为我们做设计决策提供捷径,虽然原则上也可以通过研究各个对象来做出这些决策,但实际上这会耗费太长时间,而且产生的结果可能不一致。当然,持续重构仍然是必要的,但这种结构可以帮助重构变得更易于管理,并使不同的人能够得到一致的解决方案。
A large-scale structure generally needs to be applicable across BOUNDED CONTEXTS. Through iteration on a real project, a structure will lose features that tightly bind it to a particular model and evolve features that correspond to CONCEPTUAL CONTOURS of the domain. This doesn’t mean that it will have no assumptions about the model, but it will not impose upon the entire project ideas tailored to a particular local situation. It has to leave freedom for development teams in distinct CONTEXTS to vary the model in ways that address their local needs.
大型结构通常需要跨越 BOUNDED CONTEXT 来使用。在经历了实际项目上的迭代之后,结构将失去与特定模型紧密联系的特性,也会得到符合领域的 CONCEPTUAL CONTOUR 的特性。这并不意味着它不能对模型做出任何假设,而是说它不会把专门针对局部情况而做的假设强加于整个项目。它应该为那些在不同 CONTEXT 中工作的开发团队保留一定的自由,允许他们为了满足局部需要而修改模型。
Also, large-scale structures must accommodate practical constraints on development. For example, designers may have no control over the model of some parts of the system, especially in the case of external or legacy subsystems. This could be handled by changing the structure to better fit the specific external elements. It could be handled by specifying ways in which the application relates to externals. It might be handled by making the structure loose enough to flex around awkward realities.
此外,大型结构必须适应开发工作中的实际约束。例如,设计人员可能无法控制系统的某些部分的模型,特别是外部子系统或遗留子系统。这个问题有多种解决方式,如修改结构使之更适应特定外部元素,或者指定应用程序与外部元素的关联方式,或者使结构变得足够松散,以灵活应对难以处理的现实情况。
Unlike the CONTEXT MAP, a large-scale structure is optional. One should be imposed when costs and benefits favor it, and when a fitting structure is found. In fact, it is not needed for systems that are simple enough to be understood when broken into MODULES. Large-scale structure should be applied when a structure can be found that greatly clarifies the system without forcing unnatural constraints on model development. Because an ill-fitting structure is worse than none, it is best not to shoot for comprehensiveness, but rather to find a minimal set that solves the problems that have emerged. Less is more.
与 CONTEXT MAP 不同的是,大型结构是可选的。当使用某种结构可以节省成本并带来益处时,并且发现了一种适当的结构,就应该使用它。实际上,如果一个系统简单到把它分解为 MODULE 就足以理解它,那么就不必使用这种结构了。当发现一种大型结构可以明显使系统变得更清晰,而又没有对模型开发施加一些不自然的约束时,就应该采用这种结构。使用不合适的结构还不如不使用它,因此最好不要为了追求设计的完整性而勉强去使用一种结构,而应该找到尽可能精简的方式解决所出现问题。要记住宁缺勿滥的原则。
A large-scale structure can be very helpful and still have a few exceptions, but those exceptions need to be flagged somehow, so that developers can assume the structure is being followed unless otherwise noted. And if those exceptions start to get numerous, the structure needs to be changed or discarded.
大型结构可能非常有帮助,但也有少数不适用的情况,这些例外情况应该以某种方式被标记出来,以便让开发人员知道在没有特殊注明时可以遵循这种结构。如果不适用的情况开始大量出现,就要修改这种结构了,或者干脆不用它。
As mentioned, it is no mean feat to create a structure that gives the necessary freedom to developers while still averting chaos. Although a lot of work has been done on technical architecture for software systems, little has been published on the structuring of the domain layer. Some approaches weaken the object-oriented paradigm, such as those that break down the domain by application task or by use case. This whole area is still undeveloped. I’ve observed a few general patterns of large-scale structures that have emerged on various projects. I’ll discuss four in this chapter. One of these may fit your needs or lead to ideas for a structure tailored to your project.
如前所述,要想创建一种既为开发人员保留必要自由度同时又能保证开发工作不会陷入混乱的结构绝非易事。尽管人们已经在软件系统的技术架构上投入了大量工作,但有关领域层的结构化研究还很少见。一些方法会破坏面向对象的范式,如那些按应用任务或按用例对领域进行分解的方法。整个领域的研究还很贫瘠。我曾经在一些项目上看到过几个通用的大型结构模式。本章将讨论 4 种模式,其中可能会有一种符合你的需要,或者能够为你提供一些思路,从而找到一种适合你的项目的结构。
Metaphorical thinking is pervasive in software development, especially with models. But the Extreme Programming practice of “metaphor” has come to mean a particular way of using a metaphor to bring order to the development of a whole system.
隐喻思维在软件开发(特别是模型)中是很普遍的。但极限编程中的“隐喻”却具有另外一种含义,它用一种特殊的隐喻方式来使整个系统的开发井然有序。
Just as a firewall can save a building from a fire raging through neighboring buildings, a software “firewall” protects the local network from the dangers of the larger networks outside. This metaphor has influenced network architectures and shaped a whole product category. Multiple competing firewalls—developed independently, understood to be somewhat interchangeable—are available for consumers. Novices to networking readily grasp the concept. This shared understanding throughout the industry and among customers is due in no small part to the metaphor.
一栋大楼的防火墙能够在周围发生火灾时防止火势从其他建筑蔓延到它自身,同样,软件“防火墙”可以保护局部网络免受来自更大的外部网的破坏。这个“防火墙”的隐喻对网络架构产生了很大影响,并且由此而产生了一整套产品类别。有多种互相竞争的防火墙可供消费者选择,它们都是独立开发的,而且人们知道它们在一定程度上可以互换。即使网络的初学者也很容易掌握这个概念。这种在整个行业和客户中的共同理解很大一部分上得益于隐喻。
Yet it is an inexact analogy, and its power cuts both ways. The use of the firewall metaphor has led to development of software barriers that are sometimes insufficiently selective and impede desirable exchanges, while offering no protection against threats originating within the wall. Wireless LANs, for example, are vulnerable. The clarity of the firewall has been a boon, but all metaphors carry baggage.1
然而这个类比却并不准确,而且防火墙从功能上来看也是把双刃剑。防火墙的隐喻引导人们开发出了软件屏障,但有时它并不能起到充分的防护作用,而且会阻止正当的数据交换,同时也无法防护来自网络内部的威胁。例如,无线 LAN 就存在漏洞。防火墙这个形象的隐喻确实很有用,但所有隐喻也都是有弊端的。
Software designs tend to be very abstract and hard to grasp. Developers and users alike need tangible ways to understand the system and share a view of the system as a whole.
软件设计往往非常抽象且难于掌握。开发人员和用户都需要一些切实可行的方式来理解系统,并共享系统的一个整体视图。
On one level, metaphor runs so deeply in the way we think that it pervades every design. Systems have “layers” that “lay on top” of each other. They have “kernels” at their “centers.” But sometimes a metaphor comes along that can convey the central theme of a whole design and provide a shared understanding among all team members.
从某种程度上讲,隐喻对人们的思考方式有着深刻地影响,它已经渗透到每个设计中。系统有很多“层”,层与层之间依次叠放起来。系统还有“内核”,位于这些层的“中心”。但有时隐喻可以传达整个设计的中心主题,并能够在团队所有成员中形成共同理解。
When this happens, the system is actually shaped by the metaphor. A developer will make design decisions consistent with the system metaphor. This consistency will enable other developers to interpret the many parts of a complex system in terms of the same metaphor. The developers and experts have a reference point in discussions that may be more concrete than the model itself.
在这种情况下,系统实际上就是由这个隐喻塑造的。开发人员所做的设计决策也将与系统隐喻保持一致。这种一致性使其他开发人员能够根据同一个隐喻来解释复杂系统中的多个部分。开发人员和专家在讨论时有一个比模型本身更具体的参考点。
A SYSTEM METAPHOR is a loose, easily understood, large-scale structure that it is harmonious with the object paradigm. Because the SYSTEM METAPHOR is only an analogy to the domain anyway, different models can map to it in an approximate way, which allows it to be applied in multiple BOUNDED CONTEXTS, helping to coordinate work between them.
SYSTEM METAPHOR(系统隐喻)是一种松散的、易于理解的大型结构,它与对象范式是协调的。由于系统隐喻只是对领域的一种类比,因此不同模型可以用近似的方式来与它关联,这使得人们能够在多个 BOUNDED CONTEXT 中使用系统隐喻,从而有助于协调各个 BOUNDED CONTEXT 之间的工作。
SYSTEM METAPHOR has become a popular approach because it is one of the core practices of Extreme Programming (Beck 2000). Unfortunately, few projects have found really useful METAPHORS, and people have tried to push the idea into domains where it is counterproductive. A persuasive metaphor introduces the risk that the design will take on aspects of the analogy that are not desirable for the problem at hand, or that the analogy, while seductive, may not be apt.
SYSTEM METAPHOR 是极限编程的核心实践之一,因此它已经成为一种非常流行的方法(Beck2000)。遗憾的是,很少有项目能够找到真正有用的 METAPHOR,而且人们有时还会把一些起反作用的隐喻思想灌输到领域中。有时使用太强的隐喻反而会有风险,因为它使设计中掺杂了一些与当前问题无关的类比,或者是类比虽然很有吸引力,但它本身并不恰当。
That said, SYSTEM METAPHOR is a well-known form of large-scale structure that is useful on some projects, and it nicely illustrates the general concept of a structure.
尽管如此,SYSTEM METAPHOR 仍然是众所周知的大型结构,它对一些项目非常有用,而且很好地说明了结构的总体概念。
Therefore:
因此:
When a concrete analogy to the system emerges that captures the imagination of team members and seems to lead thinking in a useful direction, adopt it as a large-scale structure. Organize the design around this metaphor and absorb it into the UBIQUITOUS LANGUAGE. The SYSTEM METAPHOR should both facilitate communication about the system and guide development of it. This increases consistency in different parts of the system, potentially even across different BOUNDED CONTEXTS. But because all metaphors are inexact, continually reexamine the metaphor for overextension or inaptness, and be ready to drop it if it gets in the way.
当系统的一个具体类比正好符合团队成员对系统的想象,并且能够引导他们向着一个有用的方向进行思考时,就应该把这个类比用作一种大型结构。围绕这个隐喻来组织设计,并把它吸收到 UBIQUITOUS LANGUAGE 中。SYSTEM METAPHOR 应该既能促进系统的交流,又能指导系统的开发。它可以增加系统不同部分之间的一致性,甚至可以跨越不同的 BOUNDED CONTEXT。但所有隐喻都不是完全精确的,因此应不断检查隐喻是否过度或不恰当,当发现它起到妨碍作用时,要随时准备放弃它。
The “Naive Metaphor” and Why We Don’t Need It
“幼稚隐喻”以及我们为什么不需要它
Because a useful metaphor doesn’t present itself on most projects, some in the XP community have come to talk of the naive metaphor, by which they mean the domain model itself.
由于在大多数项目并不会自动出现有用的隐喻,因此 XP 社区中的一些人开始谈论“幼稚隐喻”(Naive Metaphor),他们所说的幼稚隐喻就是领域模型本身。
One trouble with this term is that a mature domain model is anything but naive. In fact, “payroll processing is like an assembly line” is likely a much more naive view than a model that is the product of many iterations of knowledge crunching with domain experts, and that has been proven by being tightly woven into the implementation of a working application.
这个术语的一个问题在于,一个成熟的领域模型绝对不会是“幼稚的”。实际上,“工资处理就像一条装配线”这个隐喻与模型的实际情况相比要幼稚得多,因为模型是软件开发人员与领域专家进行了多次知识消化的迭代过程才得到的,它已经紧密结合到应用程序的实现中,并经过了实践的检验。
The term naive metaphor should be retired.
“幼稚隐喻”这个术语应该停止使用了。
SYSTEM METAPHORS are not useful on all projects. Large-scale structure in general is not essential. In the 12 practices of Extreme Programming, the role of a SYSTEM METAPHOR could be fulfilled by a UBIQUITOUS LANGUAGE. Projects should augment that LANGUAGE with SYSTEM METAPHORS or other large-scale structures when they find one that fits well.
SYSTEM METAPHOR 并不适用于所有项目。从总体上讲,大型结构并不是必须要用的。在极限编程的 12 个实践中,SYSTEM METAPHOR 的角色可以由 UBIQUITOUS LANGUAGE 来承担。当项目中发现一种非常合适的 SYSTEM METAPHOR 或其他大型结构时,应该用它来补充 UBIQUITOUS LANGUAGE。
Throughout this book, individual objects have been assigned narrow sets of related responsibilities. Responsibility-driven design also applies to larger scales.
在本书从头至尾的讨论中,单独的对象被分配了一组相关的、范围较窄的职责。职责驱动的设计在更大的规模上也适用。
When each individual object has handcrafted responsibilities, there are no guidelines, no uniformity, and no ability to handle large swaths of the domain together. To give coherence to a large model, it is useful to impose some structure on the assignment of those responsibilities.
如果每个对象的职责都是人为分配的,将没有统一的指导原则和一致性,也无法把领域作为一个整体来处理。为了保持大模型的一致,有必要在职责分配上实施一定的结构化控制。
When you gain a deep understanding of a domain, broad patterns start to become visible. Some domains have a natural stratification. Certain concepts and activities take place against a background of other elements that change independently and at a different rate for different reasons. How can we take advantage of this natural structure, make it more visible and useful? This stratification suggests layering, one of the most successful architectural design patterns (Buschmann et al. 1996, among others).
当对领域有了深入的理解后,大的模式会变得清晰起来。一些领域具有自然的层次结构。某些概念和活动处在其他元素形成的一个大背景下,而那些元素会因不同原因且以不同频率独立发生变化。如何才能充分利用这种自然结构,使它变得更清晰和有用呢?这种自然的层次结构使我们很容易想到把领域分层,这是最成功的架构设计模式之一([Buschmann et al. 1996]等)。
Layers are partitions of a system in which the members of each partition are aware of and are able to use the services of the layers “below,” but unaware of and independent of the layers “above.” When the dependencies of MODULES are drawn, they are often laid out so that a MODULE with dependents appears below its dependents. In this way, layers sometimes sort themselves out so that none of the objects in the lower levels is conceptually dependent on those in higher layers.
所谓的层,就是对系统进行划分,每个层的元素都知道或能够使用在它“下面”的那些层的服务,但却不知道它“上面”的层,而且与它上面的层保持独立。当我们把 MODULE 的依赖性画出来时,图的布局通常是具有依赖性的 MODULE 出现在它所依赖的模块上面。按照这种方式,可以将各层的顺序梳理出来,最终,低层中的对象在概念上不依赖于高层中的对象。
But this ad hoc layering, while it can make tracing dependencies easier—and sometimes makes some intuitive sense—doesn’t give much insight into the model or guide modeling decisions. We need something more intentional.
这种自发的分层方式虽然使跟踪依赖性变得更容易,而且有时具有一定的直观意义,但它对模型的理解并没有多大的帮助,也不能指导建模决策。我们需要一种具有更明确目的的分层方式。
Ad hoc layering: What are these packages about?
In a model with a natural stratification, conceptual layers can be defined around major responsibilities, uniting the two powerful principles of layering and responsibility-driven design.
在一个具有自然层次结构的模型中,可以围绕主要职责进行概念上的分层,这样可以把分层和职责驱动的设计这两个强有力的原则结合起来使用。
These responsibilities must be considerably broader than those typically assigned to individual objects, as examples will illustrate shortly. As individual MODULES and AGGREGATES are designed, they are factored to keep them within the bounds of one of these major responsibilities. This named grouping of responsibilities by itself could enhance the comprehensibility of a modularized system, since the responsibilities of MODULES could be more readily interpreted. But combining high-level responsibilities with layering gives us an organizing principle for a system.
这些职责必须比分配给单个对象的职责广泛得多才行,我们稍后就会举例说明这一点。当设计单独的 MODULE 和 AGGREGATE 时,要将其限定在其中一个主要职责上。这种明确的职责分组可以提高模块化系统的可理解性,因为 MODULE 的职责会变得更易于解释。而高层次的职责与分层的结合为我们提供了一种系统的组织原则。
The layering pattern that serves best for RESPONSIBILITY LAYERS is the variant called RELAXED LAYERED SYSTEM (Buschmann et al. 1996, p. 45), which allows components of a layer to access any lower layer, not just the one immediately below.
分层模式有一种变体最适合按职责来分层,我们把这种变体称为 RELAXED LAYERED SYSTEM(松散分层系统)[Buschmann et al. 1996, p. 45],如果采用这种分层模式,某一层中的组件可以访问任何比它低的层,而不限于只能访问直接与它相邻的下一层。
Therefore:
因此:
Look at the conceptual dependencies in your model and the varying rates and sources of change of different parts of your domain. If you identify natural strata in the domain, cast them as broad abstract responsibilities. These responsibilities should tell a story of the high-level purpose and design of your system. Refactor the model so that the responsibilities of each domain object, AGGREGATE, and MODULE fit neatly within the responsibility of one layer.
注意观察模型中的概念依赖性,以及领域中不同部分的变化频率和变化的原因。如果在领域中发现了自然的层次结构,就把它们转换为宽泛的抽象职责。这些职责应该描述系统的高层目的和设计。对模型进行重构,使得每个领域对象、AGGREGATE 和 MODULE 的职责都清晰地位于一个职责层当中。
This is a pretty abstract description, but it will become clear with a few examples. The satellite communications simulator whose story opened this chapter layered its responsibility. I have seen RESPONSIBILITY LAYERS used to good effect in domains as various as manufacturing control and financial management.
The following example explores RESPONSIBILITY LAYERS in detail to give a feel for the discovery of a large-scale structure of any sort, and the way it guides and constrains modeling and design.
这是一段很抽象的描述,但通过几个示例就可以把它说清楚了。本章开头的卫星通信模拟器就对职责进行了分层。我曾经在各种领域(如生产控制和财务管理)中看到过使用 RESPONSIBILITY LAYER(职责层)所产生的良好效果。
Example: In Depth: Layering a Shipping System
示例深入研究运输系统的分层
Let’s look at the implications of applying RESPONSIBILITY LAYERS to the cargo shipping application discussed in the examples of previous chapters.
让我们看一下把 RESPONSIBILITY LAYER 应用于前面几章所讨论的货运应用程序会有什么效果。
As we rejoin the story, the team has made considerable progress creating a MODEL-DRIVEN DESIGN and distilling a CORE DOMAIN. But as the design fleshes out, they are having trouble coordinating how all the parts fit together. They are looking for a large-scale structure that can bring out the main themes of their system and keep everyone on the same page.
当我们现在又回到这个应用程序时,开发团队已经有了很大的进展,他们已经创建了一个 MODEL-DRIVEN DESIGN,并且提炼出了一个 CORE DOMAIN。但随着设计变得充实,他们在如何把所有部分协调为一个整体上遇到了麻烦。他们正在寻找一种能够显示出整个系统主题并且让每个人都达成一致看法的大型结构。
Here is a look at a representative part of the model.
我们来看一下这个模型中有代表性的一个部分,如图 16-3 和图 16-4 所示。
A basic shipping domain model for routing cargoes
Using the model to route a cargo during booking
The team members have been steeped in the domain of shipping for months, and they have noticed some natural stratification of its concepts. It is quite reasonable to discuss transport schedules (the scheduled voyages of ships and trains) without referring to the cargoes aboard those transports. It is harder to talk about tracking a cargo without referring to the transport carrying it. The conceptual dependencies are pretty clear. The team can readily distinguish two layers: “Operations” and the substrate of those operations, which they dub “Capability.”
团队成员研究运输领域已经有好几个月了,并且已经观察到了一些自然的概念层次结构。他们发现在讨论运输时间表(安排好的货轮航次或火车班次)时不需要涉及所运输的货物。而当讨论对一个货物的跟踪时,如果不知道它的运输信息,那么就很难进行跟踪。概念依赖性是非常清楚的。团队很容易就区分出两个层:“作业”层和这些作业的基础层(他们把这个层叫做“能力”层)。
“Operational” Responsibilities
“作业”职责
Activities of the company, past, current, and planned, are collected into the Operations layer. The most obvious Operations object is Cargo, which is the focus of most of the day-to-day activity of the company. The Route Specification is an integral part of Cargo, indicating delivery requirements. The Itinerary is the operational delivery plan. Both of these objects are part of the Cargo’s AGGREGATE, and their life cycles are tied to the time frame of an active delivery.
公司的活动,无论是过去、现在还是计划的活动,都被组织到“作业”层中。最明显的作业对象是 Cargo,它是公司大部分日常活动的焦点。Route Specification 是 Cargo 的一个不可缺少的部分,它规定了运输需求。Itinerary 是运输计划。这些对象都是 Cargo 聚合的一部分,它们的生命周期与一次进行中的运输活动紧密地联系在一起。
“Capability” Responsibilities
“能力”职责
This layer reflects the resources the company draws upon in order to carry out operations. The Transit Leg is a classic example. The ships are scheduled to run and have a certain capacity to carry cargo, which may or may not be fully utilized.
这个层反映了公司在执行作业时所能利用的资源。Transit Leg 就是一个典型的例子。人们为货轮制定航程时间表,货轮具有一定的货运能力,这个能力有可能被完全利用,也有可能未被完全利用。
True, if we were focused on operating a shipping fleet, Transit Leg would be in the Operations layer. But the users of this system aren’t worried about that problem. (If the company were involved in both those activities and wanted the two coordinated, the development team might have to consider a different layering scheme, perhaps with two distinct layers, such as “Transport Operations” and “Cargo Operations.”)
当然,如果公司的主要业务是经营一个运输船队的话,那么 Transit Leg 将是作业层中的一员。但这个系统的用户并不需要关心这个问题(如果公司同时从事经营船队和经营货运这两种业务,并且希望协调它们,那么开发团队可能需要考虑不同的分层方案,或许要把作业层分成两个不同的层,如“运输作业”和“货物作业”。)
A trickier decision is where to place Customer. In some businesses, customers tend to be transient: they’re interesting while a package is being delivered and then mostly forgotten until next time. This quality would make customers only an operational concern for a parcel delivery service aimed at individual consumers. But our hypothetical shipping company tends to cultivate long-term relationships with customers, and most work comes from repeat business. Given these intentions of the business users, the Customer belongs in the potential layer. As you can see, this was not a technical decision. It was an attempt to capture and communicate knowledge of the domain.
一个稍微复杂一点儿的决策是把 Customer 放在哪里。在一些企业中,客户只是一些临时对象。例如,在邮递公司中,只有在投递包裹的时候,才需要知道客户对象,投递完成之后,大部分客户就被忘记了,直到出现下一次投递。这种性质决定了在针对个人客户的包裹投递服务中,客户仅仅与作业相关。但在我们假想的这家运输公司中,需要与客户保持长期关系,而且大部分业务都来自回头客。考虑到企业用户的这些意图,Customer 应该属于“能力”层。正如我们看到的,这并非一个技术决策,而是试图掌握并交流领域知识。
Because the association between Cargo and Customer can be traversed in only one direction, the Cargo REPOSITORY will need a query that finds all Cargoes for a particular Customer. There were good reasons to design it that way anyway, but with the imposition of the large-scale structure, it is now a requirement.
由于 Cargo 与 Customer 之间的关联可以限定在一个遍历方向,因此 Cargo REPOSITORY 需要通过一个查询来查找某个特定 Customer 的所有 Cargo。不管怎样,按照这种方式来设计都有很好的理由,但在使用了大型结构以后,现在它变成一项必须要满足的需求了。
A query replaces a bidirectional association that violates the layering.
A first-pass layered model
While the distinction between Operations and Capability clarifies the picture, order continues to evolve. After a few weeks of experimentation, the team zeroes in on another distinction. For the most part, both initial layers focus on situations or plans as they are. But the Router (and many other elements excluded from this example) isn’t part of current operational realities or plans. It helps make decisions about changing those plans. The team defines a new layer responsible for “Decision Support.”
虽然作业层与能力层的区别使这张图看上去很清楚了,但次序仍需要进一步细化。经过几个星期的实验之后,团队将注意力集中在另一个特性上。在很大程度上,最初的两个层主要考虑的是当前的情况或计划。但 Router(以及其他很多未在图中画出的元素)并不是当前的作业或计划的一部分。它是用来帮助修改这些计划的。因此团队定义了一个新的层,让它来负责决策支持(Decision Support)。
“Decision Support” Responsibilities
“决策支持”职责层
This layer of the software provides the user with tools for planning and decision making, and it could potentially automate some decisions (such as automatically rerouting Cargoes when a transport schedule changes).
软件的这个层为用户提供了用于制定计划和决策的工具,它具有自动制定一些决策的潜能(例如,当运输时间表发生变动时,自动重新制定运送 Cargo 的路线)。
The Router is a SERVICE that helps a booking agent choose the best way to send a Cargo. This places the Router squarely in Decision Support.
Router 是一个 SERVICE,能帮助预订代理(booking agent)选择运送货物的最佳路线。因此 Router 明显属于决策支持层。
The references within this model are all consistent with the three layers except for one discordant element: the “is preferred” attribute on Transport Leg. This attribute exists because the company prefers to use its own ships when it can, or the ships of certain other companies with which it has favorable contracts. The “is preferred” attribute is used to bias the Router toward these favored transports. This attribute has nothing to do with “Capability.” It is a policy that directs decision making. To use the new RESPONSIBILITY LAYERS, the model will have to be refactored.
现在模型中的元素基本上都按照这 3 个层来组织了,唯一例外的是 Transport Leg 的“ispreferred”属性。这个属性存在的原因是公司希望在可能的情况下优先使用自己的货轮,或者是那些签订了优惠合同的公司的货轮。is preferred 属性用于使 Router 优先选择这些首选的运输工具。这个属性与“能力层”毫无关系。它是一个用于指导决策制定的策略。为了使用新的 RESPONSIBILITY LAYER,需要对模型进行重构。
Refactoring the model to conform to the new layering structure
This factoring makes the Route Bias Policy more explicit while making Transport Leg more focused on the fundamental concept of transportation capability. A large-scale structure based on a deep understanding of the domain will often push the model in directions that clarify its meaning.
这次重构使 Route Bias Policy 变得更清楚,同时使得 Transport Leg 更专注于运输能力的基本概念。基于对领域的深刻理解而发现的大比例结构总是能够使模型更清楚地表达其含义。
This new model now smoothly fits into the large-scale structure.
现在,这个新模型更加符合大比例结构了。如图 16-8 所示。
The restructured and refactored model
A developer accustomed to the chosen layers can more readily discern the roles and dependencies of the parts. The value of the large-scale structure increases as the complexity grows.
开发人员在熟悉了选定的分层结构后,很容易区分出各个部分的角色和依赖关系。大比例结构的价值随着复杂度的增加而增加。
Note that although I’m illustrating this example with a modified UML diagram, the drawing is just a way of communicating the layering. UML doesn’t include this notation, so this is additional information imposed for the sake of the reader. If code is the ultimate design document for your project, it would be helpful to have a tool for browsing classes by layer or at least for reporting them by layer.
注意,虽然我使用了一个修改后的 UML 图来演示这个例子,但这只是为了表示分层而使用的一种方式。UML 中并没有这种表示法,因此这些是作为额外的信息加上去的,目的是让读者看得更清楚。如果在你的项目中,代码就是最终的设计文档,那么最好可以使用一种可以按层查看类(或至少按照层来报告与这些类有关的信息)的工具。
How Does This Structure Affect Ongoing Design?
大比例结构如何影响后续设计
Once a large-scale structure has been adopted, subsequent modeling and design decisions must take it into account. To illustrate, suppose that we must add a new feature to this already layered design. The domain experts have just told us that routing restrictions apply for certain categories of hazardous materials. Certain materials may not be allowed on some transports or in some ports. We have to make the Router obey these regulations.
一旦采用了一种大比例结构,后续的建模和设计决策就必须要把它考虑在内。为了说明这一点,假设我们必须在这个已分层的设计中增加一个新特性。领域专家们刚刚告诉我们一些针对特定类别危险品的航线约束。有些危险品在某些货轮或港口上是禁止装载的。我们必须使 Router 遵守这些规则。
There are many possible approaches. In the absence of a large-scale structure, one appealing design would be to give the responsibility of incorporating these routing rules to the object that owns the Route Specification and the Hazardous Material (HazMat) code—namely the Cargo.
有很多可行的方法。在未使用大比例结构时,一种吸引人的设计方法是让拥有 Route Specification 和 Hazardous Material(HazMat)代码的对象负责把这些航线规则加进来,这个对象就是 Cargo。
A possible design for routing hazardous cargo
The trouble is, this design doesn’t fit the large-scale structure. The HazMat Route Policy Service is not the problem; it fits neatly into the responsibility of the Decision Support layer. The problem is the dependency of Cargo (an Operational object) on HazMat Route Policy Service (a Decision Support object). As long as the project is committed to these layers, this model cannot be allowed. It would confuse developers who expected the structure to be followed.
问题是这种设计并不适合大比例结构。HazMat Route Policy Service 并没有问题,它非常适合承担决策支持层的职责。问题在于 Cargo(一个作业层对象)对 HazMat Route Policy Service(一个决策支持层对象)的依赖上。只要项目还采用目前的分层,就不能使用这个模型,因为开发人员会认为设计将遵循分层结构,而这种依赖会使开发人员感到糊涂。
There are always many design possibilities, and we’ll just have to choose another one—one that follows the rules of the large-scale structure. The HazMat Route Policy Service is all right, but we need to move the responsibility for using the policy. Let’s try giving the Router the responsibility for collecting appropriate policies before searching for a route. This means changing the Router interface to include objects that policies might depend on. Here is a possible design.
可能的设计选择总会有很多,这里我们只选择另外一种设计,这种设计符合大比例结构的规则。HazMat Route Policy 服务本身是完全没有问题的,但我们需要把使用它的职责转移到别处。让我们尝试让 Router 来承担在搜索航线之前收集相关规则的职责。这意味着要修改 Router 接口,把规则可能依赖的那些对象包括进来。下面就是一种可能的设计,如图 16-11 所示。
A design consistent with layering
A typical interaction is shown in Figure 16.12 on the next page.
一种典型的交互如图 16-12 所示。
Now, this isn’t necessarily a better design than the other. They both have pros and cons. But if everyone on a project makes decisions in a consistent way, the design as a whole will be much more comprehensible, and that is worth some modest trade-offs on detailed design choices.
现在的这个设计并不一定就比前面那个设计更好。二者都是各有利弊。但如果项目的所有人员都采用一致的方式来制定决策,那么整体的设计就更容易理解,因此这也值得在细小的设计选择上做出一些适度的折中。
If the structure is forcing many awkward design choices, then in keeping with EVOLVING ORDER, it should be evaluated and perhaps modified or even discarded.
如果所采用的结构强制性地要求我们做出很多别扭的设计选择,那么就要遵循 EVOLVING ORDER(演变的顺序),在项目进行过程中评估这种结构,并修改甚至放弃它。
Choosing Appropriate Layers
选择适当的层
Finding good RESPONSIBILITY LAYERS, or any large-scale structure, is a matter of understanding the problem domain and experimenting. If you allow EVOLVING ORDER, the initial starting point is not critical, although a poor choice does add work. The structure may well evolve into something unrecognizable. So the guidelines suggested here should be applied when considering transformations of the structure as much as when choosing from scratch.
要想找到一种适当的 RESPONSIBILITY LAYER 或大比例结构,需要理解问题领域并反复进行实验。如果遵循 EVOLVING ORDER,那么最初的起点并不是十分重要,尽管差劲的选择确实会加大工作量。结构可能最后演变得面目全非。因此,下面将给出一些指导方针,无论是刚开始选择一种结构,还是对已有结构进行转换,这些指导方针都适用。
As layers get switched out, merged, split, and redefined, here are some useful characteristics to look for and preserve.
当对层进行删除、合并、拆分和重新定义等操作时,应寻找并保留以下一些有用的特征。
- Storytelling. The layers should communicate the basic realities or priorities of the domain. Choosing a large-scale structure is less a technical decision than a business modeling decision. The layers should bring out the priorities of the business.
- Conceptual dependency. The concepts in the “upper” layers should have meaning against the backdrop of the “lower” layers, while the lower-layer concepts should be meaningful standing alone.
- CONCEPTUAL CONTOURS. If the objects of different layers should have different rates of change or different sources of change, the layer accommodates the shearing between them.
- 场景描述。层应该能够表达出领域的基本现实或优先级。选择一种大比例结构与其说是一种技术决策,不如说是一种业务建模决策。层应该显示出业务的优先级。
- 概念依赖性。“较高”层概念的意义应该依赖“较低”层,而低层概念的意义应该独立于较高的层。
- CONCEPTUAL CONTOUR。如果不同层的对象必须具有不同的变化频率或原因,那么层应该能够容许它们之间的变化。
It isn’t always necessary to start from scratch in defining layers for each new model. Certain layers show up in whole families of related domains.
在为每个新模型定义层时不一定总要从头开始。在一系列相关领域中,有些层是固定的。
For example, in businesses based on exploiting large fixed capital assets, such as factories or cargo ships, logistical software can often be organized into a “Potential” layer (another name for the “Capability” layer in the example) and an “Operations” layer.
例如,在那些利用大型固定资产进行运作的企业(如工厂或货运)中,物流软件通常可以被组织为“潜能”层(上面例子中的“能力”层的另外一个名称)和“作业”层。
- Potential. What can be done? Never mind what we are planning to do. What could we do? The resources of the organization, including its people, and the way those resources are organized are the core of the Potential layer. Contracts with vendors also define potentials. This layer could be recognized in almost any business domain, but it is a prominent part of the story in those businesses, such as transportation and manufacturing, that have relatively large fixed capital investments that enable the business. Potential includes transient assets as well, but a business driven primarily by transient assets might choose layers that emphasize this, as discussed later. (This layer was called “Capability” in the example.)
- Operation. What is being done? What have we managed to make of those potentials? Like the Potential layer, this layer should reflect the reality of the situation, rather than what we want it to be. In this layer we are trying to see our own efforts and activities: What we are selling, rather than what enables us to sell. It is very typical of Operational objects to reference or even be composed of Potential objects, but a Potential object shouldn’t reference the Operations layer.
- 潜能层。我们能够做什么?潜能层不关心我们打算做什么,而关心能够做什么。企业的资源(包括人力资源)以及这些资源的组织方式是潜能层的核心。与供应商签订的合同也明确界定了企业的潜能。这个层几乎存在于任何业务领域中,但在那些相对来说依靠大型固定资产来支持业务运作的企业中(如运输和制造业)尤其突出。潜能也包括临时性的资产,但主要依赖临时资产来运作的企业可能会强调临时资产的层(这个层在例子中被称为“Capability”),这一点稍后会讨论。
- 作业层。我们正在做什么?我们利用这些潜能做了什么事情?像潜能层一样,这个层也应该反映出现实状况,而不是我们设想的状况。我们希望在这个层中看到自己的工作和活动:我们正在销售什么,而不是能够销售什么。通常来说,作业层对象可以引用潜能层对象,它甚至可以由潜能层对象组成,但潜能层对象不应该引用作业层对象。
In many, perhaps most, existing systems in domains of this kind, these two layers cover everything (although there could be some entirely different and more revealing breakdown). They track the current situation and active operational plans and issue reports or documents about it. But tracking is not always enough. When projects seek to guide or assist users, or to automate decision making, there is an additional set of responsibilities that can be organized into another layer, above Operations.
在这类领域很多(也许是大部分)现有的系统中,这两个层可以涵盖一切对象(尽管可能会有某种完全不同的和更清晰的分解结构)。它们可以跟踪当前状况和正在执行的作业计划,以及问题报告或相关文档。但跟踪往往是不够的。当项目要为用户提供指导或帮助或者要自动制定一些决策时,就需要有另外一组职责,这些职责可以被组织到作业层之上的决策支持层中。
- Decision Support. What action should be taken or what policy should be set? This layer is for analysis and decision making. It bases its analysis on information from lower layers, such as Potential or Operations. Decision Support software may use historical information to actively seek opportunities for current and future operations.
- 决策支持层。应该采取什么行动或制定什么策略?这个层是用来作出分析和制定决策的。它根据来自较低层(如潜能层或作业层)的信息进行分析。决策支持软件可以利用历史信息来主动寻找适用于当前和未来作业的机会。
Decision Support systems have conceptual dependencies on other layers such as Operations or Potential because decisions aren’t made in a vacuum. A lot of projects implement Decision Support using data warehouse technology. The layer becomes a distinct BOUNDED CONTEXT, with a CUSTOMER/SUPPLIER relationship with the Operations software. In other projects, it is more deeply integrated, as in the preceding extended example. And one of the intrinsic advantages of layers is that the lower layers can exist without the higher ones. This can facilitate phased introductions or higher-level enhancements built on top of older operational systems.
决策支持系统对其他层(如作业层或潜能层)有概念上的依赖性,因为决策并不是凭空制定的。很多项目都利用数据仓库技术来实现决策支持。在这样的项目中,决策支持层实际上变成了一个独特的 BOUNDED CONTEXT,并且与作业软件具有一种 CUSTOMER/SUPPLIER 关系。在其他项目中,决策支持层被更深地集成到系统中,就像前面的扩展示例讲到的那样。分层结构的一个内在的优点是较低的层可以独立于较高的层存在。这样有利于在较老的作业系统上分阶段引入新功能或开发高层次的增强功能。
Another case is software that enforces elaborate business rules or legal requirements, which can constitute a RESPONSIBILITY LAYER.
另一种情形是软件实施了详细的业务规则或法律需求,这些规则或需求可以形成一个 RESPONSIBILITY LAYER。
- Policy. What are the rules and goals? Rules and goals are mostly passive, but constrain the behavior in other layers. Designing these interactions can be subtle. Sometimes a Policy is passed in as an argument to a lower level method. Sometimes the STRATEGY pattern is applied. Policy works well in conjunction with a Decision Support layer, which provides the means to seek the goals set by Policy, constrained by the rules set by Policy.
- 策略层。规则和目标是什么?规则和目标主要是被动的,但它们约束着其他层的行为。这些交互的设计是一个微妙的问题。有时策略会作为一个参数传给较低层的方法。有时会使用 STRATEGY 模式。策略层与决策支持层能够进行很好的协作,决策支持层提供了用于搜索策略层所设定的目标的方式,这些目标又受到策略层所设定的规则的约束。
Policy layers can be written in the same language as the other layers, but they are sometimes implemented using rules engines. This doesn’t necessarily place them in a separate BOUNDED CONTEXT. In fact, the difficulty of coordinating such different implementation technologies can be eased by fastidiously using the same model across both. When rules are written based on a different model than the objects they apply to, either the complexity goes way up or the objects get dumbed down to keep things manageable.
策略层可以和其他层使用同一种语言来编写,但它们有时是使用规则引擎来实现的。这并不是说一定要把它们放到一个单独的 BOUNDED CONTEXT 中。实际上,通过在两种不同的实现技术中严格使用同一个模型,可以减小在这两种实现技术之间进行协调的难度。当规则与它们所应用的对象是基于不同模型编写的时候,要么复杂度会大大增加,要么对象会变得十分笨拙而难以管理。如图 16-13 所示。
Conceptual dependencies and shearing points in a factory automation system
Many businesses do not base their capability on plant and equipment. In financial services or insurance, to name two, the potential is to a large extent determined by current operations. An insurance company’s ability to take on a new risk by underwriting a new policy agreement is based on the diversification of its current business. The Potential layer would probably merge into Operations, and a different layering would evolve.
很多企业并不是依靠工厂和设备能力来运营的。举两个例子,在金融服务或保险业中,潜能在很大程度上是由当前的运营状况决定的。一家保险公司在考虑签保单承担理赔责任时,要根据当前业务的多样性来判断是否有能力承担它所带来的风险。潜能层有可能会被合并到作业层中,这样就会演变出一种不同的分层结构。
One area that often comes to the fore in these situations is commitments made to customers.
这些情况下经常出现的一个层是对客户所做出的承诺(见图 16-14)。
- Commitment. What have we promised? This layer has the nature of Policy, in that it states goals that direct future operations, but it has the nature of Operations in that commitments emerge and change as a part of ongoing business activity.
- 承诺层。我们承诺了什么?这个层具有策略层的性质,因为它表述了一些指导未来运营的目标;但它也有作业层的性质,因为承诺是作为后续业务活动的一部分而出现和变化的。
Conceptual dependencies and shearing points in an investment banking system
The Potential and Commitment layers are not mutually exclusive. A domain in which both are prominent, say a transportation company with a lot of custom shipping services, might use both. Other layers more specific to those domains might be useful too. Change things. Experiment. But it is best to keep the layering system simple; going beyond four or possibly five becomes unwieldy. Having too many layers isn’t as effective at telling the story, and the problems of complexity the large-scale structure was meant to solve will come back in a new form. The large-scale structure must be ferociously distilled.
潜能层和承诺层并不是互相排斥的。在有的领域中(如一家提供很多定制运输服务的运输公司),这两个层都很重要,因此可以同时使用它们。与这些领域密切相关的其他层也会用到。我们需要对分层结构进行调整和实验,但一定要使分层系统保持简单,如果层数超过 4 或 5,就比较难处理了。层数过多将无法有效地描述领域,而且本来要使用大比例结构解决的复杂性问题又会以一种新的方式出现。我们必须对大比例结构进行严格的精简。
Although these five layers are applicable to a range of enterprise systems, they do not capture the salient responsibilities of all domains. In other cases, it would be counterproductive to try to force the design into this shape, but there may be a natural set of RESPONSIBILITY LAYERS that do work. For a domain completely unrelated to those we’ve discussed, these layers might have to be completely original. Ultimately, you have to use your intuition, start somewhere, and let the ORDER EVOLVE.
虽然这 5 个层对很多企业系统都适用,但并不是所有领域的主要概念都涵盖在这 5 个层中。有些情况下,在设计中生硬地套用这种形式反而会起反作用,而使用一组更自然的 RESPONSIBILITY LAYER 会更有效。如果一个领域与上述讨论毫无关系,所有的分层可能都必须从头开始。最后,我们必须根据直觉选择一个起点,然后通过 EVOLVING ORDER 来改进它。
[A KNOWLEDGE LEVEL is] a group of objects that describes how another group of objects should behave. [Martin Fowler, “Accountability,” www.martinfowler.com]
“KNOWLEDGE LEVEL 是”一组描述了另一组对象应该有哪些行为的对象。[Martin Fowler, “Accountability”,www.martinfowler.com]
KNOWLEDGE LEVEL untangles things when we need to let some part of the model itself be plastic in the user’s hands yet constrained by a broader set of rules. It addresses requirements for software with configurable behavior, in which the roles and relationships among ENTITIES must be changed at installation or even at runtime.
当我们需要让用户对模型的一部分有所控制,而模型又必须满足更大的一组规则时,可以利用 KNOWLEDGE LEVEL(知识级别)来处理这种情况。它可以使软件具有可配置的行为,其中实体中的角色和关系必须在安装时(甚至在运行时)进行修改。
In Analysis Patterns (Fowler 1996, pp. 24–27), the pattern emerges from a discussion of modeling accountability within organizations, and it is later applied to posting rules in accounting. Although the pattern appears in several chapters, it doesn’t have a chapter of its own because it is different from most patterns in the book. Rather than modeling a domain, as the other analysis patterns do, KNOWLEDGE LEVEL structures a model.
在《分析模式》[Fowler 1996, pp. 24-27]一书中,知识级别这种模式是讨论在组织内部对责任进行建模的时候提到的,后来在会计系统的过账规则中也用到了这种模式。虽然有几章内容涉及此模式,但并没有为它单独开一章,因为它与书中所讨论的大部分模式都不相同。KNOWLEDGE LEVEL 并不像其他分析模式那样对领域进行建模,而是用来构造模型的。
To see the problem concretely, consider models of “accountability.” Organizations are made up of people and smaller organizations, and define the roles they play and the relationships between them. The rules governing those roles and relationships vary greatly for different organizations. At one company, a “department” might be headed by a “Director” who reports to a “Vice President.” In another company, a “module” is headed by a “Manager” who reports to a “Senior Manager.” Then there are “matrix” organizations, in which each person reports to different managers for different purposes.
为了使问题更具体,我们来考虑一下“责任”(accountability)模型。组织是由人和一些更小的组织构成的,并且定义了他们所承担的角色和互相之间的关系。不同的组织用于控制这些角色和关系的规则大不相同。有的公司分为各个“部门”,每个部门可能由一位“主管”来领导,他要向“副总裁”汇报。而有的公司则分为各个“模块”(module),每个模块由一位“经理”来领导,他要向“高级经理”汇报。还有一些组织采用的是“矩阵”形式,其中每个人都出于不同的目的而向不同的经理汇报。
A typical application would make some assumptions. When those didn’t fit, users would start to use data-entry fields in a different way than they were intended. Any behavior the application had would misfire, as the semantics were changed by the users. Users would develop workarounds for the behavior, or would get the higher level features of the application shut off. They would be forced to learn complicated mappings between what they did in their jobs and the way the software works. They would never be served well.
一般的应用程序都会做一些假设。当这些假设并不恰当时,用户就会在数据录入字段中输入与预期不符的数据。由于语义被用户改变,因此应用程序的任何行为都可能会失败。用户将会想出一些迂回的办法来执行这些行为,或者关闭一些高级特性。他们不得不费力地找出他们的操作与软件行为之间的复杂对应关系。这样他们永远也得不到良好的服务。
When the system had to be changed or replaced, developers would discover (sooner or later) that the meanings of the features were not what they seemed. They might mean very different things in different user communities or in different situations. Changing anything without breaking these overlaid usages would be daunting. Data migration to a more tailored system would require understanding and coding for all those quirks.
当必须要对系统进行修改或替换时,开发人员(或迟或早)会发现,有一些功能的真实含义并不像它们看上去的那样。它们在不同的用户社区或不同情况下具有完全不同的含义。在不破坏这些互相叠加的含义的前提下修改任何东西都是非常困难的。要想把数据迁移到一个“更合适”的系统中,必须要理解这些奇怪的部分,并对其进行编码。
Example: Employee Payroll and Pension, Part 1
示例员工工资和养老金系统,第 1 部分
The HR department of a medium-sized company has a simple program for calculating payroll and pension contributions.
一家中等规模公司的人力资源部门有一个用于计算工资和养老金代扣的简单程序。如图 16-15 和图 16-16 所示。
The old model, overconstrained for new requirements
Some employees represented using the old model
But now, the management has decided that the office administrators should go into the “defined benefit” retirement plan. The trouble is that office administrators are paid hourly, and this model does not allow mixing. The model will have to change.
但现在,管理层决定办公室行政人员应该进入“固定受益”(Defined Benefit)退休计划。问题在于办公室行政人员是按小时付薪酬的,而这个模型不支持混合计算。因此必须修改模型。
The next model proposal is quite simple: just remove the constraints.
下面的模型提议非常简单,只是把约束去掉了,如图 16-17 所示。但也会出现一些错误,如图 16-18 所示。
The proposed model, now underconstrained
Employees can be associated with the wrong plan.
This model allows each employee to be associated with either kind of retirement plan, so each office administrator can be switched. This model is rejected by management because it does not reflect company policy. Some administrators could be switched and others not. Or the janitor could be switched. Management wants a model that enforces the policy:
在这个模型中,每个员工随便加入哪一种退休计划都可以,因此每位办公室行政人员都可以改变退休计划。管理层最后放弃了这个模型,因为它没有反映出公司的策略。一些行政人员可以选择“固定受益”计划,而另外一些则不能。要是使用这个模型,连门卫也可以改变退休计划。管理层需要一个能够实施以下策略的模型:
Office administrators are hourly employees with defined-benefit retirement plans.
办公室行政人员按小时付薪酬,且采用固定受益退休计划。
This policy suggests that the “job title” field now represents an important domain concept. Developers could refactor to make that concept explicit as an “Employee Type.”
这个策略暗示出 job title(工作头衔)字段现在表示了一个重要的领域概念。开发人员可以重构模型,用 Employee Type(员工类型)把这个概念明确显示出来,如图 16-19 和图 16-20 所示。
The Type object allows requirements to be met.
Each Employee Type is assigned a Retirement Plan.
The requirements can be stated in the UBIQUITOUS LANGUAGE as follows:
需求可以像下面这样用 UBIQUITOUS LANGUAGE 来表述出来:
An Employee Type is assigned to either Retirement Plan or either payroll.
Employees are constrained by the Employee Type.
一个 EMPLOYEE TYPE 可以被指定两种 RETIREMENT PLAN 中的任何一种,也可以被指定两种工资中的任何一种。
EMPLOYEE 受 EMPLOYEE TYPE 约束。
Access to edit the Employee Type object will be restricted to a “superuser,” who will make changes only when company policy changes. An ordinary user in the personnel department can change Employees or point them at a different Employee Type.
只有 superuser(超级用户)才能编辑 Employee Type 对象,而且只有当公司策略变更时,他才能修改此对象。人事部门的普通用户只能修改 Employee 对象,或只能将这些对象指定为另一种 Employee Type。
This model satisfies the requirements. The developers sense an implicit concept or two, but it is just a nagging feeling at the moment. They don’t have any solid ideas to pursue, so they call it a day.
这种模型可以满足需求。开发人员认识到了一两个隐含的概念,但这只是灵机一动才想到的。他们并没有具体的思路可供追查下去,因此他们暂时结束了这一天的工作。
A static model can cause problems. But problems can be just as bad with a fully flexible system that allows any possible relationship to be presented. Such a system would be inconvenient to use and wouldn’t allow the organization’s own rules to be enforced.
静态模型可能引起问题。但在一个过于灵活的系统中,如果任何可能的关系都允许存在,问题一样糟糕。这样的系统使用起来会很不方便,而且会导致组织无法实施自己的规则。
Fully customizing software for each organization is not practical because, even if each organization could pay for custom software, the organizational structure will likely change frequently.
让每个组织完全定制自己的软件也是不现实的,即使组织能够担负得起定制软件的费用,组织结构也可能会频繁变化。
So such software must provide options to allow the user to configure it to reflect the current structure of the organization. The trouble is that adding such options to the model objects makes them unwieldy. The more flexibility you add, the more complex it all becomes.
因此,这样的软件必须为用户提供配置选项,以便反映出组织的当前结构。问题是,在模型对象中添加这些选项会使这些对象变得难于处理。要求的灵活性越高,模型就会变得越复杂。
In an application in which the roles and relationships between ENTITIES vary in different situations, complexity can explode. Neither fully general models nor highly customized ones serve the users’ needs. Objects end up with references to other types to cover a variety of cases, or with attributes that are used in different ways in different situations. Classes that have the same data and behavior may multiply just to accommodate different assembly rules.
如果在一个应用程序中,ENTITY 的角色和它们之间的关系在不同的情况下有很大变化,那么复杂性会显著增加。在这种情况下,无论是一般的模型还是高度定制的模型,都无法满足用户的需求。为了兼顾各种不同的情形,对象需要引用其他的类型,或者需要具备一些在不同情况下包括不同使用方式的属性。具有相同数据和行为的类可能会大量增加,而这些类的唯一作用只是为了满足不同的组装规则。
Nestled into our model is another model that is about our model. A KNOWLEDGE LEVEL separates that self-defining aspect of the model and makes its constraints explicit.
在我们的模型中嵌入了另一个模型,而它的作用只是描述我们的模型。KNOWLEDGE LEVEL 分离了模型的这个自我定义的方面,并清楚地显示了它的限制。
KNOWLEDGE LEVEL is an application to the domain layer of the REFLECTION pattern, used in many software architectures and technical infrastructures and described well in Buschmann et al. 1996. REFLECTION accommodates changing needs by making the software “self-aware,” and making selected aspects of its structure and behavior accessible for adaptation and change. This is done by splitting the software into a “base level,” which carries the operational responsibility for the application, and a “meta level,” which represents knowledge of the structure and behavior of the software.
KNOWLEDGE LEVEL 是 REFLECTION(反射)模式在领域层中的一种应用,很多软件架构和技术基础设施中都使用了它,[Buschmann et al. 1996])中给出了详尽介绍。REFLECTION 模式能够使软件具有“自我感知”的特性,并使所选中的结构和行为可以接受调整和修改,从而满足变化需要。这是通过将软件分为两个层来实现的,一个层是“基础级别”(base level),它承担应用程序的操作职责;另一个是“元级别”(meta level),它表示有关软件结构和行为方面的知识。
Significantly, the pattern is not called a knowledge “layer.” As much as it resembles layering, REFLECTION involves mutual dependencies running in both directions.
值得注意的是,我们并没有把这种模式叫做知识“层”(layer)。虽然 REFLECTION 与分层很类似,但反射却包含双向依赖关系。
Java has some minimal built-in REFLECTION in the form of protocols for interrogating a class for its methods and so forth. Such mechanisms allow a program to ask questions about its own design. CORBA has somewhat more extensive but similar REFLECTION protocols. Some persistence technologies extend the richness of that self-description to support partially automated mapping between database tables and objects. There are other technical examples. This pattern can also be applied within the domain layer.
Java 有一些最基本的内置 REFLECTION 机制,它们采用的是协议的形式,用于查询一个类的方法等。这样的机制允许用户查询有关它自己的一些设计信息。CORBA 也有一些扩展(但类似)的 REFLECTION 协议。一些持久化技术增加了更丰富的自描述特性,在数据表与对象之间提供了部分自动化的映射。还有其他一些技术例子。这种模式也可以在领域层中使用。
Comparing the terminology of KNOWLEDGE LEVEL and REFLECTION
KNOWLEDGE LEVEL 与 REFLECTION 所使用的术语比较
Just to be clear, the reflection tools of the programming language are not for use in implementing the KNOWLEDGE LEVEL of a domain model. Those meta-objects describe the structure and behavior of the language constructs themselves. Instead, the KNOWLEDGE LEVEL must be built of ordinary objects.
要明确的一点是,编程语言的反射工具并不是用于实现领域模型的 KNOWLEDGE LEVEL 的。这些元对象描述的是语言构造本身的结构和行为。相反,KNOWLEDGE LEVEL 必须使用普通对象来构造。
The KNOWLEDGE LEVEL provides two useful distinctions. First, it focuses on the application domain, in contrast to familiar uses of REFLECTION. Second, it does not strive for full generality. Just as a SPECIFICATION can be more useful than a general predicate, a very specialized set of constraints on a set of objects and their relationships can be more useful than a generalized framework. The KNOWLEDGE LEVEL is simpler and can communicate the specific intent of the designer.
KNOWLEDGE LEVEL 具有两个很有用的特性。首先,它关注的是应用领域,这一点与人们所熟悉的 REFLECTION 模式的应用正好相反。其次,它并不追求完全的通用性。正如一个 SPECIFICATION 可能比通用的断言更有用一样,专门为一组对象和它们的关系定制的一个约束集可能比一个通用的框架更有用。KNOWLEDGE LEVEL 显得更简单,而且可以传达设计者的特别意图。
Therefore:
因此:
Create a distinct set of objects that can be used to describe and constrain the structure and behavior of the basic model. Keep these concerns separate as two “levels,” one very concrete, the other reflecting rules and knowledge that a user or superuser is able to customize.
创建一组不同的对象,用它们来描述和约束基本模型的结构和行为。把这些对象分为两个“级别”,一个是非常具体的级别,另一个级别则提供了一些可供用户或超级用户定制的规则和知识。
Like all powerful ideas, REFLECTION and KNOWLEDGE LEVELS can be intoxicating. This pattern should be used sparingly. It can unravel complexity by freeing operations objects from the need to be jacks-of-all-trades, but the indirection it introduces does add some of that obscurity back in. If the KNOWLEDGE LEVEL becomes complex, the system’s behavior becomes hard to understand for developers and users alike. The users (or superuser) who configure it will end up needing the skills of a programmer—and a meta-level programmer at that. If they make mistakes, the application will behave incorrectly.
像所有有用的思想一样,REFLECTION 和 KNOWLEDGE LEVEL 可能令人们感到振奋,但不应滥用这种模式。它确实能够使对象不必为了满足各种不同情形下的需求而变得过于复杂,但它所引入的间接性也会使系统变得更模糊。如果 KNOWLEDGE LEVEL 太复杂,开发人员和用户就很难理解系统的行为。负责配置它的用户(或超级用户)最终将需要具备程序员的技能,甚至需要掌握处理元数据的技能。如果他们出现了错误,应用程序也将会产生错误行为。
Also, the basic problems of data migration don’t completely disappear. When a structure in the KNOWLEDGE LEVEL is changed, existing operations-level objects have to be dealt with. It may be possible for old and new to coexist, but one way or another, careful analysis is needed.
而且,数据迁移的基本问题并没有完全得到解决。当 KNOWLEDGE LEVEL 中的某个结构发生变化时,必须对现有的操作级别中的对象进行相应的处理。新旧对象确实可以共存,但无论如何都需要进行仔细的分析。
All of these issues put a major burden on the designer of a KNOWLEDGE LEVEL. The design has to be robust enough to handle not only the scenarios presented in development, but also any scenario for which a user could configure the software in the future. Applied judiciously, to the points where customization is crucial and would otherwise distort the design, KNOWLEDGE LEVELS can solve problems that are very hard to handle any other way.
所有这些问题为 KNOWLEDGE LEVEL 的设计人员增加了一个沉重的负担。设计必须足够健壮,因为不仅要解决开发中可能出现的各种问题,而且还要考虑到将来用户在配置软件时可能会出现的各种问题。如果得到合理的运用,KNOWLEDGE LEVEL 能够解决一些其他方式很难解决的问题。如果系统中某些部分的定制非常关键,而要是不提供定制能力就会破坏掉整个设计,这时就可以利用知识级别来解决这一问题。
Example: Employee Payroll and Pension, Part 2: KNOWLEDGE LEVEL
示例员工工资和养老金系统,第 2 部分:KNOWLEDGE LEVEL
Our team members are back, and, refreshed from a night’s sleep, one of them has started to close in on one of the awkward points. Why were certain objects being secured while others were freely edited? The cluster of restricted objects reminded him of the KNOWLEDGE LEVEL pattern, and he decided to try it as a way of viewing the model. He found that the existing model could already be viewed this way.
我们的团队成员又回来了,经过了一夜的休息,他们恢复了精神,团队中的一个人对系统中一个难处理的问题有了点思路。为什么有些对象要被限制起来,而其他对象则可以自由编辑呢?那些受限制的对象让他想到了 KNOWLEDGE LEVEL 模式,他决定尝试着从这个角度来观察一下模型,才发现本来就可以用这种方式来观察模型的。
Recognizing the KNOWLEDGE LEVEL implicit in the existing model
The restricted edits were in the KNOWLEDGE LEVEL, while the day-to-day edits were in the operational level. A nice fit. All the objects above the line described types or longstanding policies. The Employee Type effectively imposed behavior on the Employee.
从图 16-21 可以看出,受限制的对象都在 KNOWLEDGE LEVEL 中,而可以自由编辑的对象都在操作级别中,区分得非常清楚。虚线上面的所有对象描述了类型或长期策略。Employee Type 有效地把行为加在 Employee 上。
The developer was sharing his insight with his colleagues when one of the other developers had another insight. The clarity of seeing the model organized by KNOWLEDGE LEVEL had let her spot what had been bothering her the previous day. Two distinct concepts were being combined in the same object. She had heard it in the language used on the previous day but hadn’t put her finger on it:
这位开发人员把他的想法告诉了大家,这使另一个人又产生了另一个想法。按照 KNOWLEDGE LEVEL 对模型进行组织后,模型变得更清晰了,这使她一下子发现了昨天困扰她的那个问题——两个完全不同的概念被合并到同一个模型中。昨天她在团队讨论所使用的语言中就听到了这个问题,只是没有注意到而已:
An Employee Type is assigned to either Retirement Plan or either payroll.
一个 Employee Type 可以被指定两种 Retirement Plan 中的任何一种,也可以被指定两种工资中的任何一种。
But that was not really a statement in the UBIQUITOUS LANGUAGE. There was no “payroll” in the model. They had spoken in the language they wanted, rather than the one they had. The concept of payroll was implicit in the model, lumped together with Employee Type. It hadn’t been so obvious before the KNOWLEDGE LEVEL was separated out, and the very elements in that key phrase all appeared in the same level together . . . except one.
但这实际上并不是用 UBIQUITOUS LANGUAGE 中来表达的声明。模型中并没有“payroll”(工资)。他们只是根据自己的需要来讲话,而没有使用实际就有的通用语言。payroll 的概念在模型中是隐含的,与 Employee Type 混在一起。在分离出 KNOWLEDGE LEVEL 以前,它并不明显,而且这个声明中的所有元素都出现在同一个级别上,只有一个元素例外。
Based on this insight, she refactored again to a model that does support that statement.
根据这种理解,她重构了一个真正支持该声明的模型。
The need for user control of the rules for associating objects drove the team to a model that had an implicit KNOWLEDGE LEVEL.
为了让用户控制那些制约对象之间关联的规则,开发团队开发了一个包含隐含 KNOWLEDGE LEVEL 的模型。
Payroll is now explicit, distinct from Employee Type.
Each Employee Type now has a Retirement Plan and a Payroll.
KNOWLEDGE LEVEL was hinted at by the characteristic access restrictions and a “thing-thing” type relationship. Once it was in place, the clarity it afforded helped produce another insight that disentangled two important domain concepts by factoring out Payroll.
特有的访问约束和一种“事物—事物”型的关系对开发团队起到了提示的作用,使他们看出了隐含的 KNOWLEDGE LEVEL。一旦 KNOWLEDGE LEVEL 被分离出来,它就能够使模型变得非常清晰,从而可以通过提取出 Payroll 将两个重要的领域概念分开。
KNOWLEDGE LEVEL, like other large-scale structures, isn’t strictly necessary. The objects will still work without it, and the insight that separated Employee Type from Payroll could still have been found and used. There may come a time when this structure doesn’t seem to be pulling its weight and can be dropped. But for now, it seems to tell a useful story about the system and helps developers grapple with the model.
像其他大比例结构一样,KNOWLEDGE LEVEL 也不是必须要使用的。没有它,对象照样能工作,而且团队可能仍能够认识到他们需要将 Employee Type 与 Payroll 分离。当项目进行到某个时刻,这种结构看起来已经没什么作用了,那么就可以放弃它。但现在它对于描述系统很有用,并且能够帮助开发人员理解模型。
At first glance, KNOWLEDGE LEVEL looks like a special case of RESPONSIBILITY LAYERS, especially the “policy” layer, but it is not. For one thing, dependencies run in both directions between the levels, but with LAYERS, lower layers are independent of upper layers.
乍看上去,KNOWLEDGE LEVEL 像是 RESPONSIBILITY LAYER(特别是策略层)的一个特例,但它并不是。首先,两个级别之间的依赖性是双向的,而在层次结构中,较低的层不依赖于较高的层。
In fact, KNOWLEDGE LEVEL can coexist with most other large-scale structures, providing an additional dimension of organization.
实际上,RESPONSIBILITY LAYER 可以与其他大部分的大比例结构共存,它提供了另一种用来组织模型的维度。
Opportunities arise in a very mature model that is deep and distilled. A PLUGGABLE COMPONENT FRAMEWORK usually only comes into play after a few applications have already been implemented in the same domain.
在深入理解和反复精炼基础上得到的成熟模型中,会出现很多机会。通常只有在同一个领域中实现了多个应用程序之后,才有机会使用 PLUGGABLE COMPONENT FRAMEWORK(可插入式组件框架)。
When a variety of applications have to interoperate, all based on the same abstractions but designed independently, translations between multiple BOUNDED CONTEXTS limit integration. A SHARED KERNEL is not feasible for teams that do not work closely together. Duplication and fragmentation raise costs of development and installation, and interoperability becomes very difficult.
当很多应用程序需要进行互操作时,如果所有应用程序都基于相同的一些抽象,但它们是独立设计的,那么在多个 BOUNDED CONTEXT 之间的转换会限制它们的集成。各个团队之间如果不能紧密地协作,就无法形成一个 SHARED KERNEL。重复和分裂将会增加开发和安装的成本,而且互操作会变得很难实现。
Some successful projects break down their design into components, each with responsibility for certain categories of functions. Usually all the components plug into a central hub, which supports any protocols they need and knows how to talk to the interfaces they provide. Other patterns of connecting components are also possible. The design of these interfaces and the hub that connects them must be coordinated, while more independence is possible designing the interiors.
一些成功的项目将它们的设计分解为组件,每个组件负责提供某些类别的功能。通常所有组件都插入到一个中央 hub 上,这个 hub 支持组件所需的所有协议,并且知道如何与它们所提供的接口进行对话。还有其他一些将组件连在一起的可行模式。对这些接口以及用于连接它们的 hub 的设计必须要协调,而组件内部的设计则可以更独立一些。
Several widely used technical frameworks support this pattern, but that is a secondary issue. A technical framework is needed only if it solves some essential technical problem such as distribution, or sharing a component among different applications. The basic pattern is a conceptual organization of responsibilities. It can easily be applied within a single Java program.
有几个广泛使用的技术框架支持这种模式,但这只是次要问题。一种技术框架只有在能够解决某类重要技术问题的时候才有必要使用,如在设计分布式系统或在不同应用程序中共享一个组件时。可插入式组件框架的基本模式是职责的概念组织,它很容易在单个的 Java 程序中使用。
Therefore:
因此:
Distill an ABSTRACT CORE of interfaces and interactions and create a framework that allows diverse implementations of those interfaces to be freely substituted. Likewise, allow any application to use those components, so long as it operates strictly through the interfaces of the ABSTRACT CORE.
从接口和交互中提炼出一个 ABSTRACT CORE,并创建一个框架,这个框架要允许这些接口的各种不同实现被自由替换。同样,无论是什么应用程序,只要它严格地通过 ABSTRACT CORE 的接口进行操作,那么就可以允许它使用这些组件。
High-level abstractions are identified and shared across the breadth of the system; specialization occurs in MODULES. The central hub of the application is an ABSTRACT CORE within a SHARED KERNEL. But multiple BOUNDED CONTEXTS can lie behind the encapsulated component interfaces, so that this structure can be especially convenient when many components are coming from many different sources, or when components are encapsulating preexisting software for integration.
高层抽象被识别出来,并在整个系统范围内共享,而特化(specialization)发生在 MODULE 中。应用程序的中央 hub 是 SHARED KERNEL 内部的 ABSTRACT CORE。但封装的组件接口可以把多个 BOUNDED CONTEXT 封装到其中,这样,当很多组件来自多个不同地方时,或者当组件中封装了用于集成的已有软件时,可以很方便地使用这种结构。
This is not to say that components must have divergent models. Multiple components can be developed within a single CONTEXT if the teams CONTINUOUSLY INTEGRATE, or they can define another SHARED KERNEL held in common by a closely related set of components. All these strategies can coexist easily within a large-scale structure of PLUGGABLE COMPONENTS. Another option, in some cases, is to use a PUBLISHED LANGUAGE for the plug-in interface of the hub.
这并不是说不同组件一定要使用不同的模型。只要团队采用了 CONTINUOUS INTEGRATE,或者为一组密切相关的组件定义了另一个 SHARED KERNEL,那么就可以在同一个 CONTEXT 中开发多个组件。在 PLUGGABLE COMPONENT FRAMEWORK 这种大比例结构中,所有这些策略很容易共存。在某些情况下,还有一种选择是使用一种 PUBLISHED LANGUAGE 来编写 hub 的插入接口。
There are a few downsides to a PLUGGABLE COMPONENT FRAMEWORK. One is that this is a very difficult pattern to apply. It requires precision in the design of the interfaces and a deep enough model to capture the necessary behavior in the ABSTRACT CORE. Another major downside is that applications have limited options. If an application needs a very different approach to the CORE DOMAIN, the structure will get in the way. Developers can specialize the model, but they can’t change the ABSTRACT CORE without changing the protocol of all the diverse components. As a result, the process of continuous refinement of the CORE, refactoring toward deeper insight, is more or less frozen in its tracks.
PLUGGABLE COMPONENT FRAMEWORK 也有几个缺点。一个缺点是它是一种非常难以使用的模式。它需要高精度的接口设计和一个非常深入的模型,以便把一些必要的行为捕获到 ABSTRACT CORE 中。另一个很大的缺点是它只为应用程序提供了有限的选择。如果一个应用程序需要对 CORE DOMAIN 使用一种非常不同的方法,那么可插入式组件框架将起到妨碍作用。开发人员可以对模型进行特殊修改,但如果不更改所有不同组件的协议,就无法修改 ABSTRACT CORE。这样一来,CORE 的持续精化过程(也是通过重构得到更深层理解的过程)在某种程度上会陷入僵局。
Fayad and Johnson (2000) give a good look at ambitious attempts at PLUGGABLE COMPONENT FRAMEWORKS in several domains, including a discussion of SEMATECH CIM. The success of such frameworks is a mixed story. Probably the biggest obstacle is the maturity of understanding needed to design a useful framework. A PLUGGABLE COMPONENT FRAMEWORK should not be the first large-scale structure applied on a project, nor the second. The most successful examples have followed after the full development of multiple specialized applications.
[Fayad and Johnson 2000]中详细介绍了在几个领域中使用 PLUGGABLE COMPONENT FRAMEWORK 的大胆尝试,其中包括对 SEMATECH CIM 框架的讨论。要想成功地使用这些框架,需要综合考虑很多事情。最大的障碍可能就是人们的理解不那么成熟,要想设计一个有用的框架,必须要有成熟的理解。PLUGGABLE COMPONENT FRAMEWORK 不适合作为项目的第一个大比例结构,也不适合作为第二个。最成功的例子都是在完全开发出了多个专门应用之后才采用这种结构的。
Example: The SEMATECH CIM Framework
示例 SEMATECH CIM 框架
In a factory producing computer chips, groups (called lots) of silicon wafers are moved from one machine to another through hundreds of steps of processing until the microscopic circuitry being printed and etched into them is complete. The factory needs software that can track each individual lot, recording the exact processing that has been done to it, and then direct either factory workers or automated equipment to take it to the next appropriate machine and apply the next appropriate process. Such software is called a manufacturing execution system (MES).
在一家生产计算机芯片的工厂中,一组一组的硅片(称为 lot)从一台机器传送到另一台机器,通过上百道加工工序,直到印刷上微电路并完成蚀刻。工厂需要一个软件来跟踪每个 lot,记录下来它上面已经完成的加工,然后指挥工人或自动设备把它送到下一台正确的机器上,并进行下一次正确的加工。这样的软件称为制造执行系统(Manufacturing Execution System,MES)。
Hundreds of different machines from dozens of vendors are used, with carefully tailored recipes at each step of the way. Developing MES software that could deal with such a complex mix was daunting and prohibitively expensive. In response, an industry consortium, SEMATECH, developed the CIM Framework.
工厂使用了数十家供应商生产的数百台不同的机器,每道工序都仔细设计了定制的配方。为这个复杂的混合加工过程开发 MES 软件是一项异常艰巨的任务,而且费用也十分高昂。为了解决这些问题,SEMATECH(一家行业协会)开发了 CIM 框架。
The CIM Framework is big and complicated and has many aspects, but two are relevant here. First, the framework defines abstract interfaces for the basic concepts of the semiconductor MES domain—in other words, the CORE DOMAIN in the form of an ABSTRACT CORE. These interface definitions include both behavior and semantics.
CIM 框架庞大而复杂,它有很多方面,但只有两个方面与我们这里的讨论相关。首先,这个框架为半导体 MES 领域的基本概念定义了抽象接口,换言之,以 ABSTRACT CORE 的形式定义了 CORE DOMAIN。这些接口定义既包括行为上的,也包括语义上的。
A highly simplified subset of the CIM interfaces, with sample implementations
If a vendor produces a new machine, they have to develop a specialized implementation of the Process Machine interface. If they adhere to that interface, their machine-control component should plug into any application based on the CIM Framework.
如果某家供应商生产了一种新的机器,他们必须开发 Process Machine 接口的一个专用实现。只要他们遵守该接口,他们的机器控制组件就可以插入到任何基于 CIM 框架的应用程序中。
Having defined these interfaces, SEMATECH defined the rules by which they could interact in an application. Any application based on the CIM Framework would have to implement a protocol that hosted objects implementing some subset of those interfaces. If this protocol were implemented, and the application strictly observed the abstract interfaces, then the application could count on the promised services of those interfaces, regardless of implementation. The combination of those interfaces and the protocol for using them constitutes a tightly restrictive large-scale structure.
在定义了这些接口之后,SEMATECH 又定义了组件在应用程序中进行交互时需要遵守的规则。任何基于 CIM 框架的应用程序都必须实现一个协议,通过这个协议来为那些已经实现部分接口的对象提供服务。如果这个协议已经实现,而且应用程序严格遵守抽象接口,那么这个应用程序就可以使用这些接口所提供的服务,而不用管它们是如何实现的。这些接口以及为了使用接口而实现的协议组合在一起,构成了具有严格限制的大比例结构。
The user places a lot in the next machine and logs the move into the computer.
The framework has very specific infrastructure requirements. It is tightly coupled to CORBA to provide persistence, transactions, events, and other technical services. But the interesting thing about it is the definition of a PLUGGABLE COMPONENT FRAMEWORK, which allows people to develop software independently and smoothly integrate them into immense systems. No one knows all the details of such a system, but everyone understands an overview.
这个框架需要使用专门的基础设施。它主要使用 CORBA 来提供持久化、事务、事件和其他技术服务。但它的 PLUGGABLE COMPONENT FRAMEWORK 的定义很有趣,它允许人们独立开发软件,并把开发出来的软件平滑地集成到庞大的系统中。没有人会知道这个系统中的所有细节,但每个人都理解整体视图。
How can thousands of people work independently to create a quilt of more than 40,000 panels?
数千人是如何分工来制作一个由 40000 多块组成的“艾滋病纪念拼被”的?
A few simple rules provide a large-scale structure for the AIDS Memorial Quilt, leaving the details to individual contributors. Notice how the rules focus on the overall mission (memorializing people who have died of AIDS), the features of a component that make integration practical, and the ability to handle the quilt in larger sections (such as folding it).
几条简单的规则为“艾滋病拼图被子”提供了一种大比例结构,而细节则由各个志愿者来完成。注意规则重点关注的 3 个方面,一是整体任务(纪念那些因艾滋病而死去的人们),二是各个小块所具有的那些使其容易拼到整体中的特性,三是处理更大的块的能力(如把它折叠起来)。
Here’s How to Create a Panel for the Quilt
以下就是艾滋病纪念拼被的一个拼块的制作方法
[From the AIDS Memorial Quilt Project Web site, www.aidsquilt.org]
[摘自艾滋病纪念拼被网站,www.aidsquilt.org]
Design the panel
设计拼块
Include the name of the person you are remembering. Feel free to include additional information such as the dates of birth and death, and a hometown. . . . [P]lease limit each panel to one individual . . . .
把要纪念的人的名字写到拼块上。可以自由加入其他一些信息,如出生、死亡日期和出生地等,每个拼块仅限一人……
Choose your materials
选择你的材料
Remember that the Quilt is folded and unfolded many times, so durability is crucial. Since glue deteriorates with time, it is best to sew things to the panel. A medium-weight, non-stretch fabric such as a cotton duck or poplin works best.
记住,被单要被折叠和打开许多次,因此材料的耐久性很重要。由于胶会随着时间失效,因此最好把东西缝到拼块上。最好使用重量适中、不具有拉伸性的布料,如棉帆布或毛葛。
Your design can be vertical or horizontal, but the finished, hemmed panel must be 3 feet by 6 feet (90 cm × 180 cm)—no more and no less! When you cut the fabric, leave an extra 2–3 inches on each side for a hem. If you can’t hem it yourself, we’ll do it for you. Batting for the panels is not necessary, but backing is recommended. Backing helps to keep panels clean when they are laid out on the ground. It also helps retain the shape of the fabric.
设计可以采用横向或纵向,但最终镶好边的拼块必须是 3 英尺 ×6 英尺(90 cm×180 cm)——不能多也不能少!裁剪布料时,每个边留出 2 ~ 3 英寸的镶边。如果你自己不能镶边,我们会为你代劳。无需为拼块缝制夹层,但建议在背面缝一个衬垫,这样当把拼块放到地上时,可以保持干净,也有助于保持布料不变形。
Create the panel
制作拼块
In constructing your panel you might want to use some of the following techniques:
制作拼块时可能会用到以下技术。
- Appliqué: Sew fabric, letters and small mementos onto the background fabric. Do not rely on glue—it won’t last.
- Paint: Brush on textile paint or color-fast dye, or use an indelible ink pen. Please don’t use “puffy” paint; it’s too sticky.
- Stencil: Trace your design onto the fabric with a pencil, lift the stencil, then use a brush to apply textile paint or indelible markers.
- Collage: Make sure that whatever materials you add to the panel won’t tear the fabric (avoid glass and sequins for this reason), and be sure to avoid very bulky objects.
- Photos: The best way to include photos or letters is to photocopy them onto iron-on transfers, iron them onto 100% cotton fabric and sew that fabric to the panel. You may also put the photo in clear plastic vinyl and sew it to the panel (off-center so it avoids the fold).
- 缝饰:在背景布料上缝上其他的织物、信件或小的纪念品。不要使用胶水,因为它很容易失效。
- 用颜料涂色:刷上纺织颜料或快速上色染料,也可以使用不褪色的墨水笔。不要使用“棉花彩”,因为它的黏性太大了。
- 模绘:用铅笔把你的设计画到布料上,然后把得到的模板垫高,再用刷子涂上纺织颜料或不褪色的标记。
- 拼贴:在拼块上使用的材料一定不要把布料划破(因此不要使用玻璃和金属片),还要注意不要使用体积很大的物品。
- 照片:加照片或信件的最好方法是把它们影印到烫印转印纸(iron-on transfer)上,再由烫印转印纸印到 100%的纯棉布料上,再把这块布料缝到拼块上。也可以用乙烯材料把照片塑封起来,再缝到拼块上(不要放在中央,以避免折叠)。
The large-scale structure patterns discussed in this chapter range from the very loose SYSTEM METAPHOR to the restrictive PLUGGABLE COMPONENT FRAMEWORK. Other structures are possible, of course, and even within a general structural pattern, there is a lot of choice about how restrictive to make the rules.
本章所讨论的大比例结构很广泛,从非常宽松的 SYSTEM METAPHOR 到严格的 PLUGGABLE COMPONENT FRAMEWORK。当然,还有很多其他结构,而且,甚至在一个通用的结构模式中,在制定规则上也可以选择多种不同的严格程度。
For example, RESPONSIBILITY LAYERS dictate a kind of factoring of model concepts and their dependencies, but you could add rules that would specify communication patterns between the layers.
例如,RESPONSIBILITY LAYER 规定了一种用于划分模型概念以及它们的依赖性的方式,但我们也可以添加一些规则,来指定各个层之间的通信模式。
Consider a manufacturing plant where software directs each part to a machine where it is processed according to some recipe. The correct process is ordered from a Policy layer and executed in an Operations layer. But inevitably there will be mistakes made on the factory floor. The actual situation will not be consistent with the rules of the software. Now, an Operations layer must reflect the world as it is, which means that when a part is occasionally put in the wrong machine, that information must be accepted unconditionally. Somehow, this exceptional condition needs to be communicated to a higher layer. A decision-making layer can then use other policies to correct the situation, perhaps by rerouting the part to a repair process or by scrapping it. But Operations does not know anything about higher layers. The communication has to be done in a way that doesn’t create two-way dependencies from the lower layers to the higher ones.
假设有一家制造厂,每个零件在哪台机器上加工(根据工艺配方)完全由软件来指挥。正确的加工命令是从策略层发出的,并在作业层执行。但工厂的实际生产不可避免地会有错误。实际情况将与软件的规则不符。现在,作业层必须要反映出工厂的实际情况,这意味着当一个零件偶然被放到一台错误的机器上时,机器必须无条件地接受它。这种异常情况需要以某种方式传递到更高的层。然后,决策制定层可以利用其他策略来纠正这种情况,可以把该零件重新送到修理流程或直接丢弃它。但作业层不知道较高层的任何信息。通信必须是单向的,不能让较低层产生对较高层的依赖性。
Typically, this signaling would be done through some kind of event mechanism. The Operations objects would generate events whenever their state changed. Policy layer objects would listen for events of interest from the lower layers. When an event occurred that violated a rule, the rule would execute an action (part of the rule’s definition) that makes the appropriate response, or it might generate an event for the benefit of some still higher layer.
通常,这种信号传递是通过某种事件机制实现的。每当作业层对象的状态发生变化时,它们就将生成事件。策略层对象将监听来自较低层的相关事件。如果一个事件违反了某个规则,该规则将执行一个动作(规则定义的一部分)来给出适当的响应,或者生成一个事件反馈给更高的层,以便帮助更高的层做出决策。
In the banking example, the values of assets change (Operations), shifting the values of segments of a portfolio. When these values exceed portfolio allocation limits (Policy), perhaps a trader is alerted, who can buy or sell assets to redress the balance.
例如,在银行中,当投资组合中的某些部分发生变动时,资产的价值会发生改变(作业层)。当这些值超过投资组合的配置限制时(策略层),交易商可能就会接到通知,然后他可以通过买入或卖出资产来恢复平衡。
We could figure this out on a case-by-case basis, or we could decide on a consistent pattern for everyone to follow in interactions of objects of particular layers. A more restrictive structure increases uniformity, making the design easier to interpret. If the structure fits, the rules are likely to push developers toward good designs. Disparate pieces are likely to fit together better.
我们可以为每种不同的情况设计不同的事件机制,也可以让特殊层中的对象在交互时遵守一种一致的模式。结构越严格,一致性就越高,设计也越容易理解。如果结构适当的话,规则将推动开发人员得出好的设计。不同的部分之间会更协调。
On the other hand, the restrictions may take away flexibility that developers need. Very particular communication paths might be impractical to apply across BOUNDED CONTEXTS, especially in different implementation technologies, in a heterogeneous system.
另一方面,约束也会限制开发人员所需的灵活性。在异构系统中,特别是当系统使用了不同的实现技术时,可能无法跨越不同的 BOUNDED CONTEXT 来使用非常特殊的通信路径。
So you have to fight the temptation to build frameworks and regiment the implementation of the large-scale structure. The most important contribution of the large-scale structure is conceptual coherence, and giving insight into the domain. Each structural rule should make development easier.
因此一定要克制,不要滥用框架和死板地实现大比例结构。大比例结构的最重要的贡献在于它具有概念上的一致性,并帮助我们更深入地理解领域。每条结构规则都应该使开发变得更容易实现。
In an era when the industry is shaking off excessive up-front design, some will see large-scale structure as a throwback to the bad old days of waterfall architecture. But in fact, the only way a useful structure can be found is from a very deep understanding of the domain and the problem, and the practical way to that understanding is an iterative development process.
在当今这个时代,软件开发行业正在努力摆脱过多的预先设计,因此一些人会把大比例结构看作是倒退回了过去那段使用瀑布架构的令人痛苦的年代。但实际上,只有深入地理解领域和问题才能发现一种非常有用的结构,而获得这种深刻的理解的有效方式就是迭代开发过程。
A team committed to EVOLVING ORDER must fearlessly rethink the large-scale structure throughout the project life cycle. The team should not saddle itself with a structure conceived of early on, when no one understood the domain or the requirements very well.
团队要想坚持 EVOLVING ORDER 原则,必须在项目的整个生命周期中大胆地反复思考大比例结构。团队不应该一成不变地使用早期构思出来的那个结构,因为那时所有人对领域或需求的理解都不够完善。
Unfortunately, that evolution means that your final structure will not be available at the start, and that means that you will have to refactor to impose it as you go along. This can be expensive and difficult, but it is necessary. There are some general ways of controlling the cost and maximizing the gain.
遗憾的是,这种演变意味着最终的结构不会在项目一开始就被发现,而且我们必须在开发过程中进行重构,以便得到最终的结构。这可能很难实现,而且需要高昂的代价,但这样做是非常必要的。有一些通用的方法可以帮助控制成本并最大化收益。
One key to keeping the cost down is to keep the structure simple and lightweight. Don’t attempt to be comprehensive. Just address the most serious concerns and leave the rest to be handled on a case-by-case basis.
控制成本的一个关键是保持一种简单、轻量级的结构。不要试图使结构面面俱到。只需解决最主要的问题即可,其他问题可以留到后面一个一个地解决。
Early on, it can be helpful to choose a loose structure, such as a SYSTEM METAPHOR or a couple of RESPONSIBILITY LAYERS. A minimal, loose structure can nonetheless provide lightweight guidelines that will help prevent chaos.
开始最好选择一种松散的结构,如 SYSTEM METAPHOR 或几个 RESPONSIBILITY LAYER。不管怎样,一种最小化的松散结构可以起到轻量级的指导作用,它有助于避免混乱。
The entire team must follow the structure in new development and refactoring. To do this, the structure must be understood by the entire team. The terminology and relationships must enter the UBIQUITOUS LANGUAGE.
整个团队在新的开发和重构中必须遵守结构。要做到这一点,整个团队必须理解这种结构。必须把术语和关系纳入到 UBIQUITOUS LANGUAGE 中。
Large-scale structure can provide a vocabulary for the project to deal with the system broadly, and for different people independently to make harmonious decisions. But because most large-scale structures are loose conceptual guidelines, the teams must exercise self-discipline.
大比例结构为项目提供了一个术语表,它概要地描述了整个系统,并且使不同人员能够做出一致的决策。但由于大多数大比例结构只是松散的概念指导,因此团队必须要自觉地遵守它。
Without consistent adherence by the many people involved, structures have a tendency to decay. The relationship of the structure to detailed parts of the model or implementation is not usually explicit in the code, and functional tests do not rely on the structure. Plus, the structure tends to be abstract, so that consistency of application can be difficult to maintain across a large team (or multiple teams).
如果很多人不遵守结构,它慢慢就会失去作用。这时,结构与模型和实现的各个部分之间的关系无法总是在代码中明确地反映出来,而且功能测试也不再依赖结构了。此外,结构往往是抽象的,因此很难保证在一个大的团队(或多个团队)中一致地应用它。
The kinds of conversations that take place on most teams are not enough to maintain a consistent large-scale structure in a system. It is critical to incorporate it into the UBIQUITOUS LANGUAGE of the project, and for everyone to exercise that language relentlessly.
在大多数团队中,仅仅通过沟通是不足以保证在系统中采用一致的大比例结构的。至关重要的一点是要把它合并到项目的通用语言中,并让每个人都严格地使用 UBIQUITOUS LANGUAGE。
Second, any change to the structure may lead to a lot of refactoring. The structure is evolving as system complexity increases and understanding deepens. Each time the structure changes, the entire system has to be changed to adhere to the new order. Obviously that is a lot of work.
其次,对结构的任何修改都可能导致大量的重构工作出现。随着系统复杂度的增加和人们理解的加深,结构会不断演变。每次修改结构时,必须修改整个系统,以便遵守新的秩序。显然这需要付出大量工作。
This isn’t quite as bad as it sounds. I’ve observed that a design with a large-scale structure is usually much easier to transform than one without. This seems to be true even when changing from one kind of structure to another, say from METAPHOR to LAYERS. I can’t entirely explain this. Part of the answer is that it is easier to rearrange something when you can understand its current arrangement, and the preexisting structure makes that easier. Partly it is that the discipline that it took to maintain the earlier structure permeates all aspects of the system. But there is something more, I think, because it is even easier to change a system that has had two previous structures.
但这并不像听上去那么糟糕。根据我的观察,采用了大比例结构的设计往往比那些未采用的设计更容易转换。即使是从一种结构更改为另一种结构(例如,从 METAPHOR 改为 LAYER)也是如此。我无法完全解释清楚这是什么原因。部分原因是当完全理解了某个系统的当前布局之后,再重新安排它就会更容易,而且先前的结构使得重新布局变得更容易。还有部分原因是用于维护先前结构的那种自律性已经渗透到了系统的各个方面。但我觉得还有更多的原因,因为当一个系统先前已经使用了两种结构时,它的更改甚至更加容易。
A new leather jacket is stiff and uncomfortable, but after the first day of wear the elbows have flexed a few times and are becoming easier to bend. After a few more wearings, the shoulders have loosened up, and the jacket is easier to put on. After months of wear, the leather becomes supple and is comfortable and easy to move in. So it seems to be with models that are transformed repeatedly with sound transformations. Ever-increasing knowledge is embedded into them and the principal axes of change have been identified and made flexible, while stable aspects have been simplified. The broader CONCEPTUAL CONTOURS of the underlying domain are emerging in the model structure.
一件新皮茄克穿起来又硬又不舒服,但穿了一天之后,肘部经过若干次弯曲后就会变得更容易弯曲。再穿几天之后,肩部也会变得宽松,茄克也更容易穿上了。几个月后,皮质开始变得柔软,穿着会更舒适,也更容易穿上。同样,对模型反复进行合理的转换也有相同效果。不断增加的知识被合并到模型中,更改的要点已经被识别出来,并且更改也变得更灵活,同时模型中一些稳定的部分也得到了简化。这样,底层领域的更显著的 CONCEPTUAL CONTOUR 就会在模型结构中浮现出来。
Another crucial force that should be applied to the model is continuous distillation. This reduces the difficulty of changing the structure in various ways. First, by removing mechanisms, GENERIC SUBDOMAINS, and other support structure from the CORE DOMAIN, there may simply be less to restructure.
对模型施加的另一项关键工作是持续精炼。这可以从各个方面减小修改结构的难度。首先,从 CORE DOMAIN 中去掉一些机制、GENERIC SUBDOMAIN 和其他支持结构,需要重构的内容就少多了。
If possible, these supporting elements should be defined to fit into the large-scale structure in a simple way. For example, in a system of RESPONSIBILITY LAYERS, a GENERIC SUBDOMAIN could be defined in such a way that it would fit within a single layer. With PLUGGABLE COMPONENTS, a GENERIC SUBDOMAIN could be owned entirely by a single component, or it could be a SHARED KERNEL among a set of related components. These supporting elements may have to be refactored to find their place in the structure; but they move independently of the CORE DOMAIN, and tend to be more narrowly focused, which makes it easier. And ultimately they are less critical, so refinement matters less.
如果可能的话,应该把这些支持元素简单地定义成符合大比例结构的形式。例如,在一个 RESPONSIBILITY LAYER 系统中,可以把 GENERIC SUBDOMAIN 定义成只适合放到某个特定层中。当使用了 PLUGGABLE COMPONENT FRAMEWORK 的时候,可以把 GENERIC SUBDOMAIN 定义成完全由某个组件拥有,也可以定义成一个 SHARED KERNEL,供一组相关组件使用。这些支持元素可能需要进行重构,以便找到它们在结构中的适当位置,但它们的移动与 CORE DOMAIN 是独立的,而且移动也限制在很小的范围内,因此更容易实现。最后,它们都是次要元素,因此它们的精化不会影响大局。
The principles of distillation and refactoring toward deeper insight apply even to the large-scale structure itself. For example, the layers may initially be chosen based on a superficial understanding of the domain; they are gradually replaced with deeper abstractions that express the fundamental responsibilities of the system. This sharpedged clarity lets people see deep into the design, which is the goal. It is also part of the means, as it makes manipulation of the system on a large scale easier and safer.
通过精炼和重构得到更深层理解的原理甚至也适用于大比例结构本身。例如,最初可以根据对领域的初步理解来选择分层结构,然后逐步用更深层次的抽象(这些抽象表达了系统的基本职责)来代替它们。这种极高的清晰度使人们能够透彻地理解领域,这也是我们的目标。它也是一种使系统的整体控制变得更容易、更安全的手段。