04|跨越现实的障碍(上):要性能还是要模型?

2021-07-01 徐昊
《如何落地业务建模》
课程介绍


讲述:徐昊

时长:大小22.25M


你好,我是徐昊。今天我们来聊聊通过关联对象(Assocation Object)建模聚合(Aggregation)。
在前面三节课,我们学习了领域驱动设计中的“两关联一循环”:模型与软件实现关联;统一语言与模型关联;提炼知识的循环。其中,统一语言与提炼知识的循环作为一种更为平衡的权责关系,促进了业务方与技术方更好的协作。而这一切又是以模型与软件实现关联为基础。
然而落地到实践中,关联模型与软件实现总有一些让人纠结与苦恼的地方。引起这些苦恼的主要原因是架构风格的变化。我们已经从单机时代过渡到了多层单体架构,以及云原生分布式架构,但我们所采用的建模思路与编程风格并没有彻底跟上时代的步伐,这种差异通常会以性能问题或是代码坏味道的形式出现。
如果我们想要真正发挥出领域驱动设计的作用,就需要在不同架构风格下,找到能够维持模型与软件实现统一的办法。这也是这个领域常看常新,总能产生新实践的原因。
因而接下来,我...

展开全文
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。

精选留言

  • Geek_3531cc
    置顶
    2021-07-01
    感谢老师的好文章。我这边有个疑问,专栏聚合了内容同理也可以用关联对象模式,那如果作者需要对专栏里的其中一个内容进行修改,需要先获取专栏然后通过关联对象获取要修改的内容进行修改,再将整个专栏聚合进行保存,这样是否还是没有保障到性能呢?

    作者回复: 可以在关联对象上直接增加方法,关联对象表示聚合逻辑。灵活很多

    
    5
  • 阿鸡
    2021-07-02
    想请问为什么不直接在UserRepository中添加关于分页的逻辑?虽然能感觉到不太恰当,但是好像也没暴露逻辑,并且user也没依赖具体db实现

    作者回复: 这样user就退化为纯粹的无逻辑数据对象了,也就是贫血对象

    共 2 条评论
    9
  • webmin
    2021-07-03
    以前只是觉得语言中对象与关系型数据库之间的转换非常不自然,从未问过为什么会是这样,感谢老师今天帮我理清这层关系,原来二者在单体架构时期是等价,是因为后来分层以后才被割裂了,感觉现在的多层是把以前在语言内部实现的机制给放大化了,且为了通用性把CRUD等细节暴露出来了,导致编程语言在使用时感觉不那么自然和一体化了。

    通过将隐式的概念显式化建模,就我自己的理解是需要把隐藏背后想表达的真实意途给找出来,比如今天的例子就是要操作数据,操作数据可以是操作数据库也可以是操作NOSQL等其它各种实现方式,第一反应是操作数据库是一种惯性思维,它并不是真正想要做的事。
    展开

    作者回复: 之前在美国的DDD大会上很多实践者严肃地讨论要不要放弃关系型数据库。

    
    6
  • Jxin
    2021-07-02
    关于内容:
    1.基础类型偏执。这是一个坏味道,但仅在个人或者小团队的小项目中能看到它被认真对待(也只有刻意训练,践行代码健身操时能被认真对待)。一旦项目变大时间变长人数变多,不知不觉就被抛弃了(毕竟这么写对于不理解的人来说真的会被骂死。一段代码要求阅读者有比较高的认知才能有比较好的可读性。以教育阅读者为前题的同理心写出来的代码还算得上同理心吗?)。
    2.CQRS。就这个分页场景,我多半是以单独的查询模型来承接。只有在命令/写操作才会构建聚合来实现。这么想来,似乎破坏了一个聚合模型应有的完整性。停顿几秒钟,这里我本觉得只是遵循CQRS,但实际上只是单纯不希望领域聚合实体具备任何io操作。确实,这样没有依赖任何除jdk程序库之外的技术代码,但是pojo对象的行为运行期具备db操作,而这自然又会牵扯到框架,那么这个pojo对象便不再纯粹,只能算是伪pojo。概念的咬文嚼字没有意义,不过伪pojo多了io操作这个不可靠因素,测试验证自然也得多些操作和心思,这是好是坏呢,从长期看又如何?。合理了模型的概念却提高了迭代维护时的心智负担,这是个问题。毕竟我们学习概念,并非想捧着去吵架,而是想通过概念解决自己的实际问题。

    课后题:
    1.隐式概念:规则,流程,参数(一个函数入参太多时,往往会引起我们的关注和思考,看看这里是不是有一个隐式的概念能包含这些参数)
    2.发现有滞后性,基本只有在迭代中,坏味道积累到一定程度,引发注意,重新定义和审视时才能发现。但更多的时候是被遗漏。不过漏了本身感觉也并非不合理,不见得就是麻烦。
    展开

    作者回复: pojo是幻想 cqrs是邪教

    共 3 条评论
    4
  • 赵晏龙
    2021-07-05
    关联对象我更多的是用来解决业务上的多对多关系,至于分页这个逻辑,我倒是目前还没遇到过这样的场景,不过看完有一些疑问:Subscription虽然通过接口隔离了数据库操作,但是如果Subscription本身有一些业务逻辑呢?是否就考虑把接口换成抽象类,在抽象类中实现业务?
    另外,在我看来,这种方式应该只在遇到数据库性能问题的时候使用,不应该作为通用方法来使用。有一些影响领域模型只对业务模型进行实现的单一职责。

    另另另另外,CQRS邪教您如何解读的?
    展开

    作者回复: 作为通用方法建模的是集合逻辑。有整整五年 我们在全球范围内 看到使用cqrs的项目几乎都失败了 仍然很多人前赴后继 不是邪教是什么

    共 3 条评论
    3
  • Oops!
    2021-07-01
    集合是面向对象模型中广泛存在的概念,如果全都使用关联对象进行建模, 是否会导致类爆炸呢? 有什么可以遵循的规则或者方法来鉴别哪些隐式的集合概念使用关联对象进行建模比较好, 哪些则不然呢? 是不是如果两个对象之间仅仅是简单的包含关系, 可以先用系统提供的集合容器来建模实现, 等到业务复杂了, 需要对这个集合进行除了增删改查之外的操作时, 再使用关联对象进行建模?



    展开

    作者回复: 需要持久化或者来着三方系统的聚合

    
    4
  • tongzh
    2021-07-05
    关键对象模式,不仅成功解耦了领域模型和具体实现,还成功构造出了富含知识的模型
    
    2
  • OWL
    2021-08-05
    关联对象确实是一种很巧妙的方法。但是也有疑惑,比如UserRepo获取user后,修改其部分subscriptions,容易产生修改逻辑后UserRepo.set(user)。而其实应该是在Subscriptions关联对象上操作。关联对象隐藏了Subscription聚合的Repo。

    而Eric的DDD中,则是大聚合拆分出多个小聚合,然后通过小聚合中持有聚合根的Id相互关联。同时小聚合自然有自己的repo。而这种拆分,模型的完整性和独立性也减弱,所以需要结合service来完成业务。

    既然引入关联对象,完全去Repo如何?UsersRepo直接用Users来替代。
    展开

    作者回复: 可以

    
    1
  • 大海浮萍
    2021-07-02
    我们最近在做聚合落地的时候确实遇到了性能问题,有两个问题想请教一下:
    第一个问题,以user-subscription聚合为例,使用关联对象,那么在聚合的持久化上,是不是得分为两步?第一步是先在user实体中使用关联对象接口先持久化subscription的实体,第二步是等整个聚合计算结束后,再持久化聚合根用户,这样算是真正的业务关注点与技术关注点分离吗?毕竟你是在user实体中显示地调用接口做db操作。
    第二个问题,随着迭代的进行,关联对象接口的impl类中db操作会越来越多,从而导致user行为中大量夹杂着和数据库的交互,会不会逐渐退化成面相过程编程?

    课后题
    在业务系统实践中,个人认为其中一大复杂度来源于规则校验,往往一个用例中伴随着大量的规则校验,这里面可能会有隐式概念,如果不能准确识别,并尽早建模,可能会导致代码臃肿,架构腐化
    展开

    作者回复: 持久化user是通过user repo进行的,user不会调用任何db 它只调用接口。从实际执行上看 user的确夹杂db行为,但是从user来看并没有,db行为被封装了

    共 4 条评论
    1
  • 狩月
    2021-07-01
    这个模式很接地气啊!期待更多内容
    
    1
  • 无争就是yk
    2021-11-18
    UserRepositoryDB 中每次查询到的对象都要setMySubscription 这个隐性开销和团队认知成本不小。另外如果是在真实的spring 项目中,mySubscriptions 是注入到User 中的吗?这样的话感觉MySubscriptionsDB 持有User 对象有点奇怪。
    
    
  • mars
    2021-11-16
    解决方式有点类似于DCI架构中的场景,在什么样的场景中扮演什么样的角色,从而有什么样的行为。分而治之,减少上帝类,好文。
    之前可能只能只领域服务实现的部分逻辑可以聚合到模型中,聚合关系更强,👍
    
    
  • .benxiaohai52
    2021-11-13
    能否给出实用的demo关联对象相关的,这块一直是我的疑惑
    
    
  • 八歌
    2021-10-09
    计算机的所有问题,都可以通过夹塞一个中间层来解决😂
    
    
  • 黄大仙
    2021-09-11
    在建模完毕后,实现模型的接口时,暗含了实现人员必须知道的一个逻辑:UserRepositoryDB 必须在获取 User 时在 User 内设置好 MySubscription。
    这个隐藏的逻辑该如何优雅地由建模人员传承到实现人员?

    作者回复: 这哪里隐藏了?

    共 2 条评论
    
  • SochiLee
    2021-09-07
    如果要分页查user,则需要构建用户的拥有方,比如用户组UserGroup,再使用关系对象MyUsers进行分页查。我这么理解对吗?
    
    
  • dbzh
    2021-08-30
    老师可否讲讲CQRS的项目为什么几乎都失败了呢?

    作者回复: 超纲了

    
    
  • feihui
    2021-08-22
    觉得本质还是在于看待角度,在 subscription repository 上做分页是逻辑泄露,原因在于单个 subscription 不存在分页、总数一说。其实感觉在 List 的基础上实现分页也可以的,反正 list 也是一个接口,本身也就具有行为。可能没有经历过老师那个年代,OOP中内存集合和数据库的割裂没有太多感触,感觉增加个中间层就又链接上了(这不也是解决问题的万金油)
    共 1 条评论
    
  • awephy
    2021-08-11
    从另一个角度看,前端界面显示的多样化是分页需求驱动的诱因,而领域建模往往不会把前端的变化纳入考虑范围内,所以分页的需求,使用数据驱动也未尝不可。倘若这么做,系统内会同时存在领域驱动和数据驱动两者设计理念,写入链路遵从领域驱动,保证领域的完整;复杂的查询链路遵从数据驱动,应对前端展示的需求。

    作者回复: 不是复杂查询 就是简单查询

    
    
  • 张振华
    2021-08-11
    我的仓储是泛型IOrderRepository<T,String>,我在应用时不知道怎么传这个T。因为按照理解就是实体是充血模型,它作为泛型是否合适,应该怎么办?

    作者回复: 这只是泛型签名而已 跟贫血还是充血无关

    共 2 条评论
    