前段时间在面试的时候,面试官问到:你是如何将DDD(领域驱动设计)应用到基础设施的?
我很惊讶,终于有人问我这个问题了。
在过去从事基础设施(DevOps、SRE、运维)的5年里,我经常说起DDD是一种思维模式,可以应用到任何的领域,包括基础设施的设计。
但是,从来没有人像这位面试官问起我具体的做法。
为什么没有人问?原因大概是这两个概念通常是不会放在一起的。大多数开发不会深入理解基础设施的设计,而大多数从事基础设施设计的人是不会接触到DDD。而且,开发人员对于DDD的理解,也仅局限于用它开发业务系统。
我就是那少部分人,即做基础设施的设计,又觉得自己懂DDD的人。
说回问题本身。
我所理解的DDD
我首先会向提问的人澄清我所理解的DDD。
为什么要这样呢?很久以前有一次面试,因为我说我擅长DevOps,面试官就认为我不懂GitOps。然后在这个点上他就认为我不适合,不再问我DevOps方面的问题。我只能说没有缘份。
我是这样解释DDD的:
就像开发一个象棋游戏,不论你要开发手机端,还是web端,象棋规则本身都是不变的。这个规则本身就可以理解为“领域”。 其它所有的技术(包括架构)都是具体实现,它们应该由“领域”来驱动设计。
当时的解释与以上的解释大差不差。
将DDD应用到基础设施设计的具体做法
那么该如何将DDD应用到基础设施的设计呢?
DDD的思维方式要求我们首先问:我们要设计的软件的领域(核心)问题是什么?
基础设施的领域问题是什么?我的回答是配置。
我认为基础设施的搭建、维护,本质就是配置的设计、部署、维护。
寻找领域(本质)问题的能力是DDD的核心能力。
为了让读者更好理解,我们以一个一个基于云上的虚拟机的分布式系统为例。它的基础设施就包括:vpc、LB、MQ、DB等。
要搭建、维护这一套基础设施。
根据“配置管理是基础设施设计的核心问题”,我首先将基础设施的所有的配置放在清单代码(并不一定是一个文件)中,如下:
vpc:
# ....
LB:
# ...
DB:
# ...
MQ:
# ...
APP1:
mq_addr: "{{ MQ.addr }}"
db_host: "{{ DB.addr }}"
APP2:
mq_addr: "{{ MQ.addr }}"
db_host: "{{ DB.addr }}"
app1_addr: "{{ APP1.addr }}"
从清单中,你看不出它使用何种部署方式、部署顺序。你只知道APP1引用了MQ和DB,APP2会调用APP2这样纯粹的领域知识。
现实通常是多个环境,所以,我一开始就会将不同环境的值从清单上抽离到独立的文件夹中。
第二步,我才会考虑如何部署它们。这时,我通过两个工具实现:
1. Terraform负责云基础设施;
2. Ansible负责业务基础设施。
Ansible是可以直接读取我们的YAML格式配置文件。而Terraform代码引用YAML代码,就没有那么方便了。
所以,我决定使用Jsonnet这门配置语言来统一所有的配置,这样不同的配置之间就可以相互引用了。
配置之间的相互引用功能是配置管理的核心功能。
假如某一天,我们需要将云虚拟机的基础设施,迁移到K8s呢?
最上层的清单配置是不需要做什么变更的,因为它和你的具体的基础设施实现是无关的,你只需要更改底层的部分配置。
比如,你需要将Ansible部署工具改成Helm,那么,你需要做的就是写一套通用的Helm chart,然后chart values配置引用上层的APP的配置即可。这时,你会发现,配置的标准化,我们在一开始实现了,而不需要后期返工。
最终我们的基础设施的配置的架构,可以简化成下图:
小结
我是如何将DDD应用到基础设施的:
1. 基于自己对基础设施的理解,将“配置管理”定义为这个领域的核心问题;
2. 分析配置管理的所带来的具体问题;这一步通常由领域专家来做。本文我是直接略过这一环节;
3. 根据第二步,决定使用代码去管理配置;
4. 根据基础设施的上下文(是否使用云,是否云原生)选择工具,读取配置,然后实现部署与维护。
不论你是否赞同使用基础设施即代码,我的基础设施的设计都是由配置管理(领域)这个问题驱动。
Ebean:一款被低估的ORM框架
我是如何将同事的代码改成DDD风格的
这十年,我所经历的领域驱动设计(DDD)