最近在做电商业务中,有关商品业务改版的一些东西,后端的架构设计采用现在很流行的微服务,有关微服务的简单概念:

微服务是一种架构风格,一个大型复杂软件应用由一个或多个微服务组成。系统中的各个微服务可被独立部署,各个微服务之间是松耦合的。每个微服务仅关注于完成一件任务并很好地完成该任务。在所有情况下,每个任务代表着一个小的业务能力。

关于改版的业务设计,还是想尝试 DDD 领域驱动设计,之前写的一些相关文章,都是直接进行战术设计,而非在战略设计基础上进行,所以最后可能会出现一些问题,所以这次的过程是:边了解业务、边了解 IDDD 书中关于战略设计的部分,然后尝试使用战略设计的方式进行业务分析,最后再细分出具体的战术设计,没有正确的设计方案,只有合适的设计方案,排除技术之外的业务分析过程,还是蛮有意思的。

DDD 战略建模(包含概念):领域(Domain)、核心域、子域、界限上下文(Bounded Context)、上下文映射图(Context Mapping)。

相关文章:

1. 业务流程

业务场景:发布商品

业务场景就上面四个字,看起来很简单,但其实具体分析起来,所包含的东西还是蛮多的,整个发布商品过程,就像一个商品诞生的生命周期一样,需要经历各个阶段和过程,直到商品正式发布出来,并且在这个过程中,有一系列的其他概念由商品衍生出来,比如库存、分类、品牌等等,还会有一些用户的行为参与,比如小二的后台审核等。

在业务分析过程中,我还是比较喜欢画一张简单的业务流程图,并不一定很规范,你也可以直接手绘出来,从业务流程图中,我们可以看到整个的业务方向,有利于我们从中找出关键的业务点,并进行具体分析设计。

发布商品业务流程图:

图比较简单,我们需要从添加商品到发布商品完成的过程中,抽离出关键的业务点,并且这些业务点事需要在业务系统中进行设计的,发布商品流程大概分为两个部分:

  1. 商户发布商品:这部分内容比较多,先选择分类(分类需要进行设计),然后填写基本信息(根据实际的业务,有很多不同的设计,是发布商品的核心,需要重点考虑),填写完成之后(两种选择:保存草稿和发布),在小二审核之前,需要填写入库单,用来更新商品库存,然后进入小二审核阶段,如果审核成功,并且商户选择商品上架,则代表着整个商品发布的业务流程跑完了。
  2. 小二审核商品:小二根据一些规定进行审核商户发布的商品,这个部分人工因素很大,但发布商品的规定一般是确定,因为是人工进行操作,所以这部分内容在业务系统设计方面体现不大。

其实,选择分类可以归纳到填写基本信息中,重要的是基本信息具体是什么?这部分包含的业务是什么?该如何设计呢?后来分析了下,除了一些商品的基本信息之外(比如标题、价格、商品详情、商品图片),还包含了品牌、分类、属性(一般指的是 sku)等,像标题和价格之类的属性一般是具体的值,后面我们在战术设计的时候,直接把它们设计成值对象即可,但对于品牌、分类之类的对象,需要进行单独进行设计,因为它们不是一个值所能代表的,需要进行独立维护。

除了商品信息之外,后面就是填写采购单用来更新商品库存了,这部分业务内容有点像外部服务一样,通过外部服务的一些操作,最后的结果导向商品模型,这部分类似的业务以后可能会很多,比如小二审核商品信息,也像一个外部服务一样,不过是人为进行操作的,审核最后的结果导向商品状态,我们可以归纳出,可以改变商品状态的一些行为,都是需要进行考虑的业务,并且这部分业务在后面建模的时候,需要重点设计。

画业务流程图的目的,在于熟悉整个业务的大致流程,以及对商品生命周期的了解,但只是大致的表述,当你对业务理解越深的时候,业务流程图也就会越复杂,但基本的框架是不变的,所以,在画的业务流程图的时候,要找出业务的不变规则,比如发布商品肯定要填写信息、然后小二审核等,变的业务都是在这些不变的规则之上丰富起来的,最后形成整个健全的业务系统。

2. 限界上下文

关于领域、核心域和子域的概念,相对比较容易理解,领域就是业务系统的全部,核心域就是业务系统最重要的部分,比如商品业务系统,核心域就是商品,其他相对不重要的业务部分就是子域,子域又分为支撑子域和通用子域,支撑子域用来支撑核心域,在整个领域中,可以被公用的子域,称为通用子域。

限界上下文是一个显式的边界,领域模型存在这个边界职位,领域模型把通用语言表达成软件模型,一般在设计的时候,会把领域和限界上下文一一对应(但也不是相对的),有时候限界上下文很大,但有时候限界上下文也很小,比如一段业务描述也可以称之为限界上下文,不管概念是怎么定义的,只需要知道限界上下文的核心是边界,边界的目的就是内聚合隔离。

一张简单的商品限界上下文图(虚线表示领域的边界):

首先,在商品领域中,商品是核心域,并对应一个商品上下文,库存被设计为一个通用子域,因为以后交易的业务场景会被用到,并对应一个库存上下文,品牌通用子域也一样,业务场景可能会对品牌的单独处理(比如品牌街,这是和商品不想关的),所以设计成通用子域会相对好些,分类支撑子域和属性子域相对复杂点,其实这里的分类和属性都是相对于商品而言的,你可以成为商品分类和商品属性,独立于商品之外,分类和属性是没有任何存在的业务意义的,所以,把它们设计为商品领域的支撑子域会比较好些。

另外,关于分类上下文和属性上下文之间的关系,从上面图中就可以看到,在业务场景中属性依附于分类,比如在发布商品页面,填写商品属性之前需要先确定商品分类,因为不同的分类有对应不同的属性,比如表带材质属性,只有手表分类下才会有,其实它们也可以直接合二为一,叫做分类属性支撑子域,对应分类属性上下文,上面说过添加属性之前,必须先确定分类,属性就像是分类中的一个子域,属性其实和商品没有直接的关系,它和商品的所有关系,必须都通过分类,并且属性的数据维护也是如此,这个后面会有调整,再详细说明。

分类在具体的实现中,会相对比较简单,有点像品牌的实现,顶多和商品有一些关联,但属性实现相对比较复杂些,因为属性项和属性值都是动态的,并且属性的展现形式也是动态的,比如一个属性项可能对应多个属性值,并且展现可能是组合形式的(文本+单选+下拉列表),这方便在也体现在数据存储的时候,不过可以按照一定的格式用 json 进行存储,展现方式也是一样。

限界上下文就像一个手术刀,将领域一点一点的进行解剖,解剖出来的部位独立进行实现,限界上下文的具体实现就是战术设计,并且各个限界上下文的实现之间是相互不影响。

3. 数据模型(聚合和实体)

限界上下文让我们明白,我们到底需要做什么东西,接下来就是针对这些东西的具体设计和实现了,怎么实现?就是战术设计。

战术建模(包含概念):聚合(Aggregate)、实体(Entity)、值对象(Value Objects)、资源库(Repository)、领域服务(Domain Services)、领域事件(Domain Events)、模块(Modules)。

关于战术设计的首要前提是聚合,后面实体和值对象等概念,都是在聚合的基础上衍生出来的,关于聚合的概念就不多说了,但需要说明下聚合设计的注意点:

  • 尽量小聚合设计:有助于减少事务的提交冲突,也有利于系统性能和可伸缩性,但不能过小,比如一个聚合只包含唯一标识和单个属性,这种设计不合理。
  • 根实体可以作为聚合根。
  • 聚合边界和真实的业务约束是一致的。
  • 通过唯一标识引用其他聚合。
  • 事务一致性:在一个事务中,只能修改一个聚合实例。
  • 在聚合之外使用最终一致性:如果可以不在意延迟,一般用领域事件进行实现。

先简单看下商品所包含的东西:

图中主要说明的是商品大致包含的内容:分类、属性和基本信息,属性又有具体的分类,但都基于分类确定的情况下。

一张简单的商品数据模型图:

简单归纳下:

  • 商品(聚合根):商品(根实体)、商品图片(实体)、商品 sku(实体)、商品描述(实体)、商品调整纪录(实体)
  • 库存(聚合根):库存(根实体)、入库详情(实体)
  • 品牌(聚合根):品牌(根实体)
  • 分类(聚合根):分类(根实体)、分类属性(实体)、分类属性值(实体)

实体中的属性只是一些示例,并不详细,值对象并没有在图中体现,因为实体的属性都可以被设计为值对象,这部分在具体实现的时候,再详细进行考虑,聚合根和实体、聚合根和聚合根之间的关系用箭头进行表示了。

关于分类和属性,在限界上下文设计的时候,被分开设计了,但后来想了一下,还是设计成一个比较好,分类作为聚合根,分类属性和分类属性值作为衍生出来的实体。

关于数据模型图,就不详细说明了,内容都在上面的图中,况且现在还不是很完善,后面可能还会进行调整。

大概就纪录这些。

DDD 领域驱动设计-商品建模之路的更多相关文章

  1. DDD领域驱动设计-案例建模设计-Ⅲ

    1. 背景 参考<DDD领域驱动设计-案例需求文档>,本文将构建实体,聚合根详述领域驱动中的建模设计.构建实体,聚合根的一些原则或方法,将在后续文章中说明. 2. 建模设计 2.1. 实体 ...

  2. 浅谈我对DDD领域驱动设计的理解

    从遇到问题开始 当人们要做一个软件系统时,一般总是因为遇到了什么问题,然后希望通过一个软件系统来解决. 比如,我是一家企业,然后我觉得我现在线下销售自己的产品还不够,我希望能够在线上也能销售自己的产品 ...

  3. (转载)浅谈我对DDD领域驱动设计的理解

    原文地址:http://www.cnblogs.com/netfocus/p/5548025.html 从遇到问题开始 当人们要做一个软件系统时,一般总是因为遇到了什么问题,然后希望通过一个软件系统来 ...

  4. DDD领域驱动设计的理解

    DDD领域驱动设计的理解 从遇到问题开始 当人们要做一个软件系统时,一般总是因为遇到了什么问题,然后希望通过一个软件系统来解决. 比如,我是一家企业,然后我觉得我现在线下销售自己的产品还不够,我希望能 ...

  5. 关于DDD领域驱动设计的理论知识收集汇总

    原文:关于DDD领域驱动设计的理论知识收集汇总 最近一直在学习领域驱动设计(DDD)的理论知识,从网上搜集了一些个人认为比较有价值的东西,贴出来和大家分享一下: 我一直觉得不要盲目相信权威,比如不能一 ...

  6. DDD领域驱动设计-概述-Ⅰ

     如果我看得更远,那是因为我站在巨人的肩膀上.(If I have seen further it is by standing on ye shoulder of Giants.)         ...

  7. DDD 领域驱动设计-三个问题思考实体和值对象

    消息场景:用户 A 发送一个消息给用户 B,用户 B 回复一个消息给用户 A... 现有设计:消息设计为实体并为聚合根,发件人.收件人设计为值对象. 三个问题: 实体最重要的特性是什么? Messag ...

  8. C#进阶系列——DDD领域驱动设计初探(一):聚合

    前言:又有差不多半个月没写点什么了,感觉这样很对不起自己似的.今天看到一篇博文里面写道:越是忙人越有时间写博客.呵呵,似乎有点道理,博主为了证明自己也是忙人,这不就来学习下DDD这么一个听上去高大上的 ...

  9. DDD领域驱动设计之聚合、实体、值对象

    关于具体需求,请看前面的博文:DDD领域驱动设计实践篇之如何提取模型,下面是具体的实体.聚合.值对象的代码,不想多说什么是实体.聚合等概念,相信理论的东西大家已经知晓了.本人对DDD表示好奇,没有在真 ...

随机推荐

  1. ifconfig: command not found(CentOS专版,其他的可以参考)

    ifconfig: command not found 查看path配置(echo相当于c中的printf,C#中的Console.WriteLine) echo $PATH 解决方案1:先看看是不是 ...

  2. redux-undo

    简介 通过包装reducer,创建一个state History,保留历史state,可以做退一步,进一步操作 1.install npm install --save redux-undo@beta ...

  3. Mediaplayer error (-19,0)

    Android MediaPlayer 发生 error (-19,0) 错误解决方法. 引起原因:由于多次实例化MediaPlayer.start() 进行播放操作引起的.由于没有及时释放内存资源导 ...

  4. ASP.NET Core的路由[4]:来认识一下实现路由的RouterMiddleware中间件

    虽然ASP.NET Core应用的路由是通过RouterMiddleware这个中间件来完成的,但是具体的路由解析功能都落在指定的Router对象上,不过我们依然有必要以代码实现的角度来介绍一下这个中 ...

  5. 将 instance 部署到 OVS Local Network - 每天5分钟玩转 OpenStack(130)

    上一节创建了 OVS 本地网络 first_local_net,今天我们会部署一个 instance 到该网络并分析网络结构.launch 一个 instance,选择 first_local_net ...

  6. SignalR SelfHost实时消息,集成到web中,实现服务器消息推送

    先前用过两次SignalR,但是中途有段时间没弄了,今天重新弄,发现已经忘得差不多了,做个笔记! 首先创建一个控制台项目Nuget添加引用联机搜索:Microsoft.AspNet.SignalR.S ...

  7. 关于Android避免按钮重复点击事件

    最近测试人员测试我们的APP的时候,喜欢快速点击某个按钮,出现一个页面出现多次,测试人员能不能禁止这样.我自己点击了几下,确实存在这个问题,也感觉用户体验不太好.于是乎后来我搜了下加一个方法放在我们U ...

  8. Spark踩坑记——数据库(Hbase+Mysql)

    [TOC] 前言 在使用Spark Streaming的过程中对于计算产生结果的进行持久化时,我们往往需要操作数据库,去统计或者改变一些值.最近一个实时消费者处理任务,在使用spark streami ...

  9. Android使用静默安装时碰见的问题

    升级时碰见的异常 private void installPackage(String appName,final File apk) { if (!apk.exists()) { setHasNew ...

  10. Android中Activity处理返回结果的实现方式

    大家在网上购物时都有这样一个体验,在确认订单选择收货人以及地址时,会跳转页面到我们存入网站内的所有收货信息(包含收货地址,收货人)的界面供我们选择,一旦我们点击其中某一条信息,则会自动跳转到订单提交界 ...