如何一步一步用DDD设计一个电商网站(九)—— 小心陷入值对象持久化的坑

news/2024/5/14 8:19:54/文章来源:https://blog.csdn.net/weixin_33813128/article/details/86355353

本系列所有文章

如何一步一步用DDD设计一个电商网站(一)—— 先理解核心概念

如何一步一步用DDD设计一个电商网站(二)—— 项目架构

如何一步一步用DDD设计一个电商网站(三)—— 初涉核心域

如何一步一步用DDD设计一个电商网站(四)—— 把商品卖给用户

如何一步一步用DDD设计一个电商网站(五)—— 停下脚步,重新出发

如何一步一步用DDD设计一个电商网站(六)—— 给购物车加点料,集成售价上下文

如何一步一步用DDD设计一个电商网站(七)—— 实现售价上下文

如何一步一步用DDD设计一个电商网站(八)—— 会员价的集成

如何一步一步用DDD设计一个电商网站(九)—— 小心陷入值对象持久化的坑

如何一步一步用DDD设计一个电商网站(十)—— 一个完整的购物车

如何一步一步用DDD设计一个电商网站(十一)—— 最后的准备

如何一步一步用DDD设计一个电商网站(十二)—— 提交并生成订单

如何一步一步用DDD设计一个电商网站(十三)—— 领域事件扩展

 

 

阅读目录

  • 前言
  • 场景1的思考
  • 场景2的思考
  • 避坑方式
  • 实践
  • 结语

 

一、前言

  在上一篇中(如何一步一步用DDD设计一个电商网站(八)—— 会员价的集成),有一行注释的代码:

    public interface IRoleDiscountRelationRepository// :    IRepository<RoleDiscountRelation>
    {RoleDiscountRelation Get(string roleId);}

  其中涉及的到问题是关于值对象的持久化问题。是的,由于我们之前的设计中持久化是仅针对聚合根的:

    public interface IRepository<T> where T : AggregateRoot{string NextIdentity();void Save(T aggregate);T GetByIdentity(string identity);}

  但是有时候难免会遇到一些需要持久化值对象的场景:

  场景1:一些不属于任何聚合根的对象,本身又可以当作一个不可变的值来看待(如省市区信息等),当然的确某个地区改名了可以作为一个新的值对象来表示。那么我们在把它们建立为值对象的同时,又需要持久化到数据库。这里就如这个等级折扣。

  场景2:一个聚合根的内部引用了一个值对象的集合,那么如果使用的是关系型数据库进行存储,必然需要单独存一个表。

  其中场景1正好是我们Demo里遇到的场景,下面来一起阐述下我对这2个场景的理解和处理方式。

 

二、场景1的思考

  整个问题的解决方式,首先需要梳理清楚3个基本概念:“聚合根”、“实体”、“值对象”这3者的关系。这个我在(如何一步一步用DDD设计一个电商网站(二)—— 项目架构)中有提及。因为涉及到持久化,所以我们可以再通过分析这3种对象的生命周期来帮助思考。

  聚合根:独立存在的对象,是代表某个限界上下文中的一个高内聚的整体概念。他的生命周期是其所属上下文中所承担的职责的周期。

  实体:无法独立存在,是聚合根的一部分,生命周期由所属的聚合根掌控。

  值对象:可以独立存在,但是无法进行自我管理,可以描述任何聚合根/实体,无生命周期的概念,也可以理解为永生(无限生命周期)。

  把任何一个复杂的事物化繁为简的方式就是不断的提炼,归约。动静分离就是归约的一种方式,笔者我认为在DDD中“动”就是聚合根和实体,“静”就是值对象,如果能不断的提炼出“静”的部分对于整个领域的理解复杂度是有帮助的。“动”是复杂的“似生命体”,是有寿命的;“静”是简单的“死物”,它一直存在,而且一直在那里。

  因此笔者我认为只要是可独立存在的对象都可以使用Repository来持久化。那么我们的Demo中,既然已经决定将等级和折扣率建立为值对象的话,接下去的持久化要怎么做呢?请看Part Ⅳ。

 

三、场景2的思考

  场景2里有一个比较容易踩进去的坑,为了持久化把原本设计成值对象的改为实体(特别是针对一个值对象的集合的时候,需要一个唯一表示来区分其中多个值对象)。特别特别注意,当在脑海中出现这个意识的时候,需要在思维上保证从领域建模的角度思考,而不是为了持久化。因为实体建模是一种数据化的建模方式,很大程度上收到了数据库范式的影响。这里引用[Vaughn Vernon]《实现领域驱动设计》中的4个问题:

  1.我们当前建模的概念表示领域中的一个东西呢,还是只是用于描述和度量其它东西?

  2.如果该概念起描述作用,那么它是否满足以下几大特征?

    ①它度量或者描述了领域中的一件东西。
    ②它可以作为不变量。
    ③它将不同的相关的属性组合成一个概念整体。
    ④当度量和描述改变时,可以用另一个值对象予以替换。
    ⑤它可以和其他值对象进行相等性比较。
    ⑥它不会对协作对象造成副作用。

  3.将该概念建模成实体是不是只是持久化机制上的考虑?

  4.将该概念建模成实体是不是因为它拥有唯一标识,我们关注的是对象实例的个体性,并且需要在其整个生命周期中跟踪其变化?

  如果你的答案是“描述,是,是,不是”,那么此时你应该坚持用值对象。我们不应该让持久化影响到领域对象的建模。

  那么我们该怎么做呢?请看Part Ⅳ。

 

四、避坑方式

  数据库选型上在Nosql出现后,可以很好的避免了这里的持久化问题。但是弱化的关系之后导致一些查询问题需要做更多的工作去解决。并且我认为在业务复杂的电商系统中,用关系型数据库作为最终的技术选型还是最常见的一种方式。那么在使用关系型数据库的情况下,我们可以通过使用以下几种方式解决这个问题:

  1.把值对象中的属性作为所属实体/聚合根的数据列来存储。

    缺点:会导致数据表列数较多,在一个数据页存储的数据量变少,影响数据库表的使用性能。

  2.把整个值对象序列化后作为所属实体/聚合根的数据列来存储。

    缺点:出现大数据长度的列,页会导致在一个数据页存储的数据量变少,影响数据库表的使用性能。另外无法直接通过SQL来查询值对象的属性,需要自定义做反序列化操作。

  3.如果是ORM类的框架,那么建立一个基类通过protected的标识列来委派这个唯一标识,但是在实际运用过程中是感觉不到这个存在的。

  4.如果不是ORM框架或者本身框架支持,那么可以通过无主键的方式存入到数据表中。

    缺点:是无法嵌套模型。

 

五、实践

  我想上面说的4种方式中的1、2、4都比较好理解,所以在我们的Demo中,我准备使用第3种方式来处理当前的值对象持久化。先看下我们当前抽象出来的几个核心类。

    public abstract class ValueObject{}
    public abstract class Entity{}
    public abstract class Aggregate : Entity{}

  简单的不能再简单的,仅仅起到了一个表示作用,但是也体现出了这3个对象之间的联系。根据本篇讲述的内容,再进行一次进行抽象。变成如下图1所示的一个关键模型。

                            【图1】

   其中需要注意的是Entity中的ID其实就是对DelegateIdentifier.Identity的引用,也就是仅仅为了做一个Public的公开。另外AloneStorableValueObject与ValueObject唯一不同是其需要持久化并独占一个数据表,而ValueObject是不需要持久化或者跟着所属的聚合根持久化的。然后我们的IRepository<T>变成下面这样约束:

    public interface IRepository<T> where T : DelegateIdentifier, IAloneStorable{string NextIdentity();void Save(T aggregate);T GetByIdentity(string identity);}

  这样只有继承了AggregateRoot和AloneStorableValueObject可以有自己的Repository了。

 

六、结语

  从业务角度来说设计就是不断的梳理业务再抽象建模的过程,不是一蹴而就,也没有一招打遍天下的方式。需要不断的演进、找到最适合的设计方式。从更泛角度来说设计也是约束、定义规则的过程,一套清晰的规则可以为整个项目的所有开发者往共同的目标前进起到事半功倍的效果。

 

 

 

本文的源码地址:https://github.com/ZacharyFan/DDDDemo/tree/Demo9。

 

作者:Zachary_Fan
出处:http://www.cnblogs.com/Zachary-Fan/p/DDD_9.html

 

 

▶关于作者:张帆(Zachary,个人微信号:Zachary-ZF)。坚持用心打磨每一篇高质量原创。欢迎扫描右侧的二维码~。

定期发表原创内容:架构设计丨分布式系统丨产品丨运营丨一些思考。

 

如果你是初级程序员,想提升但不知道如何下手。又或者做程序员多年,陷入了一些瓶颈想拓宽一下视野。欢迎关注我的公众号「跨界架构师」,回复「技术」,送你一份我长期收集和整理的思维导图。

如果你是运营,面对不断变化的市场束手无策。又或者想了解主流的运营策略,以丰富自己的“仓库”。欢迎关注我的公众号「跨界架构师」,回复「运营」,送你一份我长期收集和整理的思维导图。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_801687.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【面试精选】关于大型网站系统架构你不得不懂的10个问题

该文已加入笔主的开源项目——JavaGuide&#xff08;一份涵盖大部分Java程序员所需要掌握的核心知识的文档类项目),地址:https://github.com/Snailclimb/JavaGuide 。觉得不错的话&#xff0c;记得点个Star。下面这些问题都是一线大厂的真实面试问题&#xff0c;不论是对你面试…

275mib为什么不能联网_为什么HAO123类导航网站退出了历史舞台?

导航网站曾经是很多人的默认主页&#xff0c;类似好123这样的导航网站甚至一度占据50%以上的互联网入口&#xff0c;直接被百度以5000万人民币和一部分百度股权收购。这样简单的网站&#xff0c;却有这样大的价值&#xff0c;这是很多人想象不到的。然后如今好123虽然没有完全退…

.NET Core实战项目之CMS 第十七章 CMS网站系统的部署

.NET Core实战项目之CMS 第十七章 CMS网站系统的部署 原文:.NET Core实战项目之CMS 第十七章 CMS网站系统的部署目前我们的.NET Core实战项目之CMS系列教程基本走到尾声了&#xff0c;通过这一系列的学习你应该能够轻松应对.NET Core的日常开发了&#xff01;当然这个CMS系统的…

pageadmin网站制作 如何修改和管理网站模板

在使用pageadmin CMS 的同时&#xff0c;遇到问题可以参考官网帮助中心。1、网站模板目录地址/templates目录&#xff0c; 2、点击展开后&#xff0c;每个目录就是一个网站模板&#xff0c; 前端设计师制作的新版本都可以放到这个目录下&#xff0c;模板的开发请参考模板制作教…

IE打开https网站时,取消证书问题提示

上面介绍了&#xff0c;调用IE来打开对应的网页问题&#xff0c;但是在实际测试中&#xff0c;有些网站是采用https协议的&#xff0c;这时候IE浏览器会弹出如下窗口&#xff0c;一般手动选择后&#xff0c;才可进入登录界面&#xff0c;那么该如何解决呢&#xff1f; 1、点击【…

爬取彩票网站数据界面版

完整代码 import requests from lxml import etree import xlwt from pymongo import MongoClient from tkinter import *#设置浏览器的请求头&#xff0c;告诉服务器我们是从浏览器来的&#xff0c;作用是阻止被网站反爬 headers {User-Agent: Mozilla/5.0 (Windows NT 6.1; …

SEO关键词优化:如何理解被百度快速索引?

2019独角兽企业重金招聘Python工程师标准>>> 经常会有SEO人员讨论&#xff0c;为什么我的SEO关键词总是不被快速索引&#xff0c;而实际上这里主要指的是&#xff0c;针对特定关键词的核心内容&#xff0c;那么&#xff0c;它主要涉及如下两个指标&#xff1a; ①索…

阿里云快速搭建 WordPress 个人网站

创建服务器 登录轻量应用服务器控制台&#xff0c;点击页面右上方的 创建服务器。在弹出的页面上&#xff0c;为列出的各选项做出选择&#xff0c;可同时领取阿里云优惠劵 点击 立即购买。浏览订单详情&#xff0c;确认无误后&#xff0c;点击 去支付。点击 确认支付。点击 进入…

大型网站技术架构(七)网站的可扩展性架构

2019独角兽企业重金招聘Python工程师标准>>> 扩展性是指对现有系统影响最小的情况下&#xff0c;系统功能可持续扩展或提升的能力。 设计网站可扩展架构的核心思想是模块化&#xff0c;并在此基础上&#xff0c;降低模块间的耦合性&#xff0c;提供模块的复用性。模…

网站加入代码让网页以电脑端打开_网页上的文本不让你复制下载?老司机教你几招,轻松免费复制...

无论是在工作中还是在学习中&#xff0c;大家都会遇到网页上的文本无法复制粘贴的情况&#xff0c;然后只能硬着头皮将里面的内容一个个的用键盘敲出来&#xff0c;其实这些被限制的网页时可以复制的&#xff0c;今天就教你几招&#xff0c;让你轻松免费复制。一、手机端如果是…

如何在地图上显示图片和经纬度_如何在企业网站上调用百度地图?让用户清楚知道你的位置...

如何在企业网站上调用百度地图&#xff1f;基本上每个企业网站都会有联系方式展示给用户&#xff0c;但平常的联系方式就是留下地址、电话等联系方式&#xff0c;不够直观&#xff0c;我们这里讨论一种方法&#xff0c;让用户可以直接看到我们的企业在百度地图上的位置&#xf…

js 直接打开选择文件窗口_从建站到拿站 -- JS基础

喝酒喝到吐的是我&#xff0c;在家大门不出二门不迈的也是我&#xff0c;仗义疏财的是我&#xff0c;扣到几块钱也舍不得花的也是我&#xff0c;放肆到骚话满篇的是我&#xff0c;谨慎到沉默寡言的也是我&#xff0c;我自己都不知道自己是什么人。。。---- 网易云热评提示&…

python爬取新闻网站内容findall函数爬取_Python爬取新闻网标题、日期、点击量

最近接触Python爬虫&#xff0c;以爬取学校新闻网新闻标题、日期、点击量为例&#xff0c;记录一下工作进度目前&#xff0c;感觉Python爬虫的过程无非两步&#xff1a;Step1.获取网页url(利用Python库函数import urllib2)Step2.利用正则表达式对html中的字符串进行匹配、查找等…

网站设计php和python,源码:基于Python网站的设计与实现

无论您需要任何题目请直接联系客服索取系统演示资料&#xff01;咨询可赠送设计资料教程一份&#xff01;经多年积累&#xff0c;本店有java、jsp、php、、安卓、IOS、vb、net,vc、c#等设计上万套&#xff0c;本店保证所有设计可正常运行&#xff01;也可定做&#xff01;收藏管…

从哪些方面提升服务器性能优秀强大,网站服务器升级应该从哪些方面提升性能!...

网站服务器在使用运行一段时间之后&#xff0c;就需要考虑对服务器进行升级&#xff0c;一方面服务器升级能够更好满足企业对网站的使用需求&#xff0c;另一方面服务器在进行升级之后&#xff0c;用户在浏览网站时能够更加迅速&#xff0c;体验速度会更好。因此服务器升级也是…

企业网站 源码 服务邮箱:_口碑营销:惠州企业网站推广定制服务

口碑营销&#xff1a;惠州企业网站推广定制服务 qnmsptdb口碑营销&#xff1a;惠州企业网站推广定制服务 软文则是从软文推广衍生出来的&#xff0c;它的分类具体有推广类型的软文、那么软文的主要形式有哪些呢。的策略。一家的站方案&#xff0c;网页排名、关键词、整站都是重…

备案网站未正规绑定服务器,网站备案对收录到底有没有影响

不论是之前备案条件宽松的时候还是现在备案严格&#xff0c;总有很多新手站长有疑惑&#xff0c;网站备案这玩意对网站收录究竟有没有直接影响?近日一位圈内资深站长给出了自己的答案&#xff1a;做了几百个网站&#xff0c;直白讲网站备案跟网站收录是没有任何影响的!我知道&…

python登录app爬取数据_Python爬取网站上面的数据很简单,但是如何爬取APP上面的数据呢...

​ 前言 在我们在爬取手机APP上面的数据的时候&#xff0c;都会借助Fidder来爬取。今天就教大家如何爬取手机APP上面的数据。 很多人学习python&#xff0c;不知道从何学起。 很多人学习python&#xff0c;掌握了基本语法过后&#xff0c;不知道在哪里寻找案例上手。 很多已经…

url获取网站信息不包含网页源文件内的标签_大型门户网站常用的SEO优化思路!...

一般的网站SEO优化企业需要进行考虑的就是排名、流量、转化率&#xff0c;可是我们对于一个大型门户站的SEO优化则需要充分考虑到了很多事情&#xff0c;比如&#xff1a;网站系统架构、分类、路径、程序设计等等这些方面&#xff0c;所以小编觉得门户网站的SEO优化一定要提前做…

在网站底部放置备案号_艾孜尔江撰

直接将下方的备案号改为你自己的备案号即可。 <p style"background:none;height: 5%;position: fixed;bottom: 0px;width: 100%;text-align: center; "><img style"padding-top:2px;" src"/images/index/相应静态资源目录下的国徽图标.png&q…