1.识别架构特征

  1. 当决定使用软件解决某个特定问题,会收集该系统的需求列表。一个常见的软件解决方案会包括以下特征,那么怎么从一堆的需求中提炼出一个软件所需要具备的架构特征呢?<br />![](https://cdn.nlark.com/yuque/0/2021/jpeg/2699467/1632107499563-beef8782-dc1b-48e4-aec3-17d51f9ceaf4.jpeg)<br />通常一个架构特征要满足三个标准才能识别为特征:
  • 明确非领域设计的某个注意事项;
  • 影响设计的某些结构项:
  • 是否对应用成功至关重要.

(结)软件架构设计-架构特征的识别治理 - 图1

  • 明确非领域设计的某个注意事项

    1. 在设计应用时,需求指定了应用应该做什么,架构特征指定了成功的操作和设计标准,涉及如何实现需求以及为何做出某些决策。例如,一个常见的重要架构特征为应用指定了一定的性能水平,需要支撑多少用户同时访问,而这通常不会出现在需求文档中。
  • 影响设计的某些结构项

    1. 考虑架构中是否需要特殊的结构设计才能保证成功,以支付场景为例,我们既可以通过第三方支付平台也可以自建付款能力,那这两种结构设计就有可能对架构起到很大的影响,毕竟支付是跟用户金钱直接相关的,安全性是非常重要的。第三方付款服务: 如果集成点处理付款细节,则该架构不需要特殊的注意事项,只需要遵循服务对接的安全规范即可,不需要特殊的结构; 应用内付款服务: 如果正在设计的应用必须处理付款,则可以为目的设计特定的模块、组件或服务,从结构上隔离关键的安全问题,这样就有可能架构和设计都有影响。
  • 对于应用的成功至关重要的

    1. 筛选出应用必须具有的架构特征尤为重要,支持的架构特征越多,设计与实现的复杂度就越高,所以需要准确地评估出来关键性最少架构特征来去实现。例如,一个2B重业务的内部应用,请求量很小,就不必为高并发等架构特征做一些做出特殊的架构设计与处理。

(结)软件架构设计-架构特征的识别治理 - 图2

1.1 从领域问题中提取架构特征

常见的领域问题与对应的架构特征:

领域问题 架构特征
合作 互操作性、可伸缩性、适应性、扩展性
发布时间 敏捷性、可测试性、可部署性
用户体验 性能、可测试性、容错、可部署性、敏捷性、安全性
竞争优势 敏捷性、可测试性、可部署性、可伸缩性、可用性、容错
成本 简单性、可行性

1.2 从需求中提取架构特征

  1. 一些架构特征来自于需求文档中的显式声明, 如预期的用户数量和规模通常会出现在领域问题中,有些需要自己的实际过往经验来做出判断,比如在春节抢车票场景下,对照下人流量等数据评估出来系统的性能指标。

架构kata方法:

  • 说明: 系统尝试解决的整体领域问题
  • 用户: 系统的预期用户数量或类型
  • 要求: 域级别的需求,是否与用户提供的一致
  • 额外的上下文: 需要考虑许多未显示表达在需求中的,而是隐式问题领域知识

2.度量架构特征

  1. <br /> 架构特征存在于整个软件系统中,从低级代码特征(如模块化)到复杂的操作问题 (例如可伸缩性和弹性)等,而且在不同组织/部门也存在很多歧义,所以需要在组织范围内对架构特征的定义达成一致,团队围绕架构创建一种普遍存在的语言,让架构特征具有可度量性。

2.1 运营性度量

  1. 许多架构特性具有明显的直接度量,如性能或可伸缩性,主要分析用户规模与行为习惯,比如当时做的一个校园考勤应用,在早八-九点时就会有大量学生刷卡考勤,会出现请求洪峰。

2.2 结构性度量

  1. 代码的可测量方法是复杂度,一般可由循环复杂度度量定义。循环复杂度是一种代码级度量标准,旨在为功能、方法、类或用用级别上的代码复杂度提供度量指标。 通过将图论应用于代码(尤其是决策点)来计算的,而决策点会导致不同的执行路径,例如,如果一个函数没有决策语句(例如if语句),则CC=1。如果该函数具有单个条件,则CC=2,因为存在两个可能的执行路径。用于计算单个函数或方法的CC的公式为CC=E - N + 2,其中N表示节点(代码行), E代表边(可能的决策)。如下所示,
  1. public void test(int a, int b) {
  2. if (a < 100) { //决策点1
  3. reutrn 0;
  4. } else if (a + b > 500) { //决策点2
  5. return 1;
  6. } else {
  7. return -1;
  8. }
  9. }

(结)软件架构设计-架构特征的识别治理 - 图3
CC可提供一些关键指标: 功能是否由于问题域或编码不良而变得复杂?代码的分离是否做的很差?是否可以将大型的方法分解为较小的逻辑块,从而将工作(和复杂性)分配给合理的方法?一般可接受10以下的CC值,如果CC值降到5以下,则认为是结构合理,内聚性较好的代码。

2.3 过程度量

  1. 一些架构特征与软件开发过程相交,例如敏捷性,其中可分为可测试性和可部署性。这对架构设计上提出了更高的要求,更好的模块化和隔离性。
  • 可测性:在所有平台上,都可通过代码覆盖率工具评估代码的测试完整性。与所有软件检查一样
  • 部署性: 成功部署与失败部署的百分比、部署需要多久时间、部署引发的问题bug以及许多指标。

3.治理架构特征

  1. 一个好的架构设计是不断演化的,其中离不开其中治理,包括了在开发过程中施加影响的任何方面,比如通过域/模块划分调整、技术分层、引进新的技术组件等等一系列动作都可对架构特征进行治理。<br /> 模块化是大多数架构师关心的隐式架构特征,因为维护不当的模块会损害代码库的结构。每个组件都会引用到其他组件,拥有这样的组件网络会破坏模块化,因为开发人员无法重用到单个组件,而又不能一起使用其他组件。当然,如果其他组件耦合到其他组件,则该架构会越来越倾向于大泥球的反模式。

(结)软件架构设计-架构特征的识别治理 - 图4

如果是Java语言可以使用JDepend工具类进行循环依赖度的检查或使用IDEA的Maven依赖工具

  1. public class cycleTest {
  2. private JDepend jdepend;
  3. @BeforeEach
  4. void init() {
  5. jdepend = new JDepend();
  6. jdepend.addDirectory("/path/xxxx/classes");
  7. }
  8. @Test
  9. public void testAllPackages() {
  10. Collection packags = jdepend.analyze();
  11. }
  12. }

ArchUnit(一个Java的测试框架)提供了各种预定义的这里规则,允许编写特定的测试来解决模块化的问题。这里说明

(结)软件架构设计-架构特征的识别治理 - 图5

  1. layerArchitecture()
  2. .layer("Controller").definedBy("..controller..")
  3. .layer("Service").definedBy("..service..")
  4. .layer("Dao").definedBy("..Dao..")
  5. .whenLayer("Controller").mayNotBeAccessedByAnyLayer()
  6. .whenLayer("Service").mayOnlyBeAccessedByLayers("Controller")
  7. .whenLayer("Dao").mayOnlyBeAccessedByLayers("Service")