敏捷有适应性。什么时候以及如何应用架构取决于环境。本文首先解释了为什么是这样,然后说明了在敏捷环境中怎么样才能仍然给予架构足够的重视。适应性和对话是基本要素。
敏捷宣言十年前就已经问世,它的出现是对当时作为标准的计划驱动方法的一种响应。在计划驱动方法中,计划工作和开展工作是单独的步骤。编写好的计划需要一个稳定的基础:明确的需求。因此,确保需求稳定是一个明确的要求。基于那些稳定的需求,可以创建一个同样稳定的架构,并随后实现。
事实上,这是相当困难的,因为人类天生就不是那样的。你们是否曾经参与过一个这样的项目,它开始的时候有一个定义良好的、完整的需求说明书,你们带着它回到团队,可能在国外,并制定了一个刚好十八个月的计划,然后无需进一步的工作就可以开始满足客户需求?我们没有。客户会逐渐发现他们想要什么,开发团队会发现如何创建它,而与此同时,环境往往也在变化。
因此,敏捷有一种不同的计划方法,它拥抱所有会为客户增加价值的变化。该方法没有提供构建“大设计(big design)”的基础,也让我们没有意愿构建这样一个设计,因为那样的设计很脆弱。这不是说架构不再重要,不过,它必须健壮,能够适应未来的变化。那究竟上意味着什么?得看情况。还要提前做多少工作?够用就好。
你是不是已经知道这些答案?在敏捷宣言宣告敏捷十年后的今天,你是不是希望我们现在可以更具体一点?是的,我们也是如此!而且现在就挺好,因为我们的客户——我们组织内的业务方——期望值越来越高。成功不再是根据计划交付,而是要交付能够影响实际业务的软件。像“影响地图(Impact Mapping)”、“功能注入(Feature Injection)”、“用户故事映射(User Story Mapping)”等技术迅速普及;它们中的每一个方法都能帮助我们构建适当的产品。而为了构建适当的产品,具有有效架构的方法不能停止不前。由于架构是一个涉及面很广的话题,所以为了讲得更清楚,我们将接下来要重点探讨的架构定义为软件架构:单个应用程序的架构。
采用测试驱动开发对代码设计非常有益。我们发现,它在我们由外向内工作时尤其有用,就像 TDD伦敦学校 所倡导的那样从验收测试向内。由外向内工作减少了增加各种if判断代码的趋势。而且,你测试类的交互方式,测试并不是在以最严格的方式应用单元测试时创建的。根据我们的经验,这些就是那些实际上可以为重构提供支持的测试,而不是障碍。
现在,如果你也编写漂亮整洁、可以与读者清晰沟通的代码,那么几乎可以肯定,你在细节上有一些非常好的东西。但整体上怎么样呢?你如何用文档记录通过代码不容易看出来的总体设计的方方面面?你如何讨论它们,并达成一致?有一些容易理解的“ 架构草图 ”是不是有好处呢?
你可能不止一次绘制过草图,不同于我们在项目中所绘制的。那我们可以问一下,谁在场吗?架构师,领导,管理人员,还是你的整个团队?后续在哪里重新审视和修改这些草图?如果是这样,那对项目有价值吗?如果不是,什么妨碍了你?
在我看来,作为一个团队,创建并维护这样的资料是架构的基本组成部分。通过草绘架构并与所有参与编码的人进行讨论,可以将应用程序层面的关注点与繁琐的细节联系起来。TDD提供了一种做详细设计决策的框架,但是架构活动才能帮助你创建健壮的软件。虽然几乎任何东西在白板上看起来都不错,但缺陷还是会在代码中形成,告诉你架构模型还不够完善,或者不再完善了。
有一个常见的误解,就是敏捷不需要特别关注架构;于是,它先期就同定义架构的文档一起被扔掉了。“ 敏捷宣言原则 ”中提到的层创概念被解释为自然出现。一个更好的解释是:我们一起逐步地做那件事。架构是一种团队活动和共同的理解,而不是一个从象牙塔中传递给团队的文档。
最好的架构、需求和设计源于自组织团队。
——敏捷宣言原则#11
“ 层创属性(emergent property) ”的特点是,它不能脱离派生它的系统部分而存在。我们无法由它追溯到一个特定的部分,但它影响着系统的每一部分。例如,当你去除了分子,水就没有了流动性。单个分子是不流动的,但水作为一个整体是流动的。类似地,没有应用程序和团队,也就没有(层创)架构。你无法指出一个决定架构是什么的架构师。但共享的概念框架影响着团队的行为以及应用程序的样子。传统的流程中没有这种说法。在这里,我们再一次看到,敏捷真地带来了观念的改变,专注于人与交互。
那么还有专职的架构师吗?也许有,虽然与以前的角色不同。当与多个团队一起开发一个应用(或者是姊妹应用)时,有其他人关注全局问题有助于团队集中精力。这样,架构师就将架构作为一个基本关注点,但他致力于服务团队,而不是向团队发号施令,或者比团队超前工作。他知道,如果他使团队等待一个完美计划的话,他就没有价值了,而且他们可以通过实现架构来帮助他证明架构的合理性。他会实验和验证,通过“ 峰值(spikes) ”以及定期与团队成员一起编码。在架构会议上,他会不断地问团队,他们讨论的内容是否会与他们将来实际编写的代码一致。
对变化有较好健壮性的架构是,在任何需要的时候都确实很容易变化的架构。这样的架构不会没必要地限制你的可选项。每当你做架构决策的时候,尤其是以后变化会很难的决策,你就限制了你处理变化的能力。只有在知道为什么做那样的决策的那个特定时刻,你才能作难以变化的决策,并且要本着负责任的态度不断的寻找机会延期决策。这就像在你的个人生活中:你会立即买一张音乐会的票,因为你估计很快就会卖光。但晚上与几个朋友在酒吧里聚会可能是最后一分钟才做出的安排。此外,这意味着:循序渐进。不要让应用程序处理(尚且)不必要的事情,但要确保后续容易添加。换句话说,通过使架构具有可扩展性,减少变化需求。将“ 开放—封闭原则 ”应用到架构。在接下来的两节中,我们将更具体的进行描述。
简单——使未完成的工作最大化的艺术——是根本。
敏捷宣言原则#10
循序渐进还意味着追求简单,简化到再进一步简化就会去掉重要的价值。这是说积极删除几乎没有价值的功能。它同样也关乎应用本身的结构。从清晰的架构图到整洁的代码实现,在任何层面上都是越简单越容易理解。要把大量注意力放在组件布局上,并简化组件本身。简单的软件是有适应性的软件!
如何将应用划分成组件?正当你打开开发环境的时候,应用程序告诉了你什么?我希望是它所提供的功能,而技术和框架不是它十分关注的。Alistair Cockburn 几年前就建议 ,以同心圆方式分解比层叠方式更自然。使用这种分解方式的 架构模型 最近引起了人们的注意,并达到了有望成为新标准的程度。
如果愿意,这样一种设计的内核可以始于应用程序提供的功能、应用程序特性或者用例,如购买旅行卡、更换旅行卡、取消旅行卡。遗憾的是,虽然这些特性是客户所需要、应用程序所需提供的,但在许多应用程序中,要找出它们非常困难。我们希望将它们与它们所操纵的业务域实体区分开来。这样,我们就围绕业务域把它们创建成一个单独的层。你会发现,内核周围有连接外部世界的接口。不只是连接用户界面,还包括连接数据库以及与其它系统互连的接口。你立马就会发现,最有可能变化的东西容易调整了:新增一个数据库、一个移动用户界面、提供一个服务接口等等。
无一例外,所有的依赖都是指向中央的。借助 依赖注入 ,每一层都完全独立于其它层。外圈中的各种接口组件也不知道彼此的存在。
我们就像上面这段时间刚刚讨论过的那样设计架构。我们已经体验到了许多好处,不过有时候还是不满意。随着应用程序的扩展和时间的推移,内核的体量往往会开始成为我们的沉重负担。该模型基于这样一种假设,由于实体代表了业务规则——最好是企业范围内有效——所以它们很少会变化。正是这种稳定性使它可以作为其它所有一切的基础。但仅仅是逐步实现业务规则,随着我们迭代增加特性,我们已经破坏了那种稳定性。企业的各个部门以及他们的系统通常也没有统一稳定的业务实体定义;它们取决于环境,也会随着时间发展变化。
在某些时候,我们应该放弃一个统一内核的概念,转而采用来自领域驱动设计的概念“ 有界上下文 ”。一个将两个有界上下文分成两个独立模型的著名例子是“命令查询职责分离(Command Query Responsibility Segregation,缩写为 CQRS )。应用程序可以用多个类似的独立模型服务于不同的业务上下文。这些上下文是为了允许模型针对不同的——通常是组织方面的——原因以不同的速度变化。方便起见,我们必须打破它们之间的“二进制依赖(binary dependencies)”。
Russ Miles建议我们那样做并转而采用简单事件进行组件间通信。一个人能够获得的自由非常有用,但它也是权衡的开始。例如,你是只想轻度解耦,还是准备好完全放弃利用交换强类型对象的优点,从而换取可提供的灵活性?
假如与主机的接口每年更新两次,而且日期完全无法预测。同时,企业几乎每个周都想更改他们提供的保险产品的业务规则,以便与市场机遇保持一致。你大概会希望在不同的组件中处理这两个问题,它们之间不存在任何方向上的二进制依赖。但是,这两个组件是否可以同时依赖于介于它们之间的一个强类型接口,或者只是通过一个没有明确模式的、非常松散的数据结构进行交互,以便提供你所需要的灵活性?你可以拥有灵活性,但那会使两个组件都更复杂,而且为了确保只将兼容的组件投入生产环境,你需要进行更多的处理。
你发现自己在平衡灵活性和复杂性,而这种平衡很可能会随着时间变化。这使得将功能明智地分组成组件很重要,而且每当新需求出现时,要重新审视分组,保持对架构的持续关注。因此,在Russ看来,这既是一个模型,也是一个 过程工具 。他将其称为救生圈。
当运用了我们讨论的技术,你的应用程序会是什么样子呢?我们不熟悉你的项目,也不相信象牙塔架构,所以我们不打算告诉你。它或许会反映你所面对的组织和约束。 Conway定律 有类似的描述,或许,那其实是件好事。
设计系统的组织……受到限制,产生的设计是对这些组织的沟通结构的复制
——M. Conway
因此,这就是我们送你上路的地方。从已知的规则入手:分割问题,寻求有限数量的、强内聚少依赖的组件。加上我们描述的敏捷原则:对变化持开放态度、团队精神、协作、层创、简单,最重要的是可用的软件。
让它成为你的思维方式,并且行动起来,不只是作为指定的架构师要如此,作为开发人员也要如此。沿着这条道路边做边学至关重要。问下自己,现在预先完成的工作有哪些部分团队已经可以不做,什么状况可以显示团队已经找到了更高效的满足需求的方式。祝你好运!
Wim Heemskerk 为团队实践敏捷提供帮助。他是一名敏捷人士、软件工匠和Stoosian。作为一名富有经验的变革推动者,他增加流程、技术和组织工作的一致性。Wim将互补模型及其原则串联起来转化为日常行动。他致力于创建可持续的变革,就是人们常说自己创建和想要的那种。他还为其他探索敏捷、领导力、优秀软件和真正有效的测试自动化的人提供支持。他的Twitter账号是 @WimHeemskerk 。
Minze Tolsma 是一名软件工匠,他对人们的工作和思考方式有浓厚兴趣。他愿意接受一切可以增加客户价值的方式。他坚信“身后一步(from one step behind)”的领导方式:保持灵活性,作为一名工匠,要设法完全了解所服务的企业,然后利用所有配备的工具行动起来。Minze为iPROFS(荷兰哈勒姆)工作,是一名软件架构师和知识管理人员。你可以在 Twitter 和 LinkedIn 上找到他。
查看英文原文:Agile Architecture Applied