在软件开发这个行业中,将开发工作外包是个常见的举措。因为软件工程师对专业性程度要求非常高,而且薪酬普遍高出平均水平(工具人惭愧地拖了后腿),企业为了节省成本和提高效率,在没有软件研发团队的情况下,将开发工作外包给第三方公司或人员,是种行之有效的做法。
而某些传统金融公司,则非常壕气。虽然持有着珍贵无比的架构师,却整天用牛刀杀鸡,让架构师去做项目管理,让架构师去写需求文档,让架构师充当PPT架构师。
工具人就很纳闷了,偌大一个公司,连一个会写需求的采购师都没有么? 工具人想了想,其实原本是有的,嫌工资低,跑了~
好了,接下来进入正题:今天将讨论两种相互矛盾的DDD设计模式:CQRS模式以及规约模式。
CQRS vs 规模模式
这两种DDD设计模式,从本质上讲,有着巨大的不同。
首先,我们先简要的描述下这两种模式的含义:CQRS模式主张将一个统一的领域模型,拆分成一个读模型,一个写模型,这个看似简单的方式,给开发者带来了相当大的好处,其中最重要的是简单。
一般来说,应用程序的命令端和查询端的需求截然不同。为了解决这种显著差异,为这两种不同场景分别引入单独的模型是种很自然的方式。这同时,又大大降低了我们代码的复杂性。
通过命令与查询模型的分离,我们不必担心用相同的代码处理两个完全不同的用例。因此,我们可以为每个特定的场景,提供独立的解决方案。这可以被认为是单一职责原则在架构层次的一种体现。最后,你会得到两个模型,每个模型只做一件事,而且做得很好。
规约模式的主旨是将一段领域知识封装到一个独立的单元中,然后在不同的场景中重用,其场景主要分为如下三种:数据检索、内存验证和创建新对象。
其中,使用规约模式创建新对象的场景非常罕见,这里先不做讨论。而数据检索是从数据库中获取数据,并查找到符合规约的数据记录。内存验证是指检查某个对象是否符合规约中描述的标准。
规约模式的有用之处在于可以让开发人员避免领域知识的重复编写,相同的规约定义既可用于验证输入数据,又可以从数据库中过滤出需要显示的用户数据。
而CQRS的主旨在于,将数据输入与用户数据查询,切割成两部分:
数据的数据校验属于命令端。因为我们总在数据变更前,校验入参的合理性。而数据检索属于读取端。往往由客户端发起后,系统查询系统或数据库中的数据内容。
CQRS和规约模式在这里有一个明显的矛盾。其中,规约模式主张为这两个关注点使用一个域模型。而CQRS主张将领域模型一分为二,并分别处理这些问题。
这里我们可以发现了一个经典的争议之处——DRY原则(Don't Repeat Yourself)和松耦合原则之间的矛盾。这两个原则都是非常基础性的,它们都是软件开发中的核心原则。
那么,那种方式更好呢?
在绝大多数情况下,松耦合是成功的。
当在高耦合和领域知识复制之间进行取舍时候,我们往往应该两害相权取其轻!复制领域知识确实给我们带来了一定的冗余,但是与问题相比,相对更容易被接受。
而高耦合——则可能是毁灭性的。在高度耦合的情况下,开发人员的会束手无策,因为开发者不仅要考虑满足组件自身内部的需求,还要考虑组件外部以及相关依赖方的诉求。
在数据校验和用户查询两种场景中,往往后者是比较难以被满足的,如果要强行使得领域知识通用化,则会导致两者皆不能被很好的满足。
其实,规范模式仅适合在简单的情况下使用——业务不需要复杂的查询逻辑。在这种情况下,使用这种模式可能会很好。
在大型系统中,推荐尽可能地选择松散耦合,适当容忍读写之间的领域知识重复,为面临的问题选择最合适的解决方案的自由胜过DRY原则。