汽车讲堂 2018-01-29
领域驱动设计有感 最近3年一直在供应链信息化领域的管理和架构,一方面惊讶于该领域业务知识的复杂,另一方面也惊讶于管理的复杂,特别是今年在做一个系统的架构时,学习了很多的东西。这几年,几乎每年都要花2000+元在买书上。目前负责一个架构的团队有几十号人,一直在思考如何更好的做好的一个复杂的软件产品,因此,也有意识的学习一些知识。最近在看的有两本书《领域驱动设计》、《架构及未来》,本文简单的描述读《领域驱动设计》前面两部分的一些体验。
强烈推荐有志于架构师、CTO,走技术流的兄弟们也好好看一下这两本书。对于一个大型的团队,你不可能再亲力亲为,因而,这时候需要考虑的是如何为团队构建成熟的组织、流程、制度,确保团队可扩展性。阅读《领域驱动设计》的目的,是为系统分析团队探索一条比较有效的系统分析设计方法,希望将其变成团队可以遵循的标准规范,使团队形成更为高效的详细设计方法。 设计的目的是为了建模,通过模型实现更有效的沟通,确保领域专家、技术团队能够为需求达成一致的理解,并且,通过模型可以理解代码,保证代码的可理解性、可扩展性、可维护性。
领域驱动设计的一个重点就是领域建模。谈到领域建模,我们先谈谈领域。领域知识一般是用于描述有哪些角色使用系统完成什么任务。领域知识的描述语句就是“主谓宾”,比如关于订单领域知识的描述,大致是“用户通过商城可以搜索商品,将商品添加到购物车,然后下单付款等待收货。每一个订单由若干商品构成,在下单的时候,商品价格是确定的、优惠也是确定的。供货商会在不同的时期去更改商品的价格和优惠方式……”。这段领域知识的描述的每一个“主谓宾”句子,本质就是由名词(主语和宾语)和动词构成。因此,对领域知识的建模会有以下元素构成:
(1)关联:谓语一般用于关联主语和宾语,比如订单由若干商品(确切的讲,应该成为订单商品,订单商品与商品不同)构成。关联由:组合、聚合、关联、依赖四种类型,这里“订单由若干订单商品构成”,表明订单和订单商品是组合关系,即订单是订单商品组成并且订单商品不能单独存在。聚合是组合的弱化,聚合是弱化的整体和部分的关系,这里的部分可以单独存在。关联相对于组合与聚合会更弱一些,比如这里“用户下单付款等待收货”。用户和订单就是一种关联关系,订单属于用户,用户与订单的关系就是“下单”。依赖是在业务代码中,一个类使用到另一个类的功能,比如付款时,我们会依赖第三方接口来实现具体的付款。付款有不同的方式,我们会抽取为不同的支付接口,仅依赖于接口而不是实现。
(2)实体/值对象:领域知识的名称,会由实体(或称为领域实体)或者值对象来表示,动词则可能是领域实体的一个操作。实体与值对象的差别在于实体具有标识,值对象则没有。
(3)模块/系统:模块和系统用于组织领域知识的边界,确保软件系统的高内聚、低耦合。
(4)Service:一般表示一些可复用的功能,领域实体一般会依赖于服务。
我们通过以上元素,将领域知识,使用大家都能看懂的图形来构建出来,这就是建模。模型必须真实反映了领域知识,并且与代码相对应。对于复杂系统,领域模型会非常庞大,这样的模型图我相信没有任何人有兴趣细看,我们会陷入到细节中,然后不知所措。因此,更有效的建模,会使用分层、模块化、服务化,甚至按照子系统设计,传统的SOA架构也是解决软件复杂性的一种有效的方法。同理,在数据库设计时,一张庞大的ER图也是很恐怖的,模型必须进行分层,由顶向下,由High Level到Details Level。
这里,我们使用图形对“客户下单”领域知识进行了建模(模型图仅示例,不精确)。这个模型反应了客户、订单、商品、优惠折扣及历史价格的关系。
所有的领域实体,具有生命周期,我们通过以下模式来管理领域实体。
(1)聚合(Aggregate):在这里订单、订单商品(订单商品与商品折扣)、订单折扣是聚合关系,订单是这些部分的聚合根。我们使用聚合模式来将这些实体作为整体统一管理。聚合涉及的实体将一起存在、一起消亡,需要满足固定的商业规则。领域驱动设计只允许我们通过聚合根来访问,二不允许直接访问内部实体。这确保整个业务规则的完整性,具备更好的可维护性、可扩展性。
(2)Factory和Repository:Factory用于创建复杂的聚合根,Repository用于加载和保存聚合根到持久存储。 领域驱动设计提倡将系统分成UI层、应用层、领域层和基础设施层。通过这几个元素,我们整个设计就初步具备一定的标准规范了。在后续,我会进一步分享关于聚合、Factory、Repository模式的知识。 这本书可以让我们对设计有一种从朦胧到清晰的感觉,提供了更好的方法论,有助于解决软件的复杂性难题。