DailyMart03:如何基于DDD设计商城的领域模型?

news2024/10/5 16:30:16

大家好,我是飘渺。既然有人催更那今天咱们就继续更新DDD&微服务系列!

在面向对象开发中,所有事物都可以看作是对象。然而,在日常开发中,我们通常从数据出发来设计对象的表现形式,这种做法侧重于数据属性的定义,而忽略了领域逻辑的处理过程。虽然这种做法很常见,但并不是DDD推荐的开发模式。在DDD中,我们关注的是领域数据对象,而非仅仅是数据本身。领域模型对象在本质上不同于数据,它包含了一系列的标识和行为定义,而不仅仅是数据属性的定义。

在DDD中,领域模型对象可以分为聚合、实体和值对象三种类型。实体和值对象是聚合的组成部分,而值对象同时也是实体的组成部分。它们之间的关系如下图所示:

3abce25c7c6619c3de36a66bff705f07.png

在上一篇文章[[DailyMart02:DDD领域分解与微服务划分]]中,我们已经对DailyMart进行了领域分解和限界上下文的拆分。在本文中,我们将从DDD战术的角度出发,对DaliyMart进行设计,分析各个限界上下文中的实体、值对象和聚合对象。

1. 领域模型对象

让我们首先了解一下这几个关键对象。

  1. 实体(Entity):实体是具有唯一标识符(ID) 的对象,它在整个生命周期中保持不变。实体的属性可以发生变化,但其ID始终保持不变。例如,在DailyMart系统中,用户可以是一个实体,因为每个用户都有一个唯一的ID,即使他们的姓名、昵称、密码等信息发生变化,这个ID也不会改变。

  2. 值对象(ValueObject):值对象与实体对象相反,它是一种不具有唯一标识符(ID) 的对象,它们是通过其属性值来定义的,它的一个重要特点是它们是不可变的,一旦创建,它们的属性就不能被修改。如果需要修改值对象的属性,我们需要创建一个新的值对象来替换旧的值对象。值对象通常表示一些简单的概念,如颜色、地址或者金额等。例如在跨境电商系统中,商品的价格通常会被设计成值对象,价格由货币类型和数值组成,我们关心的是这两个属性的组合,而不是价格对象本身的唯一性。

  3. 聚合(Aggregate):聚合是一组相关的实体和值对象的集合,它们共同组成一个整体。聚合有一个根实体,也称为聚合根,它是聚合中最重要的实体,负责维护聚合内部的完整性和一致性。聚合根可以包含其他实体和值对象,但是其他实体和值对象不能直接访问聚合根以外的实体和值对象。这种限制可以保证聚合内部的一致性和完整性,同时也可以简化聚合的设计和实现。

2. 领域建模

接下来,我们将进行领域模型分析,并从用户限界上下文开始。

2.1 用户限界上下文

为了完成限界上下文的领域建模,我们需深入分析业务场景和需求,以形成业务领域的通用语言。

通过与业务人员的深入沟通,我们总结出以下通用语言:

  • 商城用户首先需要注册,填写账号、密码、邮箱、手机号码等信息,注册成功后,系统赠送100积分。

  • 用户购买商品后,系统会赠送一定积分。这些积分在支付时可以抵扣现金,用户还可以查看自己的积分记录。

  • 用户登录系统后,可以维护自己的收货地址。每个用户可以维护多个地址,可以选择删除其中一个地址或将其中一个地址设置为默认收货地址。商品购买后,系统会将商品配送到默认地址。

根据这些业务需求,我们识别出了一些关键的领域模型:

  1. CustomerUser - 用户,显然是一个实体。它包含UsernamePasswordEmailPhoneNumber等属性,并拥有Points(积分)属性、DeliveryAddresses(收货地址列表)和PointsRecord(积分记录)。

  2. PointsRecord - 积分记录。由于我们需要追踪每次积分的变化,即使属性相同,每个积分记录也是独一无二的,因此它是一个实体。

  3. DeliveryAddress - 收货地址。用户可以删除某个地址或将其他地址设置为默认地址,这意味着我们需要对地址进行操作和管理。在这种情况下,将收货地址作为实体可能更合适。

  4. Points - 积分,即使只有一个属性,它也是一个值对象。将Points设计为值对象的原因是它代表了一个具有特定含义和行为的概念。例如,积分不能为负数,并且在某些操作时需要增加或减少特定的数量。通过将其设计为值对象,我们可以封装这些规则和行为,保证数据的完整性。

  5. 在用户限界上下文中,CustomerUser也是一个聚合,它封装了与用户相关的所有数据和行为,包括用户的基本信息、积分、收货地址和积分历史记录。

在实际操作中,实体与值对象的区别可能并不那么明显。例如,我们也可以把PointsRecord看作是值对象,因为系统只允许查看记录,不允许修改。从这个角度来看,积分记录就像用户积分变化的一个快照。然而,在这里,我们还是选择将其设计为实体,因为我们关心的是每一条独特的积分变化记录。


48a4f6068fe2a2eb96c3ef7952f6c5be.png

2.2 订单限界上下文

完成了用户限界上下文的设计后,现在我们进行订单限界上下文的领域模型设计。在“订单”限界上下文中,通过与业务人员深入沟通,我们总结出了以下通用语言:

  • 用户可以在DailyMart网站上下单购买书籍,每个订单可以包含多本书籍。

  • 订单需要包含收货人姓名、地址、联系电话等信息。

  • 订单需要记录订单状态,比如待支付、待发货、已发货、已完成等。

  • 用户可以在订单中查看每个书籍的详细信息,包括书名、价格、作者、ISBN号等。

  • 用户可以在订单中查看订单的配送状态,比如已发货、运输中、已签收等。

根据这些业务需求,我们确定了一些关键的领域模型:

  1. Order - 订单是一个聚合,因为它有其自己的生命周期,每个订单有唯一的订单ID作为标识。订单包含了RecipientInfo(收货人的信息,包括姓名、地址、联系电话),OrderStatus(订单的状态),以及订单中的书籍列表。

  2. OrderItem - 订单项是Order聚合中的一个实体,代表订单中的一本书。它包含了书籍的详细信息,例如书名、价格、作者、ISBN号等。由于每个订单项都是独一无二的,并且在订单中具有持久性,因此被设计为实体。

  3. RecipientInfo - 收货人信息是一个值对象,它没有自己的唯一标识,一旦订单创建就不能被修改,只能被替换。

  4. OrderStatus - 订单状态是一个值对象,可以被设计成枚举类型,表示订单的不同状态,例如待支付、待发货、已发货、已完成等。

  5. DeliveryStatus - 配送状态也是一个值对象,可以设计成枚举类型,表示配送的不同状态,例如已发货、运输中、已签收等。

6c975ce966374f0185fa9c1267b3cad8.png


2.3 商品限界上下文

接下来完成商品限界上下文的领域模型设计。在“商品”限界上下文中,通过与业务人员进行讨论和沟通,我们总结出了以下通用语言:

  • DailyMart主要是销售书籍(纸质版/电子版)。商品属性相对简单,只需要包含书名、价格、作者、ISBN号、简介、出版日期等几个重要属性。

  • 一本书会属于某一个分类,比如计算机类、设计类、心理学,管理员在另一个运营系统中可以配置分类。

  • 管理员可以对书籍进行推荐。

  • 用户登录后可以对书进行评论。

根据这些业务需求,我们识别出了一些关键的领域模型:

  1. Book - 书籍显然是一个实体,它需要一个唯一标识符。书籍包含ISBNtitleauthorprice、等属性,以及bookType(电子书或纸质书),这可以设计成一个枚举类型。此外,Book还有Category分类属性。

  2. Category - 分类,由于管理员在另一个运营系统中进行配置和维护,分类不需要有太多复杂的行为和属性。在此上下文中,我们可以将Category设计为值对象。

  3. Review - 评论,Review 具有持久性的唯一标识(评价ID),应该也是一个实体对象。

  4. 在商品限界上下文中,BookReview都设计成独立的聚合。Book作为核心实体设计为聚合是很自然的,因为它包含了书籍的所有重要属性。而Review则被设计为独立的聚合,使我们可以更方便地处理评论信息,包括添加、修改和查询等。

865342ebd964907c9b64b7522d462de9.png

2.4 购物车上下文

最后,完成购物车限界上下文的领域模型设计。

在“购物车”限界上下文中,通过与业务人员进行深入沟通,我们总结出了以下通用语言:

  • 用户登录后可以将商品添加到自己的购物车中,每个商品可以添加多个,同时可以添加多个商品

  • 在购物车界面需要显示购物车所有商品的数量,以及每个商品的部分信息,如书名、作者、简介等。

  • 在购物车中可以对商品进行选中,选中商品后自动计算商品的价格合计。

  • 选中购物车的商品可以进行结算,但具体的订单生成和管理在另一个“订单”限界上下文中处理。

根据这些业务需求,我们识别出了一些关键的领域模型:

  1. CartItem - 购物车项,代表了用户添加到购物车的商品以及数量。由于购物车项有其自身的生命周期,可以单独创建、更新和删除,所以它应该被设计成一个实体。购物车项需要包含商品的部分信息,如书名、作者、简介等,以便在购物车界面中显示。

  2. Cart - 购物车,这也是一个实体,购物车包含了多个购物车项,每个购物车项代表了用户添加到购物车的商品以及数量。

在购物车限界上下文中,Cart被设计为核心聚合。它代表了用户的购物车状态,包含了用户希望购买的所有商品的信息。这样,我们可以更方便地处理购物车信息,包括添加商品、移除商品、计算总价等。

9e121716cb6be764a5f29c47e2d1155e.png

...

同样地,我们还可以分析出库存限界上下文和物流限界上下文的领域模型。下面是最终完整的领域模型:

937d9e13997aa7ef681ee2d7eb400068.png

3. 构建领域模型

完成领域建模后,我们可以根据领域模型生成关键领域对象的代码,以便更加准确地实现业务需求。例如,在订单上下文中,几个重要的对象包括:

@Data
public class RecipientInfo {
    private String name;
    private String address;
    private String phoneNumber;
}

public enum OrderStatus {
    AWAITING_PAYMENT, AWAITING_SHIPMENT, SHIPPED, COMPLETED
}

public enum DeliveryStatus {
    SHIPPED, IN_TRANSIT, DELIVERED
}

@Data
public class Order {
    private Long orderId;
    private Long userId;
    private LocalDateTime createdTime;
    private double totalAmount;
    private OrderStatus orderStatus;
    private DeliveryStatus deliveryStatus;
    private RecipientInfo recipientInfo;
    private List<OrderItem> orderItems;
}


@Data
public class OrderItem {
    private Long itemId;
    private String bookTitle;
    private String author;
    private String ISBN;
    private double price;
}

4. 设计数据库

同样的,还可以根据领域模型设计数据库表结构,以确保数据库能够准确地反映业务领域中的实体、值对象和聚合等概念。例如,下面是订单表的数据表设计:

create table order
(
    order_id               bigint         ,
    user_id                bigint         ,
    created_time           datetime       ,
    total_amount           decimal(10, 4) ,
    order_status           varchar(10)    ,
    deliveryStatus     varchar(10)    ,
    recipient_name         varchar(50)    ,
    recipient_address      varchar(255)   ,
    recipient_phone_number varchar(255)   ,
    constraint order_pk primary key (order_id)
);

create table order_item (
    item_id bigint,
    orderId bigint,
    bookTitle VARCHAR(255),
    author VARCHAR(255),
    ISBN VARCHAR(255),
    price decimal(10, 4),
    constraint order_item_pk primary key (item_id)
);

5. 小结

本文采用DDD战术,对 DailyMart 进行领域模型设计。通过识别出实体、值对象和聚合等概念,我们得到了 DailyMart 的完整领域模型,并根据该模型完成了关键领域对象的代码构建和关键表设计。

需要注意的是,领域模型并不是一成不变的,它会随着业务需求的变化而不断调整和优化,随着DailyMart商城系统的开发深入,我也会不断优化和补充它的领域模型。

最后,欢迎关注公众号和加入知识星球,获取最新的文章和源码更新。现在加入知识星球,您还可以享受30元优惠券,每天仅需不到3毛钱。

- End-

公众号回复关键词 知识星球 获取限量30元优惠券加入,每天不到3毛钱。目前更新了SpringCloud alibaba开发实战、Kubernetes云原生实战、分库分表实战、设计模式实战、架构实战、一起学DDD 、SpringBoot 老鸟等,还有每周的送书活动等着你....

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

二、高通相机bringup 流程

和你一起终身学习&#xff0c;这里是程序员Android 经典好文推荐&#xff0c;通过阅读本文&#xff0c;您将收获以下知识点: 一、相机Sensor 点亮相关的文件二、Sensor 驱动文件详解 一、相机Sensor 点亮相关的文件 1.1 Sensor 驱动XML以及CPP文件 Sensor 文件路径&#xff1a;…

搭建Nextcloud私有云 - 零基础搭建私有云盘并内网穿透远程访问

文章目录 摘要视频教程1. 环境搭建2. 测试局域网访问3. 内网穿透3.1 ubuntu本地安装cpolar3.2 创建隧道3.3 测试公网访问 4 配置固定http公网地址4.1 保留一个二级子域名4.1 配置固定二级子域名4.3 测试访问公网固定二级子域名 转载自cpolar极点云的文章&#xff1a;使用Nextcl…

Python网页开发(持续更新ing...)

诸神缄默不语-个人CSDN博文目录 最近更新时间&#xff1a;2023.5.31 最早更新时间&#xff1a;2023.5.31 文章目录 1. 项目实例2. flask包2. Django包3. mod_wsgi包 flask和Django等包是用来写网站的&#xff0c;但这些包构建的网站直接运行是不稳定的&#xff0c;所以需要Apa…

软件设计师(中级)全过程总结

软考总结目录 宏观  学习感受  阶段划分 微观  1.自己看书和看视频&#xff1a;  2.学习的知识点和课后题进行结合  3.做往年的软考真题  4.提炼出相对来说难以攻克的问题组织分享和讨论  5.小组讨论做错的题并进行结构化 总结学习时间上学习方法上学习形式上 宏…

86.建立主体页面-第二部分

上一节我们的基础的页面已经生成了&#xff0c;页面如下&#xff1a; ● 接着我们来编写标题的样式&#xff0c;标题的调整可以根据自己的需求来调整&#xff0c;我这里就直接写 .heading-primary {font-size: 5.2rem;font-weight: 700;line-height: 1.05;color: #333;lette…

一般测试用例执行过程的四个步骤

一般测试用例执行过程的四个步骤 测试用例的执行过程是软件测试中非常重要的一环&#xff0c;它可以有效验证软件是否符合预期的功能和性能要求&#xff0c;进而保证软件的质量和稳定性。一般来说&#xff0c;测试用例的执行过程可以分为四个步骤&#xff1a; 第一步&#xff1…

【C++】struct 和 class 的区别

欢迎来到博主 Apeiron 的博客&#xff0c;祝您旅程愉快。时止则止&#xff0c;时行则行。动静不失其时&#xff0c;其道光明。 目录 1、缘起 2、示例代码 3、总结 1、缘起 在 C 中&#xff0c;struct 和 class 唯一的区别就在于 默认的访问权限不同。区别如下&#xff1a; …

STM32调试功能

文章目录 STM32调试功能1.硬件接口图2.调试原理3.引脚分配4.调试接口使用方式5.芯片配置 STM32调试功能 1.硬件接口图 2.调试原理 Cortex-M内核&#xff08;M0/M3/M4/M7等&#xff09;包含用于高级调试功能的硬件。利用这些调试功能&#xff0c;可以在取指&#xff08;指令断…

设计模式之~命令模式

定义&#xff1a; 命令模式&#xff08;Command&#xff09;&#xff0c;将一个请求封装为一个对象&#xff0c;从而使你可用不同的请求对客户进行参数化&#xff1b;对请求排队或记录请求日志&#xff0c;以及支持可撤销的操作。 为什么需要命令模式? 在我们的软件开发系统中…

100万数据导出,居然爆炸了OutOfMemoryError?【EasyPoi实战系列】- 第472篇

历史文章&#xff08;文章累计460&#xff09; 《国内最全的Spring Boot系列之一》 《国内最全的Spring Boot系列之二》 《国内最全的Spring Boot系列之三》 《国内最全的Spring Boot系列之四》 《国内最全的Spring Boot系列之五》 《国内最全的Spring Boot系列之六》 用…

Web的基本漏洞--SSRF漏洞

目录 一、SSRF漏洞介绍 1.SSRF漏洞原理 2.SSRF漏洞经常存在的位置 3.攻击方式 4.SSRF漏洞危害 5.SSRF漏洞的防范 一、SSRF漏洞介绍 1.SSRF漏洞原理 SSRF&#xff08;服务器端请求伪造&#xff09;漏洞,出现的原因&#xff1a;是因为服务器与服务器之间有一个服务器内网&…

CVPR2023高质量论文 | Consistent-Teacher:半监督目标检测超强SOTA

关注并星标 从此不迷路 计算机视觉研究院 公众号ID&#xff5c;ComputerVisionGzq 学习群&#xff5c;扫码在主页获取加入方式 论文地址&#xff1a;https://arxiv.org/abs/2209.01589 计算机视觉研究院专栏 作者&#xff1a;Edison_G 《Consistent-Teacher: Towards Reducing …

【C语言】sizeof和strlen的区别【详解】

目录 一.sizeof和strlen的主要区别 二.sizeof和strlen分别讲解&#xff08;含例题和详解&#xff09; 1.sizeof 在计算字符型数组时&#xff08;例题讲解&#xff09; 计算整型数组&#xff08;例题讲解&#xff09; 2.strlen 例子一&#xff08;讲解&#xff09;&#…

运维监控Grafana部署

运维监控Grafana部署 简介 安装 部署形式 Grafana支持两种部署形式 自行部署, 可以部署在操作系统之上. 自行提供服务器, 域名等.Grafana官方托管. 无需安装, 在线注册即可得到一个专属于自己的Grafana, 但是要花钱的. 是一种SaaS服务 我们课程选择方式1 安装 Grafana支…

0531最后的挣扎结束于传说中的段错误

部署训练后的缺陷检测模型 Linux Ubuntu18.04双机尝试 报错&#xff0c;Linux内核或是编译器版本不匹配&#xff0c;多次尝试更改18.04的gcc&#xff0c;g&#xff0c;gcc-arm-linux&#xff0c;garm-linux的代码&#xff0c;尝试在Makefile文件里更改编译器路径、添加LInux内…

智慧工厂主题 Meetup 线下报名+福利开启!IoTDB X EMQ 构建数据平台赋能智能制造...

随着全球制造业的竞争日益激烈&#xff0c;智慧工厂成为当今制造业的重要趋势之一。智慧工厂采用了先进的物联网、大数据等科技手段&#xff0c;以期通过智能化、数字化管理和生产&#xff0c;实现高度自动化和高效生产。因此&#xff0c;如何通过计算分析挖掘生产数据价值&…

【Android定制】修改BUILD_AGO_GMS = no 和 BUILD_GMS=no属性

文章目录 概要名词解释细节小结 概要 在安卓底层源码中&#xff0c;有这样的两个属性&#xff0c;这两个第一眼看上去都像是带不带谷歌&#xff0c;BUILD_AGO_GMS no和BUILD_GMSno有什么区别&#xff1f;&#xff1f; 如果带了谷歌&#xff0c;那么这个设备就差不多是国外定…

SQL数据库的整体结构、索引、MVCC、锁、日志、查询优化,三大范式等

关系型数据库和非关系型数据库 SQL:关系型数据库指的是使用关系模型&#xff08;二维表格模型&#xff09;来组织数据的数据库。(mysql,sqlserver,sqllite,oracle) 关系数据库的优点&#xff1a; 容易理解&#xff0c;符合正常思维方式&#xff1b;都是用表格形式&#xff0c;格…

大数据:Hadoop HDFS,基础架构,去中心化,中心化模式,HDFS基础架构,虚拟机和云服务器部署HDFS

大数据&#xff1a;Hadoop HDFS&#xff0c;基础架构&#xff0c;去中心化&#xff0c;中心化模式&#xff0c;HDFS基础架构&#xff0c;虚拟机和云服务器部署HDFS 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;可能很多算…

美颜SDK的市场需求与技术策略:商业化落地的关键因素

随着人们对于美的追求不断加强&#xff0c;美颜技术也在不断进步&#xff0c;其中美颜SDK的出现极大地方便了开发者&#xff0c;使得美颜技术能够更加便捷地应用于各种应用中。那么&#xff0c;美颜SDK市场需求和技术策略是什么&#xff1f;商业化落地的关键因素又是什么呢&…