1.识别架构特征
当决定使用软件解决某个特定问题,会收集该系统的需求列表。一个常见的软件解决方案会包. 括以下特征,那么怎么从一堆的需求中提炼出一个软件所需要具备的架构特征呢?
通常一个架构特征要满足三个条件才能识别为特征:
-
明确非领域设计的某个注意事项;
-
影响设计的某些结构项:
-
是否对应用成功至关重要.
- 明确非领域设计的某个注意事项
在设计应用时,需求指定了应用应该做什么,架构特征指定了成功的操作和设计标准,涉及如何实现需求以及为何做出某些决策。例如,一个常见的重要架构特征为应用指定了一定的性能水平,需要支撑多少用户同时访问,而这通常不会出现在需求文档中。
- 影响设计的某些结构项
考虑架构中是否需要特殊的结构设计才能保证成功,以支付场景为例,我们既可以通过第三方支付平台也可以自建付款能力,那这两种结构设计就有可能对架构起到很大的影响,毕竟支付是跟用户金钱直接相关的,安全性是非常重要的。第三方付款服务: 如果集成点处理付款细节,则该架构不需要特殊的注意事项,只需要遵循服务对接的安全规范即可,不需要特殊的结构; 应用内付款服务: 如果正在设计的应用必须处理付款,则可以为目的设计特定的模块、组件或服务,从结构上隔离关键的安全问题,这样就有可能架构和设计都有影响。
- 对于应用的成功至关重要的
筛选出应用必须具有的架构特征尤为重要,支持的架构特征越多,设计与实现的复杂度就越高,所以需要准确地评估出来关键性最少架构特征来去实现。例如,一个2B重业务的内部应用,请求量很小,就不必为高并发等架构特征做一些做出特殊的架构设计与处理。
1.1 从领域问题中提取架构特征
常见的领域问题与对应的架构特征:
领域问题
架构特征
合作
互操作性、可伸缩性、适应性、扩展性
发布时间
敏捷性、可测试性、可部署性
用户体验
性能、可测试性、容错、可部署性、敏捷性、安全性
竞争优势
敏捷性、可测试性、可部署性、可伸缩性、可用性、容错
成本
简单性、可行性
1.2 从需求中提取架构特征
一些架构特征来自于需求文档中的显式声明, 如预期的用户数量和规模通常会出现在领域问题中,有些则需要自己的实际过往经验来做出判断,比如在做一个课程报名系统,肯定会
架构kata方法:
-
说明: 系统尝试解决的整体领域问题
-
用户: 系统的预期用户数量或类型
-
要求: 域级别的需求,是否与用户提供的一致
-
额外的上下文: 需要考虑许多未显示表达在需求中的,而是隐式问题领域知识
2.度量架构特征
架构特征存在于整个软件系统中,从低级代码特征(如模块化)到复杂的操作问题 (例如可伸缩性和弹性)等,而且在不同组织/部门也存在很多歧义,所以需要在组织范围内对架构特征的定义达成一致,团队围绕架构创建一种普遍存在的语言,让架构特征具有可度量性。
2.1 运营性度量
许多架构特性具有明显的直接度量,如性能或可伸缩性,主要分析用户规模与行为习惯,比如当时做的一个校园考勤应用,在早八-九点时就会有大量学生刷卡考勤,会出现请求洪峰,基于这些特殊的场景对应架构特征,做好削峰的一些设计。
2.2 结构性度量
代码的可测量方法是复杂度,一般可由循环复杂度度量定义。循环复杂度是一种代码级度量标准,旨在为功能、方法、类或用用级别上的代码复杂度提供度量指标。 通过将图论应用于代码(尤其是决策点)来计算的,而决策点会导致不同的执行路径,例如,如果一个函数没有决策语句(例如if语句),则CC=1。如果该函数具有单个条件,则CC=2,因为存在两个可能的执行路径。用于计算单个函数或方法的CC的公式为CC=E - N + 2,其中N表示节点(代码行), E代表边(可能的决策)。如下所示,
public void test(int a, int b) {
if (a < 100) { //决策点1
reutrn 0;
} else if (a + b > 500) { //决策点2
return 1;
} else {
return -1;
}
}
CC可提供指标可让我们在开发中反思一些问题: 功能是否由于问题域或编码不良而变得复杂?代码的分离是否做的很差?是否可以将大型的方法分解为较小的逻辑块,从而将工作(和复杂性)分配给合理的方法?一般可接受10以下的CC值,如果CC值降到5以下,则认为是结构合理,内聚性较好的代码。
2.3 过程度量
一些架构特征与软件开发过程相交,例如敏捷性,其中可分为可测试性和可部署性。这对架构设计上提出了更高的要求,更好的模块化和隔离性。
-
可测性:在所有平台上,都可通过代码覆盖率工具评估代码的测试完整性。与所有软件检查一样
-
部署性:成功部署与失败部署的百分比、部署需要多久时间、部署引发的问题bug以及许多指标。
3.治理架构特征
一个好的架构设计是不断演化的,其中离不开其中治理,包括了在开发过程中施加影响的任何方面,比如通过域/模块划分调整、技术分层、引进新的技术组件等等一系列动作都可对架构特征进行治理。
模块化是大多数架构师关心的隐式架构特征,因为维护不当的模块会损害代码库的结构。每个组件都会引用到其他组件,拥有这样的组件网络会破坏模块化,因为开发人员无法重用到单个组件,而又不能一起使用其他组件。当然,如果其他组件耦合到其他组件,则该架构会越来越倾向于大泥球的反模式。
如果是Java语言可以使用JDepend工具类进行循环依赖度的检查或使用IDEA的Maven依赖工具
public class cycleTest {
private JDepend jdepend;
@BeforeEach
void init() {
jdepend = new JDepend();
jdepend.addDirectory("/path/xxxx/classes");
}
@Test
public void testAllPackages() {
Collection packags = jdepend.analyze();
}
}
ArchUnit(一个Java的测试框架)提供了各种预定义的这里规则,允许编写特定的测试来解决模块化的问题。
layerArchitecture()
.layer("Controller").definedBy("..controller..")
.layer("Service").definedBy("..service..")
.layer("Dao").definedBy("..Dao..")
.whenLayer("Controller").mayNotBeAccessedByAnyLayer()
.whenLayer("Service").mayOnlyBeAccessedByLayers("Controller")
.whenLayer("Dao").mayOnlyBeAccessedByLayers("Service")