在任何程序(可以向外延伸到其他很多领域)的生命周期中,复杂性都会不可避免地增加。
程序越大,工作的人越多,管理复杂性就越困难,程序员在修改系统时将所有相关因素牢记在心中变得越来越难;这会减慢开发速度并导致错误,从而进一步延缓开发速度并增加成本。
很多大型系统的本质问题是复杂性问题,数百个甚至更多的微服务相互调用/依赖,组成一个组件数量大、行为复杂、时刻在变动(发布、配置变更)当中的动态的、复杂的系统。
如果,我们将领域问题的复杂度与技术细节的复杂度混合在了一起,这最终将导致——整体复杂度的指数级增长。
复杂性的一个衡量维度:
一致性是降低系统复杂性并使其行为更明显的强大工具。如果系统是一致的,则意味着相似的事情以相似的方式完成,而不同的事情则以不同的方式完成。一致性会产生认知影响力:一旦您了解了某个地方的工作方式,就可以使用该知识立即了解其他使用相同方法的地方。如果系统的实施方式不一致,则开发人员必须分别了解每种情况。这将花费更多时间。
一致性减少了错误。如果系统不一致,则实际上两种情况可能不同,但两种情况可能看起来相同。开发人员可能会看到一个看起来很熟悉的模式,并根据以前对该模式的遭遇做出错误的假设。另一方面,如果系统是一致的,则基于熟悉情况的假设将是安全的。一致性允许开发人员以更少的错误来更快地工作。
一致性是投资心态的另一个例子。确保一致性的工作将需要一些额外的工作:确定约定,创建自动检查程序,寻找类似情况以模仿新代码,以及进行代码审查以教育团队。这项投资的回报是您的代码将更加明显。开发人员将能够更快,更准确地了解代码的行为,这将使他们能够以更少的错误来更快地工作。
一致性可以应用于系统中的许多级别。这里有一些例子。
编码样式。如今,开发组织通常拥有样式指南,这些样式指南将程序结构限制在编译器所强制执行的规则之外。现代风格指南解决了一系列问题,例如缩进,大括号放置,声明顺序,命名,注释以及对认为危险的语言功能的限制。样式指南使代码更易于阅读,并且可以减少某些类型的错误。
接口。具有多个实现的接口是一致性的另一个示例。一旦了解了接口的一种实现,其他任何实现都将变得更易于理解,因为您已经知道它将必须提供的功能。
设计模式。设计模式是某些常见问题的普遍接受的解决方案,例如用于用户界面设计的模型视图控制器方法。如果您可以使用现有的设计模式来解决问题,则实现会更快地进行,更有可能起作用,并且您的代码对读者来说也会更明显。
不变量。不变式是始终为真的变量或结构的属性。例如,存储文本行的数据结构可能会强制要求每行以换行符终止。不变式减少了代码中必须考虑的特殊情况的数量,并使推理行为的方式变得更加容易。
一致性很难维护,尤其是当许多人长时间从事一个项目时。一组人可能不了解另一组中建立的约定。新来者不了解规则,因此他们无意间违反了约定并创建了与现有约定冲突的新约定。以下是建立和保持一致性的一些技巧:
“无名,万物之始也; 有名,万物之母也”。(老子的《道德经》第一章)
译文:无,可以用来表示天地浑沌未开之际的状况,而有,则是宇宙万物产生之本原的命
名。
抽象的使用是计算机科学中最为重要的概念之一。例如,为一组函数规定一个简单的应用程序接口(API)就是一个很好的编程习惯,程序员无需了解它内部的工作便可以使用这些代码。不同的编程语言提供不同形式和等级的抽象支持,例如 JAVA 类的声明和 C 语言的函数原型。操作系统中也存在着很多的抽象:
在处理器里,指令集结构提供了对实际处理器硬件的抽象。使用这个抽象,机器代码程序表现得就好像它是运行在一个一次只执行一条指令的处理器上。底层的硬件比抽象描述的要复杂精细得多,它并行地执行多条指令,但又总是与那个简单有序的模型保持一致。只要执行模型一样,不同的处理器实现也能执行同样的机器代码,而又提供不同的开销和性能。
文件是对 IO 的抽象,虚拟存储器是对程序存储器的抽象,而进程是对一个正在运行的程序的抽象。我们再增加一个新的抽象:虚拟机,它提供对整个计算机(包括操作系统、处理器和程序)的抽象。虚拟机的思想是 IBM 在 20 世纪 60 年代提出来的,但是最近才显示出其管理计算机方式上的优势,因为一些计算机必须能够运行为不同操作系统(例如,Microsoft windows、macOS 和 linux)或同一操作系统的不同版本而设计的程序。
抽象与模块化设计的思想紧密相关。抽象是实体的简化视图,其中省略了不重要的细节。抽象是有用的,因为它们使我们更容易思考和操纵复杂的事物。在模块化编程中,每个模块以其接口的形式提供抽象。该接口提供了模块功能的简化视图;从模块抽象的角度来看,实现的细节并不重要,因此在接口中将其省略。
在抽象的定义中,“无关紧要”一词至关重要。从抽象中忽略的不重要的细节越多越好。但是,如果细节不重要,则只能将其从抽象中省略。常见的不当抽象可能包含以下两种:
首先,它可以包含并非真正重要的细节。当这种情况发生时,它会使抽象变得不必要的复杂,从而增加了使用抽象的开发人员的认知负担。
第二个错误是抽象忽略了真正重要的细节。这导致模糊不清:仅查看抽象的开发人员将不会获得正确使用抽象所需的全部信息。忽略重要细节的抽象是错误的抽象:它可能看起来很简单,但实际上并非如此。
例如,考虑一个文件系统。文件系统提供的抽象省略了许多细节,例如用于选择存储设备上的哪些块用于给定文件中的数据的机制。这些详细信息对于文件系统的用户而言并不重要(只要系统提供足够的性能即可)。但是,文件系统实现的一些细节对用户很重要。大多数文件系统将数据缓存在主内存中,并且它们可能会延迟将新数据写入存储设备以提高性能。一些应用程序(例如数据库)需要确切地知道何时将数据写入存储设备,因此它们可以确保在系统崩溃后将保留数据。因此,将数据刷新到辅助存储的规则必须在文件系统的接口中可见。
我们不仅依靠抽象来管理复杂性,而且不仅在编程中,而且在日常生活中无处不在。微波炉包含复杂的电子设备,可将交流电转换为微波辐射并将该辐射分布到整个烹饪腔中。幸运的是,用户看到了一个简单得多的抽象,它由几个按钮控制微波的定时和强度。汽车提供了一种简单的抽象概念,使我们可以在不了解电动机,电池电源管理,防抱死制动,巡航控制等机制的情况下驾驶它们。
另外一个典型的抽象模型,就是计算机的存储管理模型。
我们知道,在由半导体器件、电路板组成的计算硬件体系中,根本没有操作系统、TCP/IP、内存分配、垃圾回收等等概念模型。聪明的人类(这些人通常就是计算机科学家了),就是靠着杰出的想象力与抽象能力,设计出了计算机存储分层抽象模型:
一个32位操作系统的例子。其中,1GB为操作系统的内核空间,用户无法更改,这部分不用管它;剩下的3GB位用户的内存空间,这是供用户程序使用的。
想必,分层是宇宙创造万物的方式。从地球构造到鸡蛋构造,从太阳系组成到细胞结构,无不如此。
地球分层构造图:
鸡蛋结构图:
太阳系:
细胞结构:
操作系统分层图:
软件系统由层组成,其中较高的层使用较低层提供的功能。例如:
在文件系统中,最上层实现文件抽象。文件由可变长度的字节数组组成,可以通过读写可变长度的字节范围来更新该字节。文件系统的下一个下一层在固定大小的磁盘块的内存中实现了高速缓存。调用者可以假定经常使用的块将保留在内存中,以便可以快速访问它们。最低层由设备驱动程序组成,它们在辅助存储设备和内存之间移动块。
在诸如 TCP 的网络传输协议中,最顶层提供的抽象是从一台机器可靠地传递到另一台机器的字节流。此级别在较低级别上构建,该级别可以尽最大努力在计算机之间传输有限大小的数据包:大多数数据包将成功交付,但某些数据包可能会丢失或乱序交付。
从最底层的物理链路层层层向上封装抽象,解决了复杂的网络通信的问题。同样的,任何复杂的问题,通过分层最终总能够回归最本质、最简单。
DDD 分层架构遵循了“关注点分离”原则,将属于业务逻辑的关注点放到:
1、领域层(DomAIn Layer)中,
2、而将支撑业务逻辑的技术实现放到基础设施层(Infrastructure Layer)中。
3、应用层(Application Layer),扮演了双重角色。一方面它作为业务逻辑的外观(Facade),暴露了能够体现业务用例的应用服务接口;另一方面它又是业务逻辑与技术实现的粘合剂,实现二者之间的协作。
下图“分层架构“展现的就是一个典型的领域驱动设计分层架构。蓝色区域的内容与业务逻辑有关,灰色区域的内容与技术实现有关,二者泾渭分明,然后汇合在应用层。应用层确定了业务逻辑与技术实现的边界,通过直接依赖或者依赖注入(DI,Dependency Injection)的方式将二者结合起来。
为了将我们的应用部署到服务器上,我们需要为其配置一个运行环境。从底层到顶层有这样的运行环境及容器:
实现上这是一个请求的处理过程,一个 HTTP 请求会先到达你的主机。如果你的主机上运行着多个虚拟机实例,那么请求就会来到这个虚拟机上。又或者是如果你是在 Docker 这一类容器里运行你的程序的话,那么也会先到达 Docker。随后这个请求就会交由 HTTP 服务器来处理,如 Apache、Nginx,这些 HTTP 服务器再将这些请求交由对应的应用或脚本来处理。随后将交由语言底层的指令来处理。
什么是结构(Structure)?结构,是由组成整体的各部分的搭配和安排。古人写毛笔字,有云:“结构圆备如篆法,飘颺洒落如章草。”(晋·卫夫人《笔阵图》)现代人做软件结构设计,依然追寻着这样一种美感——简洁、优雅、小巧玲珑若珍珠宝石一般的美。
在软件架构领域,“结构”包括软件元素,它们之间的关系,元素和关系的属性,以及每个元素的引入和配置的基本原理(ISO/IEC 42010:20072)。
这里定义了架构的三要素:职责明确的模块或者组件、组件间明确的关联关系、约束和指导原则。
软件系统的架构是一种隐喻,类似于建筑物的体系结构,是一种整体与局部关系的抽象描述,架构是软件系统内部设计中最重要而又模糊的方面。有系统的地方就需要架构,大到航空飞机,小到一个电商系统里面的一个功能组件,都需要设计和架构。
架构,
架构能将目标系统按某个原则进行切分,切分的原则,是要便于不同的角色进行并行工作,结构良好的创造活动要优于毫无结构的创造活动。
为什么需要架构? 举一个盖房子的例子。我们偶尔会从新闻上听到某某房子倒塌、高架塌陷等悲惨事件,但是想想背后的原因,是不是因为房子、桥梁结构不合理,施工偷工减料,质量检查没有履行职责等原因导致?
这个在软件工程领域,道理是相通的。一个没有经过合理设计就匆忙开发上线的系统,迟早要还“技术债”。
架构并不由系统的功能决定,而是由系统的非功能属性决定。架构需要:
架构师的职责是:
3.动态视角:运动变化的关系 战略与战术:动态演化的视角 大多数程序员的编程行为,都是战术思维方式,着眼于使功能尽快运行。
战术编程(Tactical%20Programming) 在战术编程方法中,核心关注点是,使某些功能正常工作,例如新功能或错误修复。
如果您进行战术编程,则每个编程任务都会带来一些此类复杂性。
不久之后,某些复杂性将开始引起问题,但是,您会告诉(qi%20pian)自己,使下一个功能正常工作比返回并重构现有代码更为重要。从长远来看,重构可能会有所帮助,但是肯定会减慢当前的任务。
因此,您需要快速修补程序来解决遇到的任何问题。这只会增加复杂性,然后需要更多补丁。
很快代码变得一团糟,但是到现在为止,情况已经很糟糕了,清理它需要花费数月的时间。您的日程安排无法容忍这种延迟,解决一个或两个问题似乎并没有太大的区别,因此您只是在战术上保持编程。
几乎每个软件开发组织,都有至少一个将战术编程发挥到极致的开发人员:战术龙卷风(The%20tactical%20tornado)。
战术龙卷风是一位多产的程序员,他抽出代码的速度比其他人快得多,但完全以战术方式工作。实施快速功能时,没有人能比战术龙卷风更快地完成任务。
在某些组织中,管理层将战术龙卷风视为英雄。但是,战术龙卷风留下了毁灭的痕迹。他们很少被将来必须使用其代码的工程师视为英雄。通常,其他工程师必须清理战术龙卷风留下的混乱局面,这使得那些工程师(他们是真正的英雄)的进步似乎比战术龙卷风慢。
在战术编程中,您将不断增加一些复杂性,这些复杂性将来会引起问题。
战略编程(Strategic%20programming) 成为一名优秀的软件设计师的第一步是要意识到仅工作代码是不够的。引入不必要的复杂性以更快地完成当前任务是不可接受的。最重要的是系统的长期结构。任何系统中的大多数代码都是通过扩展现有代码库编写的,因此,作为开发人员,最重要的工作就是促进这些将来的扩展。因此,尽管您的代码当然必须工作,但您不应将“工作代码”视为主要目标。您的主要目标必须是制作出出色的设计,并且这种设计也会起作用。这是战略计划。
战略性编程需要一种投资心态。您必须花费时间来改进系统的设计,而不是采取最快的方式来完成当前的项目。这些投资会在短期内让您放慢脚步,但从长远来看会加快您的速度。一些投资将是积极的。例如,值得花一些时间为每个新类找到一个简单的设计。而不是实施想到的第一个想法,请尝试几种替代设计并选择最简洁的设计。试想一下将来可能需要更改系统的几种方式,并确保设计容易。编写好的文档是主动投资的另一个例子。
如果您进行战略性编程,则将不断对系统设计进行小幅改进。
权衡:ROI 一开始,战术性的编程方法将比战略性方法更快地取得进展。但是,在战术方法下,复杂性积累得更快,从而降低了生产率。
随着时间的流逝,战略方针会带来更大的进步。注意:此图仅用于定性说明;我不知道对曲线精确形状的任何经验测量。
相反,如果您进行战术编程,则可以将第一个项目完成的速度提高 10%到 20%,但是随着时间的推移,复杂性的累积会降低开发速度。不久之后,您的编程速度至少会降低 10–20%。您将很快退回在开始时节省的所有时间,并且在系统的整个生命周期中,与采用策略性方法相比,您的开发速度将更加缓慢。
与传统的前期、重量级的企业架构设计相比,我们建议采用演进式架构(Evolutionary Architecture)。它提供了企业架构的好处,却没有试图准确预测未来所带来的问题。
演进式架构不需要猜测组件将如何被重用,而是支持适应性,使用适当的抽象、数据库迁移、测试套件、持续集成和重构来收获系统内发生的重用。
尽管我们尽了最大努力,但复杂度仍会随着时间的推移而增加,但是更简单的设计使我们能够在复杂性压倒性优势之前构建更大,功能更强大的系统。复杂性的应对永远不会是一劳永逸,我们需要不断地推陈出新,是动态、渐进的重塑自己对软件系统的认识,不断认识问题和寻找更优解的持续迭代:
互联网行业的软件系统,很难一开始就做出完美的设计,通过一个个功能模块衍生迭代,系统才会逐步成型;对于现存的系统,也很难通过一个大动作,一劳永逸地解决所有问题。系统设计是需要持续投入的工作,通过细节的积累,最终得到一个完善的系统。因此,好的设计是日拱一卒的结果,在日常工作中要重视设计和细节的改进。
美丽胜于丑陋。
显式优于隐式。
简单胜于复杂。
复杂胜于复杂。
扁平比嵌套好。
疏胜于密。
可读性很重要。
特殊情况不足以打破规则。
尽管,实用第一,简洁第二。
错误永远不应该无声无息地过去。
除非明确沉默。
面对暧昧,拒绝猜测的诱惑。
应该有一种——最好只有一种——显而易见的方法来做到这一点。
虽然这种方式一开始可能不明显,除非你是荷兰人。
现在总比没有好。
虽然永远不会比现在好。
如果实现很难解释,那就是一个坏主意。
如果实现容易解释,这可能是一个好主意。
命名空间是一个非常棒的主意——让我们做更多这样的事情吧!
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
架构师,是一个既能掌控整体全局,又能洞悉局部瓶颈,并依据具体的业务场景,给出解决方案的团队领导型人物。
软件系统架构师综合的知识能力包括9个方面,即:
1、战略规划能力。
2、业务流程建模能力。
3、信息数据结构能力。
4、技术架构选择和实现能力。
5、应用系统架构的解决和实现能力。
6、基础IT知识及基础设施、资源调配能力。
7、信息安全技术支持与管理保障能力。
8、IT审计、治理与基本需求分析、获取能力。
9、面向软件系统可靠性与系统生命周期的质量保障服务能力。
作为系统架构师,必须成为所在开发团队的技术路线指导者;具有很强的系统思维的能力;需要从大量互相冲突的系统方法和工具中区分出哪些是有效的,哪些是无效的。架构师应当是一个成熟的、丰富的、有经验的、有良好教育的、学习快捷、善沟通和决策能力强的人。丰富是指他必须具有业务领域方面的工作知识,知识来源于经验或者教育。他必须广泛了解各种技术并精通一种特定技术,至少了解计算机通用技术以便确定那种技术最优,或组织团队开展技术评估。优秀的架构师能考虑并评估所有可用来解决问题的总体技术方案。需要良好的书面和口头沟通技巧,一般通过可视化模型和小组讨论来沟通指导团队确保开发人员按照架构建造系统。
具备的能力
(1)技术能力
技术能力,不用置疑肯定是最重要的。技术能力弱的架构不是一个好架构。所以,你需要知道所有主流技术的基本原理、应用场景,及快速解决问题的能力。所以,架构师必须要有见识,所需知识面肯定是要不断拓展的。你需要清楚在什么样的场景用什么样的技术比较合适,并知道可能存在什么样的风险。来了需求,你脑袋是空的,不知道用什么技术这是最可怕的。
(2)架构能力
这个可以表现为抽象能力、整体规划能力、及设计能力。你需要照在业务的角度进行系统分解、技术选型、架构搭建,以及规范制定。架构出来了至少可以满足最近的发展,或者可以很方便对现有架构进行扩容。有人说架构不需要懂业务,我面试过的就有明确表示不做业务架构。当然有方面的架构师,如中间件架构师,运维基础设施架构师等。但一般的后端架构师都是需要了解业务,不理解业务你如果进行系统分解,服务划分,及根据不同业务作出不同的架构。技术都是为业务服务的,不站在业务的角度设计架构,那架构就是空谈。[1]
(3)沟通能力
这个看起来不是最重要的,其实也非常重要。作为一个优秀的架构师,你需要清楚的知道客户的需求,需要不断和需求人员进行沟通,以达到客户真正的目的。不论是不是架构师,任何一个职场人,提高自己的沟通表达能力无疑是不可或缺的。有一句话怎么说的,领导就喜欢拍马屁的。做领导的大多不是技术特别牛的,但沟通能力肯定是很好的。