代码架构

在软件开发生命周期中,代码架构很少被提到,但是实际上每一个开发者都在做代码架构。代码架构在软件开发中有着重要的作用,那么我们就来聊聊在软件开发领域中代码架构,从Why、What、How几个方面来讲讲:

Why:为什么要做代码架构?

What:代码架构要做些什么事情?

How:怎样来设计代码架构?

Why:为什么要做代码架构?

软件开发的生命周期,大致都是一样的流程:

  • 需求分析:产出概念模型,需求定义
  • 概要设计:包括系统边界设计、内部架构设计、设计决策
  • 详细设计,包括代码架构,业务逻辑实现
  • 开发代码
  • 单元测试、连通测试
  • 部署上线
  • 系统运维

代码架构属于详细设计部分的内容,代码架构在整个软件开发生命周期中承上启下:

  • 向上要表达设计意图,要把软件产品概念模型、边界交互、数据流设计、数据库设计、系统内部架构设计等表达清楚,来说明业务逻辑和业务领域模型

  • 代码架构本身也是来保证代码有更好的可读性和可维护性、可扩展性,决定了如何开发,是否可以多人协同开发调试,代码如何维护,如何扩展功能。

    • 经常听到新人抱怨代码太乱了看不懂逻辑,就是代码架构做的不合理。
    • 重写代码,推翻原有架构,重新设计等等说法,来说明架构的进化。这实际上就是当初为了完成任务,没有充分思考所带来的后果。没有做好代码架构
  • 向下影响测试、部署和运维过程,是由代码架构来决定的,承载了代码运行的硬件部署架构,代码架构决定了。

    • 代码架构的好坏也会影响单元测试编写和设计、连通测试、集成测试、交付测试的过程和可测试性。
    • 代码架构决定了硬件部署架构,系统部署上线和运行维护都会受到代码架构的影响。因为代码架构不合理,是无法把一个运行单元分拆出多个来的,那么硬件架构能分拆的就非常的有限,整个系统最终很难长的更大。

What:代码架构要做些什么事情?

软件是真实世界在计算机里面的模拟。真实世界是由世间万物构成,包括了各种生物和非生物,每种事物有着各自的职责和行为、生命周期,相互碰撞发生事件,保证这个世界的运转。代码架构也是一样的,需要,结合每个部署单元所承担的责任,可以明确的拆分为三层责任:

  1. 向上沟通的职责,软件领域也叫做调用或通信,是和其他软件体交互的唯一入口,代码运行的结果就是可以被调用,可以和其他代码通信,这一层主要定义代码的沟通方式和职责,唯一的对外可见的部分,对外是相对的,就如同各种事物以自己独有的方式向外传递信息,并接受外部的信息和外部交互。人类和动物通过自己的声音、视觉、听觉、触觉、外形来其他事物通信交互,植物通过根、茎、叶、花、果实、光合作用、外观来和其他事物通信交互。

    • 进程之间的沟通,交互通信,作为可运行部署单元来说,可能就是定义对外RESTful或者RPC接口
      • 比如HTTP API接口、dubbo远程服务调用方法
    • 代码之间的沟通,交互通信,作为内部某个模块来说,可能就是定义interface和接口公有方法,。
      • 比如java\go等语言中的interface,java中的public方法、golang中首字母大写的函数和结构体方法
  2. 核心职责:这部分代码主要来表达业务逻辑,这部分叫做 Domain Logic,或者叫 Domain Model。这部分实际是来源于生活的,必须保持和现实生活中的切分一致,并非人为的抽象而成。

  3. 向下沟通的职责,完成核心能力所需要的基础实施,对用户提供访问并保存业务逻辑运行结果的代码。计算机的状态保存有一个缺陷,本机保留业务运行结果有很大的问题,一般都在外存储设备上保存,也便于扩展。

    • 依赖数据库,文件等存储
    • 依赖分布式缓存
    • 依赖分布式消息队列服务

How:怎样来设计代码架构?

代码架构-分层架构

User Interface:用户接口层

用于人机交互,这里的人是泛指,是个角色并非特定指人。用户接口层负责用户显示信息和解释用户指令,有些系统用于接受用户请求和解析用户输入的配置文件等,并将信息传递给Application层的接口,有些系统则指用户界面。

这一层通常被用在网络交互,实现进程之间,人与进程之间的沟通,代码层面来说,这一层的基础框架已经很成熟了,比如http协议的tomcat、undertow、jetty等等http server,golang 标准库中本身就内置了http server的功能,还有一些RPC框架,像thrift,grpc,dubbo等等。

那么在代码架构中,我们无需关心网络交互本身,只需要定义框架和应用层的适配,这一层也要定义的非常轻的一层,这样也方便我们针对不同的场景,提供不同的网络交互方式。

Application:应用层

相对于领域层来说很薄的一层,应用层用来表达user case和user story的主要手段,主要用于协调领域模型与其他应用组件的工作(但并不处理领域业务逻辑),定义软件要完成的所有任务。对外为展现层提供各种应用功能(包括查询或命令),对内调用领域层(领域对象或领域服务)完成各种业务逻辑,应用层不包含业务逻辑,但包含流程控制逻辑。比如在红包业务中发红包就属于应用层职责,发红包时先要检查账户余额、要转账扣款,创建红包等业务逻辑则属于领域层职责。

通常事务,日志和安全等逻辑也被放在应用层。应用层通常就是我们所说的service,

Domain:领域层

领域层主要负责表达业务概念,业务状态信息和业务规则。

Domain层是整个系统的核心层,几乎全部的业务逻辑会在该层实现。

领域模型层主要包含以下的内容:

  • 实体(Entities):具有唯一标识的对象
  • 值对象(Value Objects): 无需唯一标识
  • 领域服务(Domain Services): 一些行为无法归类到实体对象或值对象上,本质是一些操作,而非事物
  • 聚合/聚合根(Aggregates & Aggregate Roots): 聚合是指一组具有内聚关系的相关对象的集合,每个聚合都有一个rootboundary
  • 工厂(Factories): 创建复杂对象,隐藏创建细节
  • 仓储(Repository): 提供查找和持久化对象的方法

Infrastructure层

基础设施层为上面其他各层提供通用的技术能力:为应用层传递消息,为领域层提供持久化机制,为用户界面层绘制屏幕组件

编程框架,持久化机制,消息机制,第三方库的封装,通用算法等等。

基础设施层以不同的方式支持所有三个层,促进层之间的通信。 基础设施包括独立于我们的应用程序存在的一切:外部库,数据库引擎,应用程序服务器,消息后端等。

作为基础设施层,InfrastructureInterfacesApplicationDomain三层提供支撑。所有与具体平台、框架相关的实现会在Infrastructure中提供,避免三层特别是Domain层掺杂进这些实现,从而“污染”领域模型。Infrastructure中最常见的一类设施是对象持久化的具体实现。