The best architectures, requirements, and designs emerge from self-organizing teams.
当我们谈到架构的时候,往往我们忽略了软件工程本身的复杂性,即系统中哪些部分是变化的,哪些是不变的。初中物理课上,老师告诉我们运动是永恒的,而静止则是相对概念。同里,在不同时间维度上,“变化”是永恒的,“不变”是相对的。如果我们认为需求是不断变化的,那么在软件开发中,是否存在不变的东西呢?
在过去的一周中,花了两天的时间引导客户团队一起做了一次领域驱动设计(DDD)工作坊。两天的时间不长,从输出的角度看,没有完全将客户系统的领域模型完整梳理出来。但是在这两天的Workshop当中,我们从软件架构设计的角度,试图找到一个软件系统中的不变量。
参与工作坊的团队成员在过程中向我提出一个问题:最终用户能否作为领域专家参与到领域驱动设计的过程中。我把这个问题抛给了所有的听众来一起讨论。讨论的结果这样阐释了需求和领域的关系:
领域来自需求,但高于需求。相对于善变的用户需求而言,领域知识和领域模型本身是“静止”的,是“不变”的。
让我们假想这样一个需求:我们要开发一款图书馆管理软件。客户可能给了我们很多的需求,比如要一个统计功能,或者快速图书查询功能。恰好,客户的团队中可以为我们找到一位领域专家,请设想她(或他)在大学中学的是文献管理专业,已经在图书馆工作了几十年,熟悉几十年来图书馆的管理方式。她(或他)可以轻松的为你讲解一本书从购入到借出、还回、入库等一系列流程或专业名词,并且她(或他)清楚地知道为什么在“图书馆”这个上下文内要有这样一套流程;为什么这样的管理方式是最适用于图书馆的。
这样的领域专家实际上已经“图书馆管理”这一领域进行过了足够的抽象与总结,从而形成了完整的领域知识体系,也就是领域模型。
如果我们能够找到这样的领域专家,和她(或他)聊一聊,或者我们坐到一起组织一个工作坊,将领域模型从专家的脑子中平移到纸面上。我们能获得什么呢?我们得到的是只要“图书馆”这样的组织存在,它为解决它领域的问题,而积累下来的领域解决方案。无论我们的系统“看起来”是什么样的,无论我们的部署方式是单机部署还是云环境部署,领域内的问题以及解决方案就是这个样子。我们只需要将领域模型再次从纸面平移到软件系统中,就可以完成整个核心领域层的构建。
有了领域模型,实现层面的用户需求怎么办?比如如何能更快速的找到统计页面,如何能够更好的向用户展示数据,如何能方便用户查询。这些实现层面的需求相应的解决方案,是和领域模型本身解耦的。而为了将领域层同实现层建立起关系,我们需要引入应用层,来实现关联关系。
最后一个问题:领域模型在它自己的维度上如果是变化的怎么办?此时此刻我很难想象一个合适的例子,因为领域模型本身的变化意味着领域解决方案的变化,也就意味着领域知识的变化。但针对这样的可能性,我认为Bounded Context的存在,恰好可以解决一个不同领域间、或者同一领域下的子领域间解耦的问题,使得替换子领域模型变成可能。得到这样的结论是因为我做了这样的前提假设:
系统中领域模型的变化必然是渐进的。而这样渐进的过程,必然是从某一个领域或子领域开始,继而扩展到整个系统的。
如果这样的前提假设为真,依据语义相关性及功能相关性来划分的领域或子领域之间的松耦合关系,便是在软件系统层面支撑领域模型演化的重要手段。
在这样的“变”与“不变”共存的软件系统开发中,如果我们想要轻松引入DDD的思路来做领域建模,找到一个像前文描述的那样的领域专家是重要条件。如果找不到这样的领域专家,DDD的唯一途径是通过开发团队同客户需求人员,或最终用户代表的沟通,帮助他们实现领域建模。这样做我们同样可以获得一个领域模型。
但在实践中这样的做法有一个致命的弱点:在领域模型抽象过程中,由于没有领域专家的存在,团队非常容易脱离开领域统一语言本身,形成了一套抽象的“领域语言”,甚至“领域模型”。这样的语言或者模型对于一个系统的扩展性和健壮性是有害的,同时也不能很好的体现DDD本身的优势。
写了这么多,我的点在于,对于敏捷软件开发而言,领域驱动设计对于系统架构设计而言,能够帮助我们识别出系统中相对稳定的部分,从而提升对于变化部分“可变性”的支撑。而要想取得这样的效果,需要团队重视领域专家的存在,倾听他们,和他们交流。因为一个软件系统的存在的意义,仍然是要解决该领域中早已存在的问题。