程序员必读之软件架构书摘
什么是架构
"架构"作为名词的一种理解:
从产品整体考虑,采用一定的结构,将产品分解为一系列组件、模块和交互。
比如考虑处理软件的安全、配置、错误处理等横切关注点的基础设施服务。
"架构"作为动词的一种理解:
以需求为基础,通过需求驱动架构。架构是关于交流愿景以及引入技术领导力的,使得参与构建产品的每个人都能理解这个愿景,并为产品的成功做出积极贡献。
因此架构包括了理解你需要构建什么、设定愿景以便于构建和做出恰当的设计决策。
架构的种类
在 IT 行业就有很多不同种类的架构和架构师,诸如:基础设施、安全、技术、解决方案、网络、数据、硬件、企业、应用程序、系统、数据库、信息、流程、商务、软件…
虽然很多不同,但它们有个共同点:都具有结构和愿景。
"基础设施架构"为例,要在两个远隔千里的办公室建立网络连接。为了达到这个目标,要考虑很多环境约束和非功能特性(如距离、成本等),这就是架构的过程以及设定实现目标愿景的重要之处。
采用一条很长的线缆是一种方法,但现实里不可行,因此连接网络需要一组协同工作的组件来满足目标。
从基础设施的角度触发,谈论结构时,其实是在期望看到的是这一领域内的通用组件,如路由器、防火墙、交换机等。
所以,不论何种领域的架构,任何成功的方案都需要你理解问题,并设定一个愿景可以喝每个参与构建最终产品的人沟通。
因此架构的主要就是结构和愿景。
软件架构是什么
首先需要明确,软件架构并不局限于软件。
应用程序架构
应用程序(如 Java网络应用程序、Windows桌面应用程序等等)架构的关注点是应用程序,通常包括将应用程序解构为类和组件,确保设计模式的正确应用,构建或使用框架,等等。
本质上,应用程序架构谈论的是软件设计的低级别切面,通常只考虑单一的技术栈(如Java、.NET 等),着重考虑软件和代码组织。
结构单元主要以软件为基础,包括编程语言和结构、类库、框架、API等。它由类、组件、模块、函数、设计模式等加以描述。
系统架构
系统架构可以看做是更大规模的应用程序架构。大多数软件系统实际上是由横跨不同层次和技术的多个应用程序组成。
如一个软件系统,Java EE 中间层消费 Oracle 数据库提供的数据,同时向 .NET Silverlight 客户端提供 Web 服务。每个部分都有自己的应用程序架构。
要让整个软件系统工作起来,就要思考如何组合这些单独的应用程序。
因此系统架构不仅要有整体结构,还关注互操作性和与环境中其他系统的集成。
结构单元就是各种软硬件,从编程语言和软件框架到服务器和基础设施。
跟应用程序架构相比,系统架构描述为从组件和服务到子系统等更高层次的抽象。
系统架构的定义大多数都包括了软件和硬件。
软件架构
人们对"软件架构"的理解不尽相同,简单理解的话,可以认为软件架构就是应用程序和系统架构的结合。
换句话说,从代码结构和基础到将代码成功部署到生产环境,与一个软件系统重要元素相关的所有东西就是软件架构。
因此需要考虑有助于构架更好软件的东西,比如面向对象的原则、类、接口、控制反转、重构、自动化单元测试、代码整洁等等各类技术实践。以及其他需要考虑的,诸如:
- 横切关注点,比如登录和异常处理;
- 安全性,如认证、授权、敏感数据保密
- 性能、可伸缩性
- 互操作性,与其他软件系统的集成
- 运营、支持和维护的需求
- 结构和整个代码库方法的一致性
- 评估正在构建的基础有助于交付按计划进行
同样,有时也需要退一步,原理代码和开发工具,这不意味着低层次的细节不重要(可用的软件最终还要考交付可运行的代码)。细节固然重要,但从大局来看,对软件的整体视角可以确保代码符合整体愿景。
企业架构:战略而非代码
企业架构一般是指整个组织的中心工作,着眼于如何组织与利用人员、流程和技术来使企业有效和高效地工作。
换句话说,它是关于企业如何分成组或部门,业务流程如何在上层运作,以及技术如何支撑这一切。
所以,企业架构看重的是如何在整个组织中最好地利用技术,而无需实际介入这些技术的工作原理。
从事企业架构工作所需要的思维方式和软件架构大相径庭,视角很不一样。
企业架构需要更高层次的抽象,它关乎广度而非深度,关乎战略而非代码。
因此开发者和软件架构师把企业架构看作职业发展的下一站需要转换思想。
什么是敏捷软件架构
软件开发的敏捷方法是指:快速行动,拥抱变化,持续交付,接收反馈,不一而足。
因此给软件架构打上"敏捷"一词,表示它能够应对所处环境中的变化,适应不断变化的需求。
但以敏捷方式交付软件并不能保证得到的软件架构是敏捷的,往往可能是因为团队更关注交付功能,而非架构。
如何理解“敏捷”
美国空间战斗机飞行员 JohnBoyd 提出了个 OODA 循环的概念(观察、定向、决策和行动,Observe、Orient、Decide、Act)。这个循环构成了基本的决策过程。
打个比方,你是一个与敌人缠斗的战斗机飞行员,为了击败对手,你要观察情况,确定自己的方位,决定做什么,并采取行动。为了避免被对手击落,这个循环要执行得尽可能的快。如果你洞悉对手的OODA循环,执行得比他更快,就能混淆视听,误导对手。也就是,如果你比对手更敏捷,就能成为最后的赢家。
不列颠哥伦比亚大学的史蒂夫·阿道夫在论文《What Lessons Can the Agile Community Learn from A Maverick Fighter Pilot》引用了JohnBoyd的概念并将其应用于软件开发,得出的结论是:敏捷是相对的,且按时间来衡量。
如果你的软件团队交付的软件跟不上所处环境的变化,就不算敏捷。如果你在一个庞大而行动缓慢、鲜有改变的组织中工作,很可能交付软件要花费数月,却仍被组织认为是“敏捷”的。
好的架构带来敏捷
基于微服务的架构,使得每个服务只专注做好一件事,一个微服务通常可能不到100行代码。如果需要改变,服务可以用另一种语言重新编写。这种架构以多种方式提供了敏捷。
然而构建这样的软件系统需要时间、精力和准则,很多人也不需要这种水平的适应性和敏捷性。
因此,各部分捆绑在一起并以单一单元部署的整体架构就有了存在的合理性。当然整体架构虽然易于构建,但面对变化的需求时通常要花费更多精力去适配,因为功能往往交织在代码库中。
所以,这两种架构各有优缺点,应该在权衡利弊之后,再决定是构架一个整体系统还是几个微系统。
从实用主义出发,可以选择构建一个由很多定义好的小组件构成但仍作为单一单元部署的软件系统,这样,如果将来要迁移到微服务架构时,也可以相对轻松些。
需要多敏捷呢
理解组织或业务变化的速度很重要,因为这能帮助你决定采用何种架构风格,可能是整体架构、微服务架构或者介于两者之间。要理解这种权衡并做出相应的选择。
架构 VS 设计
对于架构和设计的区别,格雷迪·布奇(Grady Booch)在On Design一文中有些说法常被引用:
1、作为名词,设计是指一个系统内命名的(尽管有时无法命名)结构或行为,解决或有助于解决该系统的一个或多个问题。因而设计代表了潜在的决策空间中的一个点。
2、所有架构都是设计,但并非所有设计都是架构。
3、架构反映了使一个系统成型的重要设计决策,而重要性则通过改变的成本来衡量。
因此,一个解决方案本质上就是一次设计练习。
总结而言就是,重要决策即“架构”,其他的都是设计。
所以“架构”就是要去思考软件系统中哪些是重要的(或者说“架构的”),比如:
- 系统的形态(例如,客户端-服务器、基于Web、分布式、异步等等)
- 软件系统的结构(如,组件,层,交互等)
- 技术选择(如编程语言、部署平台等)
- 框架选择(如Web MVC框架、持久性/ORM框架等)
- 设计方法/模式选择(如针对性能、可伸缩性、可用性等)
软件架构的角色
要成为一名软件架构师,绝非一夜之间或一次晋升。这是一个角色,而不是一个级别。这是一个循序渐进的过程,会逐渐获得这个角色所需要的经验和信心。所以,这里所说的“角色”:它可以是一个人,也可以由团队共同扮演。
许多软件架构师都是构建大师,所以经常练手是有意义的。此外,编码为架构师提供了一种与团队分享软件开发经验的方式,从而帮助他们更好地理解如何从开发的角度看待架构。
许多公司都有阻止软件架构师参与编码工作的政策,因为他们的架构师“太宝贵了,不该承担日常编码工作”。这显然是错误的,如果你不打算让软件架构师为成功交付做出自己的贡献,为什么还要让他们为软件设计投入全部精力?
当然,有些情况下要参与到代码级别并不实际。例如,一个大型项目通常意味着要照看更大的“大局”,有可能你根本没时间写代码。
但是一般来说,一个写代码的软件架构师会更有成效也更快乐。你不应该因为“我是架构师”,就把自己排除在编码之外。
建议是让编码成为你作为软件架构师角色的一部分,只要把自己当作软件开发团队的一份子就行了。
换句话说,你有一顶软件架构的帽子和一顶编写代码的帽子。
你不见得要成为团队里写代码最厉害的,但参与到实践和交付流程的好处非常大。毕竟,“知”和“行”还是不同的。
创建能实际实现的软件架构,这样做的好处显而易见,除此之外,贡献代码还能帮助你和团队建立起融洽的关系,有助于缩短存在于很多软件团队的架构师和开发者之间的距离。
引用瑞秋·戴维斯(Rachel Davies)和丽兹·赛德利(Liz Sedley)在《敏捷教练:如何打造优秀的敏捷团队》一书中说的话:
如果你了解如何编程,往往会忍不住对开发者该如何编写代码提出建议。小心,因为你可能在浪费时间:如果你没有参与项目的编程,开发者多半会无视你的编码经验。他们还会认为你越权,影响了他们的工作,所以尽量别在这方面指指点点。
重申一下我的建议,软件架构师不必放弃编码。无论你怎么做,在不断变化的世界中,编码是一个保持技术能力的好办法。
很多人认为软件架构是一种“后技术”的职业选择,但除了丰富的经验和更宽的知识面,它还需深厚的技术能力,需要能够回答设计是否真的管用这类问题的T形人才。把这归为“实现细节”是不可接受的。
只是别把时间都花在编码上。如果你花全部时间写代码,那软件架构角色的其他部分由谁来扮演?
需要知道的是,软件架构师是通才型专家,我所知大部分最优秀的软件设计师都有软件开发背景。这并不意味着他们是团队中最好的程序员,但他们能够在底层细节和大局之间切换。他们还有着深厚的技术积累,以及从多年软件构建的经验中获得的广阔的知识面。但他们不能(也不会)总是知道一切。
如果你是软件架构师,需要做到两点:
- 包容与合作:让开发团队参与软件架构的过程,帮助他们了解大局,认同你所做的决策。确保每个人都明白决策背后的原理和目的,会对此有所帮助。
- 动手:如果可能的话,参与一些项目的日常开发工作来提高你对架构交付的理解。根据你的角色和团队规模,这可能会不太现实,那就通过其他方式来了解底层的进展,比如协助设计和代码评审。了解软件的底层如何工作会让你更透彻地了解开发团队对架构(比如:他们是否对其视而不见)的感受,也会为你提供有价值的信息,可以用来更好地塑造/影响架构。如果开发者感到痛苦,你也要感同身受。
为软件生成文档
软件文档所需要涉及的部分:
语境:
- 这个软件是关于什么的?
- 构建的是什么?
- 它如何融入现有环境?(比如系统、业务流程等)
- 谁在使用?(用户、角色、参与者等)
功能性概览:
- 系统实际上做什么是否清楚?
- 哪些特性、功能、用例对架构是重要的,原因是否清楚?
质量属性:
- 对于脚骨必须满足的质量属性是否清晰?
- 质量属性是否满足SMART原则?(具体、可衡量、可达成、相关、及时)
约束:
- 时间、预算、资源
- 目标部署平台
- 允许使用的技术清单、技术约束
- 标准消息格式
- 开发团队规模
- 开发团队技能配置
原则:
- 架构分层策略
- 接口的使用
- 依赖注入
- 好莱坞原则(不要给我们打电话,我们会给你打电话)
- 高内聚、低耦合
- 遵循SOLID(单一职责、开闭原则、里氏替换原则、接口隔离原则、依赖倒置原则)
- 不要重新发明轮子
- 错误处理、日志等的通用方法
软件架构:
- “大局”看起来怎么样?(概念视图或逻辑视图)
- 是否有清晰的结构?
- 展示了主要的容器和技术选择了吗?
- 展示了主要的组件及其交互了吗?
- 关键的内部接口有哪些?
外部接口:
- 暴露出来的 API
- 接口多久会有变化,版本怎么处理?
代码:
- 数据绑定:根据 HTTP POST 请求更新业务对象的方法
- 组件框架
- 架构分层:分层策略和用来实现的模式的概览
- 异常和日志
- 模式和原则:解释模式和原则如何实现
数据:
- 数据模型看起来是什么样?
- 数据存在在哪里?
- 数据需要多少存储空间?
- 归档和备份策略是什么?
- 日志文件和审计跟踪是否有类似要求?
基础设施架构:
- 是否有清晰的物理架构?
- 是否满足冗余、故障转移和灾难恢复?
- 有照管通用基础架构(如数据库、路由器、交换机、应用程序服务器、反向代理等)的中心团队吗?
- 开发、测试、验收等是否有合适的环境?
部署:
- 软件安装和配置软件在哪里,怎么做?
- 软件如何部署到基础设施上?(如么给服务器多个容器等)
- 部署和回滚策略是否已经定义?
- 软件或基础设施出现故障时会发生什么?
运营和支持:
- 运营人员如何诊断问题?
- 错误和信息记录在哪里?
- 更改配置是否需要启动?
- 有需要定期执行的手动管理任务吗?
软件架构总结
概括来说,架构就是结构和愿景,这个过程的关键在于理解重要设计决策。
敏捷和架构并不冲突。
考虑架构的驱动力不需要花很长时间,却能为软件设计的其他部分提供一个开始。经验就是:小方向有大用场。