在 Dr. Dobbs 期刊最近探讨的几个问题的其中一个里,期刊编辑 Andrew Binstock 写了一篇文章 1 探讨敏捷活动中一个特别的问题:在架构和设计领域目前没有敏捷的足迹。在参阅了关于这个主题的几篇文章后,他注意到,我们很少会碰到任何像“敏捷设计”和“敏捷架构”这样的阶段,既然如此,他得出结论,“敏捷方法显然遗漏了敏捷架构”。我倾向于赞同他的观点。但为什么到目前为止会是这种情况呢?目前这种情况的形成可能有多个潜在的原因,但在我看来,有一个原因特别突出。
虽然看上去好像是常识,但迭代开发这一简单做法是它最强大的实践方法之一;我们甚至可以说,迭代是敏捷方法的核心原则之一。不管一个开发团队使用简单 Scrum 还是极限编程,他们都可以反复对代码主体进行迭代和重构。但是,对于架构和设计,我们可以对它进行同样迭代和重构的内容主体是什么?设计会议会产生一些模型和一般文档,但通常,它们不会形成一个详细的、有条理的设计描述。在项目管理者和架构师与开发人员最终沟通之前,那些珍贵的细微差别都会像蒸汽一样悬在空中,只有在团队编写了实际的代码后,架构的那些细节才会凝结成形。虽然那时架构已经有了具体的实现形式,但它却从来没有一个原始的形式。团队从来都没有机会只在设计上实践敏捷方法,因为设计从来都没有机会仅仅自己独立地存在。
通常,在使用元数据的时候,它仅仅是作为一个可配置的数据集而存在。该数据集决定了一个应用程序某些方面的行为。C#属性可以指定已定义类的首选模式,一组.properties 文件包含了一个 Java 应用程序为创建一个数据库连接所需要的值。在某种程度上,我们可以单独地仔细查看这些元数据,并推断它对程序运行的预期影响。在某些情况下,它甚至有能力讲述一个故事,而且如果经过了仔细地组织,它可以讲述整个架构的故事。说实话,完全相同的元数据也不能驱动这样一个架构是没有理由的,不过,我们会在稍后再介绍那部分。
在 InfoQ 最近的一篇文章 2 中,徐涵提出了一个问题,敏捷项目是否应该具有创新性。在开发过程中的敏捷和创新有些问题,但如果在设计阶段将元数据当作中介,敏捷和创新将可以和谐地发挥作用。为了说明元数据如何作为架构的粘合剂,让我们想象一个简单的场景。在这个场景中,我们试图设计一个负责各种产品数据的分布式架构的服务器部分(流程、系统,等等),它的目标之一是监听客户请求,并将任何附加的数据持久化到数据库中:
(点击查看大图)
这个电子表格可以作为领域驱动设计的第一次增量迭代:元数据驱动设计。尤其重要的是,架构现在以一种不同于简单摘记和图表的形式而存在。在这个特殊的情况下,元数据是一种类似数据库表的存在,但对其它团队而言,它可以以一种更熟悉的格式(如XML)存在。无论如何,我们都已经创建了预期功能的一个表格式可扩展模型,所有的参与者都能理解和调整。现在,让我们展示一下使用敏捷方法在这份元数据上迭代的能力。在这种情况下,我们假定的干系人选出了一位经理、一位设计人员和一位开发人员,建立了一个三人小组,然后给他们分配了重构和改进原始设计的任务。由于现在我们有了实际的设计描述媒介,我们也就可以使用可行的指标衡量团队绩效,这比使用“虚荣指标(vanity metrics)”3 有额外的好处。通过讨论及比较笔记,我们虚拟的三人组认识到,这些属性中的部分属性(比如属于价格的那些属性)应该作为一个集合进行存取,而且为了与业务需求保持一致,这些集合应该作为不同的子集(如“域(field)”)进行权限分配。所以,为了重构设计,他们要对这些元数据进行迭代:
通过使用一个结对编程(增加了一个人)的变种对元数据进行迭代,我们在一个设计会议上使用了敏捷方法_voilà_。当然,随着我们针对干系人的需求对设计进行裁剪,我们可以继续对其做进一步的完善,但可以肯定地说,我们已经说明了元数据驱动设计的潜力。
虽然不经常被提及,但这个例子也凸显了敏捷的一个意义最深远的好处:它作为交流工具的角色,不管是在开发人员同行之间,还是在管理人员与其下属之间。为了取得成功,软件的具体实现需要充分的合作,但对一个项目而言,最大的风险是最初的设计(以及后续的设计规范修改)没有充分传达给整个团队。由于项目设计阶段通常不会预先设定一种结构化的格式,所以在这段时间里很难应用敏捷;然而,如果选定一种描述设计的元数据格式,那么应用敏捷就非常简单了。但更重要的是,在任何应用了敏捷的会议中,使用指定格式可以更快地传达架构的目的和意图,而不仅仅是一堆松散的模型和由此生成的类。如果元数据是架构的肖像,如果一幅画胜过千言万语,那么你只需要对这个有组织的结构及其值域进行少量更改就可以与任何经验丰富的开发人员沟通大段的设计变更。这种方式,尤其是元数据可以看作任何软件实现的燃料。
像燃料这样一种比喻说法可能是对元数据驱动设计的一个恰当描述,作为一种转录架构或它的其中一个子系统的思路的方式……但为什么不更准确地描述这种观点呢?我认为,此元数据不仅可以作为设计人员的“共鸣板(sounding board)”,而且也可以用于帮助创建实际的软件。就像UML 可以用于生成类甚至整个Spring 框架应用程序4 那样的情况,这里可能会有一种有用的开源解决方案;如果没有,一个小型团队就可以创建一个工具,从表生成这样的类。回到我们先前假想的示例,想象一下,如果我们为实现那个目的创建这样一个工具,然后基于这些属性行生成一个类:
在这个场景中,我们虚拟的干系人会生成一个包含这些属性的Product 类,以及需要由开发人员实现的空存根方法。这看上去多少可以作为一个令人满意的解决方案,但是总还会有改进的空间。假如那样的话,我们将重新回到先前那个示例的故事,并且假定,先前的三人组有同样的改进观点。在尝试将元数据(例如,设计转录)与代码(例如,实现转录)结合起来的过程中,他们决定将设计和实现看作一个整体同时进行迭代。在对架构元数据(包括将属性组织成单独的组)进行了一些调整后,他们为一组新类生成了UML:
这样对设计和实现进行迭代之后,就不再需要使用元数据生成类,因为我们现在把它与代码整合在了一起。事实是,设计现在更复杂了,但也有好处。现在,架构的数据设计在任何时候做了任何变更,代码都会立即检测到那些变更并做相应的调整。当一个新属性引入数据设计并插入元数据表时,代码很可能不需要任何修改,因为它以线性方式处理所有数据点。此外,Product 类的操作(如_readData()和persistData()_)很少需要修改,因为这种迭代整合已经创建了一种系统化的方法,使通用处理成为可能。现在,敏捷可以同时应用到设计和实现了。实际上,我们可以使用许多敏捷工具帮助我们进一步对设计进行迭代。
这个深入探讨元数据驱动设计的具体例子还有额外的好处。比如说,它有心理方面的好处,它可以提高士气,并增加管理人员和开发人员之间的信任。尤其是在临近里程碑或重要时限时,管理人员和开发人员的关系可能会比较紧张,因为需求说明似乎每天都被重新考虑和调整。不过,如果在元数据驱动设计上有足够的投入,每一处修改附加到相关任务的开销就会大大减少,给团队带来的工作量和产生的压力也就越小。通过修改元数据记录满足新的或不同的需求,管理人员随即就可以在软件行为上观察到想要的变化。通过赋予管理人员这种能力,这种操作模式使管理人员可以从始至终对团队的预期交付能力进行确认。通过受到到元数据的约束,管理人员和开发团队有一个共同的基础,他们可以基于这个基础构建工作环境。
当然,之前假想的场景适用于企业编程和/ 或为那个领域专门定制的软件,而且它包含了一个更专有的实现,缺少通常对可信赖的开源解决方案的融合。这些因素可能会阻碍一些人考虑元数据驱动的设计,因为它可能看上去与他们的项目无关。不过,如果一个注意到了从这个特定的练习中抽象出的信息,那么学到的经验就是如果将这种特定的方法应用到任何给定的问题上。同精益方法非常像,元数据驱动设计不只是被软件使用,还可以用于帮助解决软件问题,而不只是简单的数据设计。对于使用缺少解决方案(或者缺少资金购买解决方案,这极有可能)的小众系统的科学家而言,它可以用于大概描述一个可以自动处理数据记录的脚本框架,就像上文的例子,设计会议元数据甚至都可以与它集成。在其它情况下,它可以简单地用于创建一个在两个不同的公司之间相互沟通的契约。如果他们胆量够大,那么他们甚至可以使用已创建的元数据作为代码生成的基础,很像远程过程调用(如果你年纪足够大还记得那个东西的话)的存根生成。通过将元数据看作实现的潜在搭档,这种方法有可能将领域驱动设计导向一个最优的结论。在任何情况下,元数据驱动设计或许都会为你寻找解决方案提供一些帮助。
关于作者
Aaron Kendall是来自纽约的一名软件工程师,他有将近 20 年的企业数据系统设计和实现经验。在成为一名设备驱动和专业软件开发人员之后,他开始对软件设计和架构充满热情。他已经使用多种平台和语言创建了创新的业务解决方案以及许多自由软件项目,从开源包到游戏设计和移动应用。关于他的工作,如果你希望了解得更多,那么欢迎访问 LinkedIn 和他的博客。
参考资料
- Binstock Andrew(2014.11.4) 敏捷架构研究 Dr. Dobb 期刊
- 徐涵(2015.1.6) 敏捷项目应该面向创新吗? InfoQ
- Ries Eric(2011.9) 精益创业:如今的企业级如何使用持续创新创建极为成功的企业
- Avram Abel(2013.11.14) GenMyModel:一种在线实时协作 UML 设计器 InfoQ
查看英文原文:**** Metadata Driven Design - An Agile Bridge Between Design and Development
评论