DDD即领域驱动设计,是一种建模方法论,强调通过分析建模、再设计实现,而不是数据库表驱动。
DDD解决的是核心复杂业务设计,特别强调的是“核心”与”复杂”,DDD只适用于业务系统。
DDD简化业务系统的实现,让业务逻辑高度内聚,聚合之间通过聚合根ID引用与领域事件松耦合,正是高内聚低耦合能让项目代码随着业务需求的不断迭代保持整洁。
通过四层架构设计/六边形架构设计实现与基础设施、框架的解偶。
DDD也解决了微服务的拆分问题。
DDD解决的是业务问题,通过事件风暴识别领域事件、识别限界上下文、识别问题子域、聚合,再对聚合建模。并非解决数据查询问题、数据分析问题。
前面我们了解了如何通过CQRS解决读写分离问题,然而……
在实战DDD过程中我们总会遇到一些即不适合领域建模也不能简单通过CQRS解决的问题,如为前端提供住址、性别等选项的接口如何实现、用户标签功能如何设计、评价功能如何设计……
对于这些看似简单的问题,我们要考虑的是如何避免过度设计,保持架构的简洁以及扩展。
为前端提供选项有非常多的场景。有些选项是个枚举类型,如性别、订单类型;有些选项需要支持增加或删除,如商品分类,虽然实体需要通过ID引用它,但却又不适合作为实体存在。
无论是枚举类型的选项,还是通过ID引用却又不适合作为实体存在的选项,都应该作为值对象。可以直接在应用层读写存储/缓存中间件,如mysql、es、redis。
下文中会出现的领域(业务)名词:达人:网红、红人,有粉丝群体的个人、有一定影响力的个人;OTO探店:线上报名线下探店,线上推广(主要服务美食点,为商家推广线下店铺);
我们项目中有需要实现为达人打标签的需求,在接到这个需求时,我们首先做的就是分析标签能产生什么样的价值,再通过事件风暴看是否能分析出领域事件。
结合项目业务分析,达人标签在我们项目中发挥的价值其实就是用于兴趣匹配推送,在OTO探店业务场景下,兴趣匹配可以理解为根据达人喜好的口味给达人推送相关店铺的订单。
分析到这里我们就清楚了标签功能要做的就是数据分析还有算法推荐,除此之外,标签没有任何领域事件(行为、业务动作),标签不存在领域上下文,所以不应该过度设计。
另外,为达人打标签/达人自己打标签,那么标签就是达人的值对象,与达人的性别、年龄、住址等属性都是达人的值对象。达人可以添加标签或者删除标签。
所以最终我们实现的标签功能就只是在达人的聚合根上添加一个标签值对象。给达人聚合根添加修改标签的方法。
另外我们需要为前端提供标签选项,支持管理员添加或删除标签,与实现普通选项一样,我们单独在达人聚合下提供一个应用层的LableService,直接操作数据库增/删标签。
标签表的存在只是提供标签选项,达人给自己打标签只能从系统提供的标签中选择,标签相当于只是选项。因此达人的标签值对象并非存标签的id,而是直接存标签的名称。
当标签选项删除时我们不应该为所有打了该标签的用户默认删除他的标签,这实际是不允许的,就像我们不能随便改用户的性别、住址一样。
只有在达人更新标签时不提供已经删除的选项,让达人自己选择新的标签更新,更新后旧的标签全部被替换也就达到删除的目的。
除选项、标签外,我们要做的还有一个评价功能。
根据事件风暴我们分析出以下领域事件: a、达人完成任务后商家可评价达人(每个任务只允许评价一次) b、商家评价达人后更新达人的得分(如内容质量得分) "商家评价达人"涉及到任务、达人、商家三个维度,达人可以查看商家给自己的评价,商家也可以查看自己给哪些达人写了评价,并且都能查看任务关联的评价。
显然,无论是把评价功能放到任务上下文还是达人上下文、商家上下文,都不合理。因此我们为评价划分出评价上下文,识别出评价问题子域。 商家评价达人作为评价上下文的一个聚合,后期可能还会实现支持达人评价店铺,而达人评价店铺又是一个评价上下文的聚合,评价上下文存在多个聚合。
我们在实战DDD过程中,在实现前端选项、标签功能、评价功能过程中也做了详细的分析再设计,目的都是避免过度设计。
不断摸索……