Software architecture
软件架构说明
主条目:软件架构描述
软件架构描述涉及使用架构描述语言、架构观点和架构框架等机制来建模和表示架构的原理和实践。
架构描述语言
主条目:架构描述语言
架构描述语言 (ADL) 是用于描述软件架构的任何表达方式 ( ISO/IEC/IEEE 42010 )。自20世纪90年代以来,已经开发出许多特殊用途的ADL,包括AADL(SAE标准)、Wright(由卡内基梅隆大学开发)、Acme(由卡内基梅隆大学开发)、xADL(由UCI开发)、Darwin (由伦敦帝国理工学院开发) 、DAOP-ADL(马拉加大学开发)、SBC-ADL(国立中山大学开发)和ByADL(意大利拉奎拉大学)。
架构观点
主条目:视图模型
软件架构描述通常被组织成视图,这类似于构建架构中制作的不同类型的蓝图。每个视图都遵循其视点的约定来解决一组系统问题,其中视点是一种规范,描述在视图中使用的符号、建模和分析技术,从给定集合的角度表达所讨论的体系结构利益相关者及其关注点(ISO/IEC/IEEE 42010)。观点不仅指定了所构建的关注点(即要解决的问题),还指定了表示、使用的模型类型、使用的约定以及任何一致性(对应)规则,以保持视图与其他视图一致。
架构框架
主条目:架构框架
架构框架捕获“用于描述在特定应用领域和/或利益相关者社区内建立的架构的约定、原则和实践”(ISO/IEC/IEEE 42010)。框架通常根据一个或多个观点或 ADL 来实现。
架构风格和图案(设计模式)
架构模式是针对给定上下文中软件架构中常见问题的通用的、可重用的解决方案。架构模式通常被记录为软件设计模式。
遵循传统的建筑风格,“软件建筑风格”是一种特定的建筑方法,其特点是具有显着的特征”(架构风格)。
架构风格定义: 就结构组织模式而言的系统族;组件和连接器的词汇表,以及如何组合它们的限制。
架构风格是可重复使用的设计决策和约束的“包”,它们应用于架构以产生所选择的所需品质。
有许多公认的建筑模式和风格,其中:
- Blackboard(黑板)
- Client-server (2-tier, 3-tier, ntier, cloud computing exhibit this style)(客户端-服务器)
- Component-based(基于组件)
- Data-centric(以数据为中心)
- Event-driven (or implicit invocation)(事件驱动)
- Layered (or multilayered architecture)(分层)
- Microservices architecture(微服务架构)
- Monolithic application(单体应用)
- Peer-to-peer (P2P)(点对点)
- Pipes and filters(管道和过滤器)
- Plug-ins(插件)
- Reactive architecture(反应式架构)
- Representational state transfer (REST)(表征状态传输)
- Rule-based(基于规则)
- Service-oriented(面向服务)
- Shared nothing architecture(无共享架构)
- Space-based architecture(基于空间的架构)
有些人将建筑模式和建筑风格视为相同,有些人将风格视为模式的特殊化。它们的共同点是模式和风格都是架构师使用的惯用语,它们“提供了一种通用语言”或“词汇”来描述系统类。
软件架构与敏捷开发
主条目:敏捷开发
还有人担心软件架构会导致过多的前期大设计,尤其是敏捷软件开发的支持者。人们已经开发了许多方法来平衡前期设计和敏捷性之间的权衡,包括敏捷方法DSDM,它要求“基础”阶段,在此阶段奠定“刚好足够”的架构基础。IEEE Software专门出了一期关于敏捷性和架构之间的相互作用的特刊。
software design pattern
- Multitier architecture(多层架构)
- Model–view–controller(模型-视图-控制器)
- Domain-driven design(领域驱动设计)
- Blackboard pattern(黑板模式)
- Sensor–controller–actuator(传感器-控制器-执行器)
- Presentation–abstraction–control(表示-抽象-控制)
- Component-based(基于组件)
- Monolithic application(单体应用)
- Layered(分层)
- Pipes and filters(管道和过滤器)
- Database-centric(以数据库为中心)
- Blackboard(黑板)
- Rule-based(基于规则)
- Event-driven aka implicit invocation(事件驱动)
- Publish-subscribe(发布-订阅)
- Asynchronous messaging(异步消息传递)
- Microkernel(微内核)
- Reflection(反射)
- Client-server (multitier architecture exhibits this style)(客户端-服务器)
- Shared nothing architecture(无共享架构)
- Space-based architecture(基于空间架构)
- Object request broker(对象请求代理)
- Peer-to-peer(点对点)
- Representational state transfer (REST)(表示状态传输)
- Service-oriented(面向服务)
- Cloud computing patterns(云计算模式)
Classification and list
设计模式最初根据其解决的问题类型分为 3 个子类别。创建模式提供了根据所需的标准以可控的方式创建对象的能力。结构模式是将不同的类和对象组织起来,形成更大的结构,并提供新的功能。最后,行为模式是指识别对象之间常见的通信模式并实现这些模式。
Creational patterns
| Name | Description | In Design Patterns | In Code Complete | Other |
| Abstract factory | Provide an interface for creating families of related or dependent objects without specifying their concrete classes.提供一个接口,用于创建相关或从属对象族,而无需指定它们的具体类别。 | Yes | Yes | — |
| Builder | Separate the construction of a complex object from its representation, allowing the same construction process to create various representations.将复杂对象的构造与表示分离开来,允许同一构造过程创建各种表示。 | Yes | No | — |
| Dependency Injection | A class accepts the objects it requires from an injector instead of creating the objects directly.类从注入器中接受所需的对象,而不是直接创建对象。 | No | No | — |
| Factory method | Define an interface for creating a single object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.定义用于创建单个对象的接口,但让子类决定实例化哪个类。工厂方法允许类将实例化推迟到子类。 | Yes | Yes | — |
| Lazy initialization | Tactic of delaying the creation of an object, the calculation of a value, or some other expensive process until the first time it is needed. This pattern appears in the GoF catalog as "virtual proxy", an implementation strategy for the Proxy pattern.延迟创建对象、计算值或其他昂贵过程的策略,直到第一次需要时才进行。这种模式在 GoF 目录中以 "虚拟代理 "的形式出现,是代理模式的一种实现策略。 | No | No | PoEAA |
| Multiton | Ensure a class has only named instances, and provide a global point of access to them.确保一个类只有已命名的实例,并为它们提供一个全局访问点。 | No | No | — |
| Object pool | Avoid expensive acquisition and release of resources by recycling objects that are no longer in use. Can be considered a generalisation of connection pool and thread pool patterns.通过回收不再使用的对象,避免昂贵的资源获取和释放。可视为连接池和线程池模式的概括。 | No | No | — |
| Prototype | Specify the kinds of objects to create using a prototypical instance, and create new objects from the 'skeleton' of an existing object, thus boosting performance and keeping memory footprints to a minimum.使用原型实例指定要创建的对象类型,并根据现有对象的 "骨架 "创建新对象,从而提高性能并将内存占用降至最低。 | Yes | No | — |
| Resource acquisition is initialization (RAII) | Ensure that resources are properly released by tying them to the lifespan of suitable objects.通过将资源与合适对象的生命周期挂钩,确保资源得到适当释放。 | No | No | — |
| Singleton | Ensure a class has only one instance, and provide a global point of access to it.确保一个类只有一个实例,并提供一个全局访问点。 | Yes | Yes | — |
Structural patterns
| Name | Description | In Design Patterns | In Code Complete | Other |
| Adapter, Wrapper, or Translator | Convert the interface of a class into another interface clients expect. An adapter lets classes work together that could not otherwise because of incompatible interfaces. The enterprise integration pattern equivalent is the translator. | Yes | Yes | — |
| Bridge | Decouple an abstraction from its implementation allowing the two to vary independently. | Yes | Yes | — |
| Composite | Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly. | Yes | Yes | — |
| Decorator | Attach additional responsibilities to an object dynamically keeping the same interface. Decorators provide a flexible alternative to subclassing for extending functionality. | Yes | Yes | — |
| Delegation | Extend a class by composition instead of subclassing. The object handles a request by delegating to a second object (the delegate) | — | — | — |
| Extension object | Adding functionality to a hierarchy without changing the hierarchy. | No | No | Agile Software Development, Principles, Patterns, and Practices |
| Facade | Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use. | Yes | Yes | — |
| Flyweight | Use sharing to support large numbers of similar objects efficiently. | Yes | No | — |
| Front controller | The pattern relates to the design of Web applications. It provides a centralized entry point for handling requests. | No | No | J2EE Patterns PoEAA |
| Marker | Empty interface to associate metadata with a class. | No | No | Effective Java |
| Module | Group several related elements, such as classes, singletons, methods, globally used, into a single conceptual entity. | No | No | — |
| Proxy | Provide a surrogate or placeholder for another object to control access to it. | Yes | No | — |
| Twin | Twin allows modeling of multiple inheritance in programming languages that do not support this feature. | No | No | — |
Behavioral patterns
| Name | Description | In Design Patterns | In Code Complete | Other |
| Blackboard | Artificial intelligence pattern for combining disparate sources of data (see blackboard system) | No | No | — |
| Chain of responsibility | Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it. | Yes | No | — |
| Command | Encapsulate a request as an object, thereby allowing for the parameterization of clients with different requests, and the queuing or logging of requests. It also allows for the support of undoable operations. | Yes | No | — |
| Fluent interface | Design an API to be method chained so that it reads like a DSL. Each method call returns a context through which the next logical method call(s) are made available. | No | No | — |
| Interpreter | Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language. | Yes | No | — |
| Iterator | Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation. | Yes | Yes | — |
| Mediator | Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it allows their interaction to vary independently. | Yes | No | — |
| Memento | Without violating encapsulation, capture and externalize an object's internal state allowing the object to be restored to this state later. | Yes | No | — |
| Null object | Avoid null references by providing a default object. | No | No | — |
| Observer or Publish/subscribe | Define a one-to-many dependency between objects where a state change in one object results in all its dependents being notified and updated automatically. | Yes | Yes | — |
| Servant | Define common functionality for a group of classes. The servant pattern is also frequently called helper class or utility class implementation for a given set of classes. The helper classes generally have no objects hence they have all static methods that act upon different kinds of class objects. | No | No | — |
| Specification | Recombinable business logic in a Boolean fashion. | No | No | — |
| State | Allow an object to alter its behavior when its internal state changes. The object will appear to change its class. | Yes | No | — |
| Strategy | Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it. | Yes | Yes | — |
| Template method | Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure. | Yes | Yes | — |
| Visitor | Represent an operation to be performed on instances of a set of classes. Visitor lets a new operation be defined without changing the classes of the elements on which it operates. | Yes | No | — |
Concurrency patterns
| Name | Description | In POSA2 | Other |
| Active Object | Decouples method execution from method invocation that reside in their own thread of control. The goal is to introduce concurrency, by using asynchronous method invocation and a scheduler for handling requests. | Yes | — |
| Balking | Only execute an action on an object when the object is in a particular state. | No | — |
| Binding properties | Combining multiple observers to force properties in different objects to be synchronized or coordinated in some way. | No | — |
| Compute kernel | The same calculation many times in parallel, differing by integer parameters used with non-branching pointer math into shared arrays, such as GPU-optimized Matrix multiplication or Convolutional neural network. | No | — |
| Double-checked locking | Reduce the overhead of acquiring a lock by first testing the locking criterion (the 'lock hint') in an unsafe manner; only if that succeeds does the actual locking logic proceed.Can be unsafe when implemented in some language/hardware combinations. It can therefore sometimes be considered an anti-pattern. | Yes | — |
| Event-based asynchronous | Addresses problems with the asynchronous pattern that occur in multithreaded programs. | No | — |
| Guarded suspension | Manages operations that require both a lock to be acquired and a precondition to be satisfied before the operation can be executed. | No | — |
| Join | Join-pattern provides a way to write concurrent, parallel and distributed programs by message passing. Compared to the use of threads and locks, this is a high-level programming model. | No | — |
| Lock | One thread puts a "lock" on a resource, preventing other threads from accessing or modifying it. | No | PoEAA |
| Messaging design pattern (MDP) | Allows the interchange of information (i.e. messages) between components and applications. | No | — |
| Monitor object | An object whose methods are subject to mutual exclusion, thus preventing multiple objects from erroneously trying to use it at the same time. | Yes | — |
| Reactor | A reactor object provides an asynchronous interface to resources that must be handled synchronously. | Yes | — |
| Read-write lock | Allows concurrent read access to an object, but requires exclusive access for write operations. An underlying semaphore might be used for writing, and a Copy-on-write mechanism may or may not be used. | No | — |
| Scheduler | Explicitly control when threads may execute single-threaded code. | No | — |
| Thread pool | A number of threads are created to perform a number of tasks, which are usually organized in a queue. Typically, there are many more tasks than threads. Can be considered a special case of the object pool pattern. | No | — |
| Thread-specific storage | Static or "global" memory local to a thread. | Yes | — |
| Safe Concurrency with Exclusive Ownership | Avoiding the need for runtime concurrent mechanisms, because exclusive ownership can be proven. This is a notable capability of the Rust language, but compile-time checking isn't the only means, a programmer will often manually design such patterns into code - omitting the use of locking mechanism because the programmer assesses that a given variable is never going to be concurrently accessed. | No | — |
| CPU atomic operation | x86 and other CPU architectures support a range of atomic instructions that guarantee memory safety for modifying and accessing primitive values (integers). For example, two threads may both increment a counter safely. These capabilities can also be used to implement the mechanisms for other concurrency patterns as above. The C# language uses the Interlocked class for these capabilities. | No | — |
如果一个软件开发人员,不了解软件架构的演进,会制约技术的选型和开发人员的生存、晋升空间。这里我列举了目前主要的四种软件架构以及他们的优缺点,希望能够帮助软件开发人员拓展知识面。
单体架构
单体架构比较初级,典型的三级架构,前端(Web/手机端)+中间业务逻辑层+数据库层。这是一种典型的Java Spring mvc或者Python Drango框架的应用。其架构图如下所示:
单体架构
单体架构的应用比较容易部署、测试, 在项目的初期,单体应用可以很好地运行。然而,随着需求的不断增加, 越来越多的人加入开发团队,代码库也在飞速地膨胀。慢慢地,单体应用变得越来越臃肿,可维护性、灵活性逐渐降低,维护成本越来越高。下面是单体架构应用的一些缺点:
复杂性高: 以一个百万行级别的单体应用为例,整个项目包含的模块非常多、模块的边界模糊、 依赖关系不清晰、 代码质量参差不齐、 混乱地堆砌在一起。可想而知整个项目非常复杂。 每次修改代码都心惊胆战, 甚至添加一个简单的功能, 或者修改一个Bug都会带来隐含的缺陷。
技术债务: 随着时间推移、需求变更和人员更迭,会逐渐形成应用程序的技术债务, 并且越积 越多。“ 不坏不修”, 这在软件开发中非常常见, 在单体应用中这种思想更甚。 已使用的系统设计或代码难以被修改,因为应用程序中的其他模块可能会以意料之外的方式使用它。
部署频率低: 随着代码的增多,构建和部署的时间也会增加。而在单体应用中, 每次功能的变更或缺陷的修复都会导致需要重新部署整个应用。全量部署的方式耗时长、 影响范围大、 风险高, 这使得单体应用项目上线部署的频率较低。 而部署频率低又导致两次发布之间会有大量的功能变更和缺陷修复,出错率比较高。
可靠性差: 某个应用Bug,例如死循环、内存溢出等, 可能会导致整个应用的崩溃。
扩展能力受限: 单体应用只能作为一个整体进行扩展,无法根据业务模块的需要进行伸缩。例如,应用中有的模块是计算密集型的,它需要强劲的CPU; 有的模块则是IO密集型的,需要更大的内存。 由于这些模块部署在一起,不得不在硬件的选择上做出妥协。
阻碍技术创新: 单体应用往往使用统一的技术平台或方案解决所有的问题, 团队中的每个成员 都必须使用相同的开发语言和框架,要想引入新框架或新技术平台会非常困难。
分布式应用
中级架构,分布式应用,中间层分布式+数据库分布式,是单体架构的并发扩展,将一个大的系统划分为多个业务模块,业务模块分别部署在不同的服务器上,各个业务模块之间通过接口进行数据交互。数据库也大量采用分布式数据库,如redis、ES、solor等。通过LVS/Nginx代理应用,将用户请求均衡的负载到不同的服务器上。其架构图如下所示:
分布式架构
该架构相对于单体架构来说,这种架构提供了负载均衡的能力,大大提高了系统负载能力,解决了网站高并发的需求。另外还有以下特点:
降低了耦合度:把模块拆分,使用接口通信,降低模块之间的耦合度。
责任清晰:把项目拆分成若干个子项目,不同的团队负责不同的子项目。
扩展方便:增加功能时只需要再增加一个子项目,调用其他系统的接口就可以。
部署方便:可以灵活的进行分布式部署。
提高代码的复用性:比如service层,如果不采用分布式rest服务方式架构就会在手机wap商城,微信商城,pc,android,ios每个端都要写一个service层逻辑,开发量大,难以维护一起升级,这时候就可以采用分布式rest服务方式,公用一个service层。
缺点 : 系统之间的交互要使用远程通信,接口开发增大工作量,但是利大于弊。
微服务架构
微服务架构,主要是中间层分解,将系统拆分成很多小应用(微服务),微服务可以部署在不同的服务器上,也可以部署在相同的服务器不同的容器上。当应用的故障不会影响到其他应用,单应用的负载也不会影响到其他应用,其代表框架有Spring cloud、Dubbo等。 其架构图如下所示:
微服务架构
易于开发和维护: 一个微服务只会关注一个特定的业务功能,所以它业务清晰、代码量较少。 开发和维护单个微服务相对简单。而整个应用是由若干个微服务构建而成的,所以整个应用也会被维持在一个可控状态。
单个微服务启动较快: 单个微服务代码量较少, 所以启动会比较快。
局部修改容易部署: 单体应用只要有修改,就得重新部署整个应用,微服务解决了这样的问题。 一般来说,对某个微服务进行修改,只需要重新部署这个服务即可。
技术栈不受限:在微服务架构中,可以结合项目业务及团队的特点,合理地选择技术栈。例如某些服务可使用关系型数据库MySQL;某些微服务有图形计算的需求,可以使用Neo4j;甚至可根据需要,部分微服务使用Java开发,部分微服务使用Node.js开发。
微服务虽然有很多吸引人的地方,但它并不是免费的午餐,使用它是有代价的。使用微服务架构面临的挑战。
运维要求较高:更多的服务意味着更多的运维投入。在单体架构中,只需要保证一个应用的正常运行。而在微服务中,需要保证几十甚至几百个服务服务的正常运行与协作,这给运维带来了很大的挑战。
分布式固有的复杂性:使用微服务构建的是分布式系统。对于一个分布式系统,系统容错、网络延迟、分布式事务等都会带来巨大的挑战。
接口调整成本高:微服务之间通过接口进行通信。如果修改某一个微服务的API,可能所有使用了该接口的微服务都需要做调整。
重复劳动:很多服务可能都会使用到相同的功能,而这个功能并没有达到分解为一个微服务的程度,这个时候,可能各个服务都会开发这一功能,从而导致代码重复。尽管可以使用共享库来解决这个问题(例如可以将这个功能封装成公共组件,需要该功能的微服务引用该组件),但共享库在多语言环境下就不一定行得通了。
Serverless架构
当我们还在容器的浪潮中前行时,已经有一些革命先驱悄然布局另外一个云计算战场:Serverless架构。
Serverless架构
2014年11月14日,亚马逊AWS发布了新产品Lambda。当时Lambda被描述为:一种计算服务,根据时间运行用户的代码,无需关心底层的计算资源。从某种意义上来说,Lambda姗姗来迟,它像云计算的PaaS理念:客户只管业务,无需担心存储和计算资源。在此前不久,2014年10月22日,谷歌收购了实时后端数据库创业公司Firebase。Firebase声称开发者只需引用一个API库文件就可以使用标准REST API的各种接口对数据进行读写操作,只需编写HTML+CSS+JavaScrip前端代码,不需要服务器端代码(如需整合,也极其简单)。
相对于上两者,Facebook 在2014年二月收购的 Parse,则侧重于提供一个通用的后台服务。这些服务被称为Serverless或no sever。想到PaaS(平台即服务)了是吗?很像,用户不需要关心基础设施,只需要关心业务,这是迟到的PaaS,也是更实用的PaaS。这很有可能将会变革整个开发过程和传统的应用生命周期,一旦开发者们习惯了这种全自动的云上资源的创建和分配,或许就再也回不到那些需要微应用配置资源的时代里去了。
Serverless架构能够让开发者在构建应用的过程中无需关注计算资源的获取和运维,由平台来按需分配计算资源并保证应用执行的SLA(服务等级协议),按照调用次数进行计费,有效的节省应用成本。ServerLess的架构如上图所示。其优点如下所示:
低运营成本:在业务突发性极高的场景下,系统为了应对业务高峰,必须构建能够应对峰值需求的系统,这个系统在大部分时间是空闲的,这就导致了严重的资源浪费和成本上升。在微服务架构中,服务需要一直运行,实际上在高负载情况下每个服务都不止一个实例,这样才能完成高可用性;在Serverless架构下,服务将根据用户的调用次数进行计费,按照云计算pay-as-you-go原则,如果没有东西运行,你就不必付款,节省了使用成本。同时,用户能够通过共享网络、硬盘、CPU等计算资源,在业务高峰期通过弹性扩容方式有效的应对业务峰值,在业务波谷期将资源分享给其他用户,有效的节约了成本。
简化设备运维:在原有的IT体系中,开发团队即需要维护应用程序,同时还要维护硬件基础设施;Serverless架构中,开发人员面对的将是第三方开发或自定义的API 和URL,底层硬件对于开发人员透明化了,技术团队无需再关注运维工作,能够更加专注于应用系统开发。
提升可维护性:Serverless架构中,应用程序将调用多种第三方功能服务,组成最终的应用逻辑。目前,例如登陆鉴权服务,云数据库服务等第三方服务在安全性、可用性、性能方面都进行了大量优化,开发团队直接集成第三方的服务,能够有效的降低开发成本,同时使得应用的运维过程变得更加清晰,有效的提升了应用的可维护性。
更快的开发速度:这一点在现在互联网创业公司得到很好的体现,创业公司往往开始由于人员和资金等问题,不可能每个产品线都同时进行,这时候就可以考虑第三方的Baas平台,比如使用微信的用户认证、阿里云提供的RDS,极光的消息推送,第三方支付及地理位置等等,能够很快进行产品开发的速度,把工作重点放在业务实现上,把产品更快的推向市场。
但ServerLess架构也有其缺点:
厂商平台绑定:平台会提供Serverless架构给大玩家,比如AWS Lambda,运行它需要使用AWS指定的服务,比如API网关,DynamoDB,S3等等,一旦你在这些服务上开发一个复杂系统,你会粘牢AWS,以后只好任由他们涨价定价或者下架等操作,个性化需求很难满足,不能进行随意的迁移或者迁移的成本比较大,同时不可避免带来一些损失。Baas行业内一个比较典型的事件,2016年1月19日Facebook关闭曾经花巨额资金收购的Parse,造成用户不得不迁移在这个平台中产生一年多的数据,无疑需要花费比较大的人力和时间成本。
成功案例比较少,没有行业标准:目前的情况也只适合简单的应用开发,缺乏大型成功案例的推动。对于Serverless缺乏统一的认知以及相应的标准,无法适应所有的云平台。
目前微服务架构在四种架构中处于主流地位,很多应用第一、第二种架构的企业也开始慢慢转向微服务架构。到目前为止微服务的技术相对于二三年前已经比较成熟,第四种架构将是未来发展的一种趋势。如果你喜欢我的文章,欢迎关注我的简书,后续我将教会大家利用spring cloud和docker轻松愉快的构建微服务。
分层架构
分层架构(layered architecture)是最常见的软件架构,也是事实上的标准架构。如果你不知道要用什么架构,那就用它。
这种架构将软件分成若干个水平层,每一层都有清晰的角色和分工,不需要知道其他层的细节。层与层之间通过接口通信。
虽然没有明确约定,软件一定要分成多少层,但是四层的结构最常见。
表现层(presentation):用户界面,负责视觉和用户互动业务层(business):实现业务逻辑持久层(persistence):提供数据,SQL 语句就放在这一层数据库(database) :保存数据
有的软件在逻辑层和持久层之间,加了一个服务层(service),提供不同业务逻辑需要的一些通用接口。
用户的请求将依次通过这四层的处理,不能跳过其中任何一层。
优点
结构简单,容易理解和开发不同技能的程序员可以分工,负责不同的层,天然适合大多数软件公司的组织架构每一层都可以独立测试,其他层的接口通过模拟解决
缺点
一旦环境变化,需要代码调整或增加功能时,通常比较麻烦和费时部署比较麻烦,即使只修改一个小地方,往往需要整个软件重新部署,不容易做持续发布软件升级时,可能需要整个服务暂停扩展性差。用户请求大量增加时,必须依次扩展每一层,由于每一层内部是耦合的,扩展会很困难
事件驱动架构
事件(event)是状态发生变化时,软件发出的通知。
事件驱动架构(event-driven architecture)就是通过事件进行通信的软件架构。它分成四个部分。
事件队列(event queue):接收事件的入口分发器(event mediator):将不同的事件分发到不同的业务逻辑单元事件通道(event channel):分发器与处理器之间的联系渠道事件处理器(event processor):实现业务逻辑,处理完成后会发出事件,触发下一步操作
对于简单的项目,事件队列、分发器和事件通道,可以合为一体,整个软件就分成事件代理和事件处理器两部分。
优点
分布式的异步架构,事件处理器之间高度解耦,软件的扩展性好适用性广,各种类型的项目都可以用性能较好,因为事件的异步本质,软件不易产生堵塞事件处理器可以独立地加载和卸载,容易部署
缺点
涉及异步编程(要考虑远程通信、失去响应等情况),开发相对复杂难以支持原子性操作,因为事件通过会涉及多个处理器,很难回滚分布式和异步特性导致这个架构较难测试
微核架构
微核架构(microkernel architecture)又称为"插件架构"(plug-in architecture),指的是软件的内核相对较小,主要功能和业务逻辑都通过插件实现。
内核(core)通常只包含系统运行的最小功能。插件则是互相独立的,插件之间的通信,应该减少到最低,避免出现互相依赖的问题。
优点
良好的功能延伸性(extensibility),需要什么功能,开发一个插件即可功能之间是隔离的,插件可以独立的加载和卸载,使得它比较容易部署,可定制性高,适应不同的开发需要可以渐进式地开发,逐步增加功能
缺点
扩展性(scalability)差,内核通常是一个独立单元,不容易做成分布式开发难度相对较高,因为涉及到插件与内核的通信,以及内部的插件登记机制
微服务架构
微服务架构(microservices architecture)是服务导向架构(service-oriented architecture,缩写 SOA)的升级。
每一个服务就是一个独立的部署单元(separately deployed unit)。这些单元都是分布式的,互相解耦,通过远程通信协议(比如REST、SOAP)联系。
微服务架构分成三种实现模式。
RESTful API 模式:服务通过 API 提供,云服务就属于这一类RESTful 应用模式:服务通过传统的网络协议或者应用协议提供,背后通常是一个多功能的应用程序,常见于企业内部集中消息模式:采用消息代理(message broker),可以实现消息队列、负载均衡、统一日志和异常处理,缺点是会出现单点失败,消息代理可能要做成集群
优点
扩展性好,各个服务之间低耦合容易部署,软件从单一可部署单元,被拆成了多个服务,每个服务都是可部署单元容易开发,每个组件都可以进行持续集成式的开发,可以做到实时部署,不间断地升级易于测试,可以单独测试每一个服务
缺点
由于强调互相独立和低耦合,服务可能会拆分得很细。这导致系统依赖大量的微服务,变得很凌乱和笨重,性能也会不佳。一旦服务之间需要通信(即一个服务要用到另一个服务),整个架构就会变得复杂。典型的例子就是一些通用的 Utility 类,一种解决方案是把它们拷贝到每一个服务中去,用冗余换取架构的简单性。分布式的本质使得这种架构很难实现原子性操作,交易回滚会比较困难。
云架构
云结构(cloud architecture)主要解决扩展性和并发的问题,是最容易扩展的架构。
它的高扩展性,主要原因是没使用中央数据库,而是把数据都复制到内存中,变成可复制的内存数据单元。然后,业务处理能力封装成一个个处理单元(prcessing unit)。访问量增加,就新建处理单元;访问量减少,就关闭处理单元。由于没有中央数据库,所以扩展性的最大瓶颈消失了。由于每个处理单元的数据都在内存里,最好要进行数据持久化。
这个模式主要分成两部分:处理单元(processing unit)和虚拟中间件(virtualized middleware)。
处理单元:实现业务逻辑虚拟中间件:负责通信、保持sessions、数据复制、分布式处理、处理单元的部署。
虚拟中间件又包含四个组件。
消息中间件(Messaging Grid):管理用户请求和session,当一个请求进来以后,决定分配给哪一个处理单元。数据中间件(Data Grid):将数据复制到每一个处理单元,即数据同步。保证某个处理单元都得到同样的数据。处理中间件(Processing Grid):可选,如果一个请求涉及不同类型的处理单元,该中间件负责协调处理单元部署中间件(Deployment Manager):负责处理单元的启动和关闭,监控负载和响应时间,当负载增加,就新启动处理单元,负载减少,就关闭处理单元。
优点
高负载,高扩展性动态部署
缺点
实现复杂,成本较高主要适合网站类应用,不合适大量数据吞吐的大型数据库应用较难测试
其他架构
断路器模式通过将流量重新路由到另一个服务来最大限度地减少危险的影响。虽然它有助于提高系统的容错能力以防止发生事故,但它还需要复杂的测试和使用服务网格等基础设施管理技术。
客户端-服务器模式是一种点对点架构,由请求服务的客户端和提供服务的服务器组成。示例包括银行、文件共享、电子邮件和万维网。这种模式的优点之一是数据和网络外围设备集中管理,但服务器价格昂贵。
命令查询责任分离(CQRS) 模式可处理数据库查询比数据更改更频繁发生的情况。它将读取和写入活动分开,以提供更高的稳定性、可扩展性和性能,但它需要更多的数据库技术,因此可能会增加成本。
控制器-响应器模式将架构分为两个组件:控制器处理数据并分配工作负载,响应器从控制器复制数据并生成结果。优点之一是您可以从响应器读取数据,而不会影响控制器中的数据,但如果控制器发生故障,您可能会丢失数据并需要重新启动应用程序。
事件溯源模式适用于使用实时数据的应用程序。它将连续的消息流发送到数据库、Web 服务器、日志或其他目标。它非常灵活,但需要高效可靠的网络基础设施来最大限度地减少延迟。
分层模式适用于电子商务、桌面和其他包含按特定顺序执行的子任务组的应用程序。分层模式使得快速编写应用程序变得很容易,但缺点是以后很难拆分层。
微服务模式结合了设计模式来创建多个服务,这些服务相互依赖地工作以创建更大的应用程序。由于每个应用程序都很小,因此在需要时更容易更新它们,但复杂性意味着您需要更多的架构专业知识才能使一切正常工作。
模型-视图-控制器(MVC) 模式将应用程序分为三个组件。该模型包含应用程序的数据和主要功能;视图显示数据并与用户交互;控制器处理用户输入并充当模型和视图之间的中介。此模式使应用程序能够生成各种视图,但其抽象层增加了复杂性。
发布-订阅模式将相关消息发送(发布)到已订阅主题的位置。配置很容易,但测试起来更具挑战性,因为发布者和订阅者之间的交互是异步的。
saga模式用于具有多个步骤的事务,例如旅行预订服务。“传奇”包括完成交易必须发生的各个步骤。这种模式使事务(最好是五个或更少的步骤)能够在松散耦合、消息驱动的环境中发生,但它需要大量编程,并且管理起来可能很复杂。
分片模式对数据库中的数据进行分段以加速命令或查询。它确保跨实例均匀地消耗存储,但需要熟练且经验丰富的数据库管理员来有效管理分片。
静态内容托管模式用于优化网页加载时间。它将静态内容(不经常更改的信息,如作者简介或 MP3 文件)与动态内容(如股票价格)分开存储。它对于交付不经常更改的内容和媒体非常有效,但缺点包括数据一致性和更高的存储成本。
当您对系统进行增量更改时,会使用扼杀者模式。它将旧系统置于中介后面以支持增量转型,与进行较大的更改相比,这可以降低风险。但是,您需要密切关注路由和网络管理,并确保制定回滚计划,以防出现问题。
节流(或速率限制)模式控制数据流入目标的速度。它通常用于防止分布式拒绝服务攻击期间发生故障或管理云基础设施成本。要成功使用此模式,您需要良好的冗余机制,并且它通常与断路器模式一起使用以维持服务性能
软件开发流程
大多数现代开发流程都可以模糊地描述为敏捷。其他方法包括瀑布、原型、迭代和增量开发、螺旋式开发、快速应用程序开发和极限编程。
- 1969 年以来的结构化编程
- Cap Gemini SDM,最初来自PANDATA,第一个英文译本于1974年出版。SDM代表系统开发方法论
- 1980年以来的结构化系统分析与设计方法(SSADM)
- 信息需求分析/软系统方法
- 面向对象编程(OOP) 于 20 世纪 60 年代初发展起来,并在 20 世纪 90 年代中期成为主流编程方法
- 快速应用程序开发(RAD),自 1991 年以来
- 动态系统开发方法(DSDM),自 1994 年
- Scrum,从1995年开始
- 团队软件流程,自 1998 年
- Rational Unified Process(RUP),自 1998 年起由 IBM 维护
- 极限编程,始于1999年
- 敏捷统一流程Scott Ambler维护(AUP) 自 2005 年起由
- 纪律敏捷交付(DAD) 取代 AUP
值得注意的是,自 1994 年 DSDM 以来,除了 RUP 之外,上述列表中的所有方法都是敏捷方法 - 然而许多组织,尤其是政府,仍然使用前敏捷流程(通常是瀑布式或类似流程)。软件过程与软件质量密切相关;在实践中观察到了一些意想不到的方面和影响
其中,另一种软件开发流程已在开源中建立。在公司范围内采用这些已知的最佳实践和既定流程称为内部源。
方法论
敏捷开发
主条目:敏捷软件开发
“敏捷软件开发”是指一组基于迭代开发的软件开发框架,其中需求和解决方案通过自组织跨职能团队之间的协作而不断发展。该术语是在 2001 年敏捷宣言制定时创造的。
敏捷软件开发以迭代开发为基础,但倡导比传统方法更轻松、更以人为中心的观点。敏捷过程从根本上结合了迭代及其提供的持续反馈,以不断完善和交付软件系统。
敏捷模型还包括以下软件开发流程:
持续集成
主条目:持续集成
持续集成是每天多次将所有开发人员工作副本合并到共享主线的实践。Grady Booch在他 1991 年的方法中首次命名并提出了 CI ,尽管他并不主张每天集成几次。极限编程(XP)采用了 CI 的概念,并且确实主张每天集成一次以上——也许每天集成数十次。
增量开发
主条目:迭代和增量开发
可以采用各种方法来组合线性和迭代系统开发方法,每种方法的主要目标都是通过将项目分解为更小的部分并在开发过程中提供更容易的更改来降低固有的项目风险。
增量开发有三种主要变体:
- 执行一系列迷你瀑布,其中系统的一小部分完成瀑布的所有阶段,然后再进行下一个增量,或者
- 在对系统的各个增量进行渐进式、迷你瀑布式开发之前定义总体需求,或者
- 最初的软件概念、需求分析以及架构和系统核心的设计是通过瀑布定义的,然后是增量实施,最终安装最终版本,即工作系统。
快速应用开发
主条目:快速应用程序开发
快速应用程序开发 (RAD) 模型
快速应用程序开发(RAD)是一种软件开发方法,它有利于迭代开发和快速构建原型,而不是大量的前期规划。使用 RAD 开发的软件的“规划”与编写软件本身是交织在一起的。缺乏广泛的预先规划通常可以使软件编写得更快,并且更容易更改需求。
快速开发过程从使用结构化技术开发初步数据模型和业务流程模型开始。在下一阶段,使用原型验证需求,最终完善数据和流程模型。这些阶段不断重复;进一步的开发导致“用于构建新系统的综合业务需求和技术设计声明”。
该术语首次用于描述James Martin在 1991 年引入的软件开发过程。根据 Whitten (2003) 的说法,它是各种结构化技术(特别是数据驱动的信息技术工程)与原型技术的合并,以加速软件系统开发。
快速应用开发的基本原则是:
- 主要目标是以相对较低的投资成本快速开发和交付高质量的系统。
- 尝试通过将项目分解为更小的部分并在开发过程中更轻松地进行更改来降低固有的项目风险。
- 旨在快速生产高质量的系统,主要通过迭代原型设计(在开发的任何阶段)、用户的积极参与和计算机化的开发工具。这些工具可能包括图形用户界面计算机辅助软件工程数据库管理系统第四代编程语言(GUI)构建器、(CASE)工具、(DBMS)、、代码生成器和面向对象技术。
- 重点是满足业务需求,而技术或工程卓越性则不太重要。
- 项目控制包括确定开发的优先顺序和定义交付期限或“时间盒”。如果项目开始下滑,重点是减少要求以适应时间范围,而不是增加最后期限。
- 通常包括联合应用程序设计系统设计。(JAD),其中用户通过结构化研讨会或电子促进的交互中建立共识来深入参与
- 用户的积极参与势在必行。
- 迭代地生产生产软件,而不是一次性原型。
- 生成促进未来开发和维护所需的文档。
- 标准系统分析和设计方法可以适合该框架。
瀑布式开发
主条目:瀑布模型
软件开发过程的活动以瀑布模型表示。还有其他几个模型可以代表这个过程。
瀑布模型是一种顺序开发方法,其中开发被视为稳步向下流动(像瀑布一样),经过几个阶段,通常是:
该方法的第一个正式描述经常被引用为Winston W. Royce在 1970 年发表的一篇文章,尽管 Royce 在这篇文章中没有使用术语“瀑布”。罗伊斯将该模型作为有缺陷的、不起作用的模型的示例来展示。
基本原则是:
- 该项目分为几个连续的阶段,阶段之间有一些重叠和溅水是可以接受的。
- 重点是整个系统的规划、时间表、目标日期、预算和实施。
- 通过在大多数阶段结束时开始下一阶段之前进行的大量书面文档、正式审查以及用户和信息技术管理人员
的批准/签署,在项目的整个生命周期中保持严格的控制。书面文档是每个阶段的明确可交付成果。
瀑布模型是应用于软件工程的传统工程方法。严格的瀑布方法不鼓励在完成后重新访问和修改任何先前的阶段。[根据谁的说法?]纯瀑布模型中的这种“不灵活性”一直是其他更“灵活”模型的支持者批评的根源。由于采用了“大设计预先”方法,许多大型政府项目都超出了预算,随着时间的推移,有时甚至无法满足要求,人们普遍指责它。[根据谁的说法?]除非合同要求,否则瀑布模型已在很大程度上被专为软件开发开发的更灵活和通用的方法所取代。[根据谁的说法?]参见瀑布模型的批评。
螺旋式发展
螺旋模型(Boehm,1988)
主条目:螺旋模型
1988年,Barry Boehm发表了正式的软件系统开发“螺旋模型”,它结合了瀑布模型和快速原型方法论的一些关键方面,力图结合自上而下和自下而上概念的优点。它强调了许多人认为被其他方法忽视的关键领域:深思熟虑的迭代风险分析,特别适合大规模复杂系统。
基本原则是:
- 重点是风险评估和通过将项目分解为更小的部分来最小化项目风险,并在开发过程中提供更容易的更改,以及提供评估风险和权衡整个生命周期中项目延续性的机会。
- “每个周期都涉及到产品的每个部分及其每个细化级别的相同步骤序列,从总体操作概念文档到每个单独程序的编码。”
- 围绕螺旋的每次旅行都会穿过四个基本象限:(1)确定迭代的目标、替代方案和约束,以及(2)评估替代方案;识别并解决风险;(3) 开发并验证迭代的可交付成果;(4) 计划下一次迭代。
- 每个周期以确定利益相关者及其“获胜条件”开始,并以审查和承诺结束每个周期。
塑造身材
Shape Up是Basecamp在2018年推出的一种软件开发方法。它是Basecamp内部开发的一套原则和技术,旨在克服项目一拖再拖的问题。其主要目标受众是远程团队。与瀑布、敏捷或Scrum不同,Shape Up 没有估计和速度跟踪、待办事项或冲刺。相反,这些概念被欲望、投注和周期所取代。截至 2022 年,除了 Basecamp 之外,采用 Shape Up 的著名组织还包括 UserVoice 和 Block。
周期
经过反复试验,Basecamp 发现理想的周期长度是 6 周。这 6 周的时间对于构建有意义的功能来说足够长,但又足够短,足以引发紧迫感。
成型
塑造是指在将工作交给设计师和工程师之前准备工作的过程。明确的工作阐明了解决方案的主要 UI 元素,识别兔子洞,并勾勒出清晰的范围边界。本来就是粗略的,把更精细的细节留给建造者(设计师和工程师)去解决,让建造者发挥创造力,做出权衡。使用支持评论的在线文档解决方案以推介的形式记录成型工作,从而允许团队成员异步贡献技术信息。这些评论对于发现可能使项目脱轨的隐藏惊喜至关重要。
在一个周期开始之前,利益相关者持有一个投注表,在那里对投球进行审查。对于每个投球,都会做出押注或放弃的决定。
食欲
Shape Up 确定为项目分配多少时间的方式与其他方法截然相反。Shape Up 从食欲(例如 6 周)开始,到可以在此限制内交付的解决方案设计结束。这种需求成为项目建设者的严格期限。
建筑
Shape Up 是一个双轨系统,塑造者和构建者并行工作。当前周期中正在形成的工作可能会交给设计师和工程师在未来周期中进行构建。
认识到建筑带来的技术不确定性,我们使用图表来跟踪进度,该图表形象化了山的隐喻,恰如其分地命名为山图。上坡阶段是建设者仍在研究方法的阶段,而下坡阶段则是消除未知因素的阶段。构建者使用 Basecamp 或Jira上的交互式在线山图主动、异步地自我报告进度,将焦点从已完成或未完成状态转移到未知或已解决的问题。山图的使用取代了 Scrum 或看板站立会议中报告线性状态的过程。
先进的方法论
其他高级软件项目方法包括:
- Behavior-driven development and business process management.
- Chaos model - The main rule always resolves the most important issue first.
- Incremental funding methodology - an iterative approach
- Lightweight methodology - a general term for methods that only have a few rules and practices
- Structured systems analysis and design method - a specific version of waterfall
- Slow programming, as part of the larger Slow Movement, emphasizes careful and gradual work without (or minimal) time pressures. Slow programming aims to avoid bugs and overly quick release schedules.
- V-Model (software development) - an extension of the waterfall model
- Unified Process (UP) is an iterative software development methodology framework, based on Unified Modeling Language (UML). UP organizes the development of software into four phases, each consisting of one or more executable iterations of the software at that stage of development: inception, elaboration, construction, and guidelines. Many tools and products exist to facilitate UP implementation. One of the more popular versions of UP is the Rational Unified Process (RUP).
- Big Bang methodology - an approach for small or undefined projects, generally consisting of little to no planning with high risk.
瀑布模型
随着计算机的普及,温斯顿·罗伊斯博士的瀑布模型(1956)指导公司如何以最短、最有效的方式生产软件。
这种逻辑分层系统是 Bell 和 Thayer 在 1976 年的一篇期刊文章中引入的,后来由美国国防部于 1985 年为其 DoD 软件开发提供商进行了标准化。
瀑布模型的六个阶段
- 初步设计
- 详细设计
- 发展
- 单元测试
- 一体化
- 测试
该模型至今仍在使用,最适合大型项目和组织,可以从其严格的阶段和截止日期中受益。
螺旋模型
1986 年,Barry Boehm 将瀑布模型与迭代过程结合在一个他称为螺旋模型的系统中。
该模型的四个阶段中的每个阶段都以设计目标开始,以客户审查结果结束。每个阶段也有其特定的任务。
螺旋模型最适合大型且复杂的不可预测的项目。
Frederick P. Brooks 在他 1975 年出版的《人月神话》一书中指出,从另一个切线来探讨软件开发,“增加更多的人会延长而不是缩短进度。”
布鲁克斯随后发表了一篇名为“软件工程中的无银弹——本质与意外”的文章,该文章认为,由于没有任何一个软件是完全没有错误的,因此我们需要简单可靠的软件开发方法。
Scrum 框架
同年,即 1986 年,Takeuchi 和 Nonaka引入了“Scrum”一词,他们说这是一种组织知识创造的方法:
“在当今快节奏、竞争激烈的商业新产品开发世界中,速度和灵活性至关重要。”
Scrum 框架建议软件开发从“橄榄球”方式转向“接力”方式。中继软件开发方法使用传统/瀑布方法,其中一组将流程传递给下一组。
相比之下,橄榄球模式是在球队作为一个整体在球场上移动时在球队内部“传球”。最后一种速度更快,并且具有更多的自主性和稳定性。
SCRUM 开发流程
三年后,Schwaber 和 Sutherland 将“Scrum”纳入各自的软件开发公司。1995 年,Schwaber 和 Shuterland 出版了《SCRUM 开发流程》,概述了 Scrum 方法。
尽管 Scrum 相对容易理解,但新用户可能会发现 Scrum(及其价值观、角色、事件和工件)很复杂
Rational 统一(软件)流程
一年后,IBM 的 Rational 软件公司创建了Rational 统一(软件)流程(RUP),将 Scrum 的软件项目生命周期分为四个阶段。
每个阶段都包含所有六个核心开发规则,而某些流程比其他流程受到更多关注。
到目前为止,创新者主要考虑的是中型到大型公司。1999 年,Kent Beck为那些在模糊且不断变化的软件开发需求中挣扎的小型企业撰写了《极限编程解释》一书。
该书介绍了软件处理的敏捷革命,据说 Scrum 的协作敏捷性比瀑布框架的僵化更好。
敏捷宣言
2001年,17名软件开发从业者聚集在犹他州的一个滑雪场滑雪放松。他们在业余时间编写了敏捷宣言,其中包含四个价值观和 12 条原则,以实现更敏捷的软件开发流程
“通过这项工作,我们认识到了以下价值:(1)个体和交互胜过流程和工具;(2) 工作软件胜过全面的文档;(3) 客户协作胜过合同谈判,以及 (4) 响应变化胜过遵循计划”
后来,罗伯特·“鲍勃叔叔”·马丁在多伦多举行的 2008 年敏捷主题演讲中添加了第五个价值观,称为“工艺胜过废话”。
精益软件开发
您想要更好、更快、更便宜的软件开发吗?Mary 和 Tom Poppendieck 在他们 2003 年出版的《精益软件开发》一书中确定了七个基本的“精益”原则,这些原则可以适用于软件开发领域。
这些原则中的每一个都已经彻底改变了制造、物流和产品开发。它们可以应用于软件开发价值、流程和程序开发人员。
开发运营
与此同时,一位沮丧的 Beligan 顾问 Patrick Debois 通过他的演讲开发并推广了 DevOps 一词。
DevOps 只是意味着开发(创建代码的部门)和运营(使用该代码的部门)之间的跨部门集成。
当然,它远比这复杂,并且它对软件开发和部署的影响是巨大的。通过统一开发和运营,DevOps 为每个人创造了参与的机会。
看板
最后一个影响是 David Anderson 2010 年出版的书《看板》。不像 Scrum 这样的软件方法论,来自日本的看板告诉你如何不断改进你的软件功能、产品或服务。
这样,它的方法既可以应用于 Scrum 等敏捷模型,也可以应用于迭代过程等传统模型。