02 | 分离关注点:软件设计至关重要的第一步
一个失败的分解案例
- 深入了解
- 翻译
- 解释
- 总结
软件设计中的关键一步:分离关注点 在软件设计中,分离关注点是至关重要的一步。文章强调了正确分解问题的重要性,以及分离关注点的必要性。作者通过举例说明了一个失败的分解案例,强调了分解的重要性,并提出了解决方案。在设计中,将一个模块的不同维度分开,有一个专门的说法,叫分离关注点。分离关注点很重要,一方面,不同的关注点混在一起会带来许多问题;另一方面,分离关注点有助于我们发现不同模块的共性,更好地进行设计。分离关注点,是我们在做设计的时候,需要时时绷起的一根弦。文章还提到了两种常见的关注点混淆的情况,即技术和业务的混淆,以及不同数据变动方向的混淆。最后,作者提出了思考题,鼓励读者去了解CQRS(Command Query Responsibility Segregation),并在留言区分享想法。 总的来说,本文强调了在软件设计中正确分解问题的重要性,以及分离关注点的必要性。通过合适的分解和解决方案,可以避免后续问题的发生,提升系统的稳定性和效率。
《软件设计之美》,新⼈⾸单¥59
全部留言(55)
- 最新
- 精选
- Jxin1.cqrs,命令与查询分离,最早是在ddd实战里面看到。其分离啦增删改与查询这两个关注点。 2.静态上,拆分了这两块的代码。使各自可以采用不同的技术栈,做针对性的调优。动态上,切分了流量,能够更灵活的做资源分配。 3.查询服务的实现。可以走从库,这有利于降低主库压力,也可以做到水平扩展。但需要注意数据延迟的问题。在异步同步和同步多写上要做好权衡。 也可以都走主库,这时候查询服务最好能增加缓存层,以降低主库压力,而增删改服务要做好缓存的级联操作,以保证缓存的时效性。 当然也可以走非关系型数据库,搜索引擎类的es,solr,分布式存储的tidb等等,按需选择。
作者回复: 非常棒的分享!
2020-05-27265 - 西西弗与卡夫卡近期有一本书《被统治的艺术》,正好和软件设计中的职责分离策略异曲同工。 我们知道明朝自朱元璋开始有一个顶层设计,就是每家每户做什么,一开始就规定好了。军队也是一个固定职业,即军户制。比如说国家需要100万个士兵,那就要有100万个军户,每户出一个兵,世世代代都是这样。如果这个兵逃了或者死了怎么办?家族里就再出一个来补充。 这会带来什么后果呢?你可以想象一下,如果儿童节的时候你正坐在家里跟妻子儿女享天伦之乐,忽然有人闯进来,把你抓走了去当兵,只是因为你家族里面的另外一个人当了逃兵或者死掉了。 可见,这样的顶层设计会给自己的家族带来各种不确定性甚至家庭悲剧。人民群众想出了很多的策略来对付这样的制度。 有种设计是这样的,就是每个家族中选出一个分支代表整个家族去当兵,与之相对的是家族的其他分支需要共同出一笔钱,世世代代赡养这个当兵的分支。此外还有其他一些「福利」,比如说,如果原本他在家族中的排位比较低,那他的后代就可以在家族的各项活动中提升座次。 这个世世代代当兵的分支会比较惨,但带来的好处是这个家族中的其他分支就会少受骚扰,得以繁衍。 事实上这样的策略运行得不错,有些家族好几代人一直都执行这样的策略,甚至贯穿了几乎整个明代。 某种角度说,这就是一种职责分离,将国家统治的要求和家族稳定繁衍的需要分开。
作者回复: 刚好最近万维钢老师讲了这本书中的内容,但你从软件设计的角度去理解这个问题,确实让人有一种耳目一新的感觉。
2020-05-27249 - 夏天我发现大家在工作中往往不做分离,分析需求的时候把方案揉在一起。 可以怎样去练习做分离呢?
作者回复: 有一种从小事练起的方法,就是写代码时,把自己写的函数行数限定在一定的规模之下,比如,10行。超过10行的代码,你就要去仔细想想是否是有东西混在了一起。 这种方法锻炼的就是找出不同关注点的思维习惯,一旦你具备了这种思维习惯,再去看大的设计,自然也会发现不同的关注点。
2020-05-2734 - 北天魔狼想起Kent Beck 说的一句话,大致意思是:我不准备在这本书里讲高并发问题,我的做法是把高并发问题从我的程序里移出去
作者回复: 没错,就是这样。
2020-05-2729 - 飞翔老师 比如说订单系统 先下单写到数据库 然后发送消息给消息队列 这两部 没法放到一个事务中去。 如果用本地消息表, order 写数据库 然后 在写本地消息表 这样这两步就放到一个事务中去了 保证肯定成功, 然后在有线程 读取本地消息表 发送队列 如果成功更改本地消息表状态 。 从设计角度讲这就没分离关注点, 这个应该怎么分呀?
作者回复: 我们来分析一下这个需求,下单入库和发消息给下游,这确实是两个动作,但这两个动作的顺序一定是这样吗?它们一定要在一个线程里完成吗? 我们可不可以先发消息呢?比如,我们把消息发给下游之后,有一个下游接收到消息之后,再把消息入库。如果这样做的话,发消息,由消息队列保证消息不丢,下游入库,又可以保证订单持久化。你看,在这个设计中,其实,并不需要事务,所以,我们也不必为事务纠结了。
2020-05-281028 - Rovebiy老师,我觉得补偿机制还是要的吧,就算换吞吐量大的消息队列,丢失消息还是有可能出现的,只是几率小很多。只是他补偿机制设计得不合理?
作者回复: 如果我们分析是不是丢消息,就要看它在什么情况下丢消息。在之前的业务场景中,丢消息就是因为消息队列处理不过来,而我们换了吞吐更好的队列就不存在这个问题了。 其实,我们真正需要的是可靠的信息传送通道,至于是不是消息队列不重要。如果怕丢消息,可以在生产者端重试,可以在消费者端做幂等。补偿是一个能把场景弄复杂的做法,不鼓励。
2020-05-29413 - 小学一年级郑老师 我有个需求描述下(类比): 用户购买网站会员 我目前的设计用了两张表 一张表存储用户购买会员的所有记录, 另一张表 存当前的会员信息 (主要是 开始,结束时间,没有会员等级之类) 单独设计这张表的目的是为了sql关联查询方便,不用再判断是否过期。 但有个问题:我要用定时器一直扫这表,等会员过期了要删除记录。 请问郑老师 我这么做的问题在哪? 更好的解决方式应该是什么?如果做到更细维度的拆分?
作者回复: 首先,你的描述没有把业务和实现分清楚。 你的业务是实现一个会员系统,具体到这里,是判断用户当前是否是会员。更具体一点的话,会涉及会员购买,主要是会员时间要延长,还会涉及到会员资格的判断,也就是当前用户是否是会员。 基于这些内容的判断,可以有不同的实现。根据你当前的实现,可以这样做: * 购买会员,如果会员信息不存在,则添加会员信息,如果会员信息存在,则修改会员结束时间。 * 会员资格判别,根据用户 ID 和当前时间是否在时间范围内查询查询,如果记录存在,则是会员,否则不是。 结合你提供的信息,可以考虑的点是: * 购买会员时,可以产生会员购买记录,此记录仅供后续查询使用; * 只有当会员信息表过大时,才考虑是否需要删除。 在这个实现中,把购买和会员信息分开,把会员信息是否生效与记录是否删除分开了。 以上仅仅是根据你提供的信息进行的分析,如有不当之处,欢迎继续讨论。
2020-06-1111 - 桃源小盼能提供关于分离关注点更多的例子或者相关资料吗?
作者回复: 专栏后面还会多次提到分离关注点的,敬请期待!
2020-05-2711 - 业余爱好者技术和业务混杂的情况,让我想起来一篇文章,大意是说要区分技术异常和业务异常的。也就是说,技术层面的异常信息不应该暴露给上层的业务人员。典型的例子就是大型网站的错误页面,而不是直接把后台的npe堆栈信息抛给用户。
作者回复: 这是一个很好的例子,确实要做区分。
2020-05-27211 - 我是小妖怪🇨🇳有感觉,但是又不明确,没有get到那个点,应该举一下具体的业务来说明或者证明,感觉是理论上的
作者回复: 你把你困惑的点提出来,我争取进一步讲清楚。 我在部落里写了一个回答,可以参考一下。 http://gk.link/a/10iHp
2020-05-279