nvme通用驱动_对领域驱动设计的理解与实践

领域驱动设计(Domain-Driven-Design)是一种针对大型复杂系统的领域建模与分析方法论。

2003 年,Eric Evans 发布《Domain-Driven Design: Tackling Complexity in the Heart of Software》(领域驱动设计:软件核心复杂性应对之道),其中定义了DDD。

DDD改变了传统软件开发针对数据库进行的建模方法;DDD先对业务领域进行分析,建立领域模型,根据领域模型驱动代码设计。合理运用面向对象的高内聚低耦合设计要素,降低整个系统的业务复杂性,并使得系统具有更好的扩展性,更好的应对多变的业务需求。

领域 Domain

一个领域就是一个问题域,只要是同一个领域,那问题域就相同。

只要确定了系统所属的领域,那么这个系统的核心业务,即要解决的问题以及问题的边界就基本确定了。

举例

陌生人社交领域,包含有聊天,用户推荐,朋友圈等核心环节。 只要是这个领域,一般都会有这些相同的核心业务,因为他们要解决问题的本质是一样的,就是交友。

每一个领域,都有一个对应的领域模型,领域模型能够很好的帮我们解决复杂的业务问题。

驱动设计

在DDD中,以领域(domain)为边界,分析领域的核心问题,再设计对应的领域模型,最后通过领域模型驱动代码设计的实现。这样设计的系统才有合理的分层与解耦,对以后业务的迭代开发,代码的维护才更加容易。

然而很多互联网公司,为了追求快速的上线,都是模型都没有想清楚就开始写代码,这就导致了后续代码维护困难,无法扩展。修改bug同时又引入新的bug,反反复复,进入恶性循环。

当然,这跟梳理清楚领域模型需要一定时间,这与初创型的互联网公司需求快速上线有点相悖,但是,这点时间的投入是非常值得的。因为可以避免了系统上线后不久又得重构的问题。

概念总结

  • 领域就是问题域
  • 模型驱动的思想:通过建立领域模型来解决领域中的核心问题
  • 领域建模的目标:针对我们在领域中核心问题,而不是整个领域中的所有问题
  • 领域模型设计:设计时应考虑一定的抽象性、通用性,以及复用价值
  • 代码实现:通过领域模型驱动代码的实现,确保代码让领域模型落地,代码最终能解决问题

为什么需要DDD

系统复杂性

耦合

随着产品不断的迭代,业务逻辑变得越来越复杂,系统也越来越庞大。模块彼此互相关联、耦合。导致增加或修改一个功能变得异常艰难,同时功能间的界限也变得模糊,职责不再清晰。这个时候就需要进行重构,拆分。

虽然架构本身是随着业务进行不断演进的;但是,如果架构初始设计不体现出业务的模型,那么新需求就无法体现在现有架构上,导致不断腐化,不断重构。

内聚

贫血模型 Anemic Domain Object

domain object仅用作数据载体,而没有行为和动作的领域对象。

指领域对象里只有get和set方法,没有相关领域对象的业务逻辑。业务逻辑放在业务层。

充血模型 Rich Domain Object

将业务逻辑和对象存储放在domain object里面,业务层只是简单进行小部分业务的封装及其他domain的编排。

面向对象设计,符合单一职责设计。

贫血 vs 充血

贫血模型的domain object很轻量,这导致业务层的复杂,domain object相关的业务逻辑散布在各个业务层,造成业务逻辑的冗余以及原本domain object的定义就变得相对模糊,这就是贫血症引起的失忆症。

而采用领域开发的方式,将数据和行为封装在一起,与业务对象相映射;领域对象职责清晰,将相关业务聚合到领域对象内部。

微服务

DDD 的本质是一种软件设计方法论,而微服务架构是具体的实现方式。微服务架构并没有定义对复杂系统进行分解的具体方法论,而 DDD 正好就是解决方案。

微服务架构强调从业务维度来分治系统的复杂度,而DDD也是同样的着重业务视角。

DDD能带来什么

  • 建立通用语言: 围绕领域模型建立的一种语言,团队所有成员都使用这种语言进行沟通和活动
  • 驱动代码设计:领域建立模型,模型指导设计,设计产出代码
  • 解决核心问题:模型的设计中心就是核心域,就是解决核心的问题

DDD建模

战略设计

战略设计就是从宏观角度对领域进行建模。划分出业务的边界,组织架构,系统架构。

DDD中,对系统的划分是基于领域的,也是基于业务的。

通用语言Ubiquitous Language

通用语言是指确定统一的领域术语,提高开发人员与领域专家之间的沟通效率。

一旦确定了统一语言,无论是与领域专家的讨论,还是最终的实现代码,都可以通过使用相同的术语,清晰准确地定义领域知识。

当确认整个团队统一的语言后,就可以开始进行领域建模。

领域和子域

领域Domain

一个领域本质上可以理解为就是一个问题域。只要我们确定了系统所属的领域,那这个系统的核心业务,即要解决的关键问题、问题的范围边界就基本确定了。

举例

  • 社交领域:关键问题是用户推荐,聊天
  • 电商领域:关键问题是购物,订单,物流

子域Subdomain

如果一个领域过于复杂,涉及到的领域概念、业务规则、交互流程太多,导致没办法直接针对这个大的领域进行领域建模。这时就需要将领域进行拆分,本质上就是把大问题拆分为小问题,把一个大的领域划分为了多个小的领域(子域),那最关键的就是要理清每个子域的边界

子域可以根据自身重要性和功能属性划分为三类子域:

  • 核心域:公司核心产品和业务的领域
  • 支撑子域:不包含决定产品和公司核心竞争力的功能,也不包含通用功能的子域
  • 通用子域:被多个子域使用的通用功能子域

每个领域的划分都不一样。对相同领域公司而言,其核心,支撑,通用的子域也可能有不一样的地方,但大体上基本都是一样的。

举例

社交领域的划分

  • 核心域:用户推荐,聊天
  • 支撑子域:客服,反垃圾
  • 通用子域:消息推送

限界上下文Bounded Context

限界上下文

限界指划分边界,上下文对应一个聚合,限界上下文可以理解为业务的边界。

一个子域对应一个或多个限界上下文。如果对应多个上下文,则可以考虑子域是否要再进行细粒度的拆分。

限界上下文的目的是为了更加明确领域模型的职责和范围

划分限界上下文

三个原则:

  • 概念相同,含义不同(通用语言):如果一个模型在一个上下文里面有歧义,那有歧义的地方就是边界所在,应该把它们拆到不同的限界上下文中。
  • 外部系统:有时候系统需要同外部系统交互,这时可以把与外部系统交互的那部分拆分出去以实现更好的扩展性。这样一旦外部系统发生了变化,就不会影响到我们的核心业务逻辑。
  • 组织扩展:尽量不要两个团队一起在一个限界上下文里面开发,因为这样可能会存在沟通不顺畅、集成困难等问题。

组织架构

康威定律

任何组织在设计一套系统时,所交付的设计方案在结构上都与该组织的沟通结构保持一致。

团队结构就是组织结构,限界上下文就是系统的业务结构。所以,团队结构应该尽量和限界上下文保持一致。

举例

社交领域中,订单子域对应订单上下文

上下文映射

从宏观上看每个上下文之间的关系,可以更好理解各个上下文之间的依赖关系。

梳理清楚上下文之间的关系是为了:

  • 任务更好拆分,一个开发人员可以全身心的投入到相关的一个单独的上下文中
  • 沟通更加顺畅,一个上下文可以明确自己对其他上下文的依赖关系,从而使得团队内开发直接更好的对接
  • 每个团队在它的上下文中能够更加明确自己领域内的概念,因为上下文是领域的解系统

举例

聊天上下文依赖消息推送,推广上下文也依赖消息推送

战术建模

战术建模是从微观角度对上下文进行建模。

梳理清楚聚合根,实体,值对象,领域服务,领域事件,资源库等。

实体Entity

当一个对象可以由标识进行区分时,这种对象称为实体

和数据库中的实体是不同的,这里的实体是从业务角度进行划分的。

实体:

  • 具有唯一标识
  • 持久化
  • 可变

举例

社交中的用户即为实体,可以通过用户唯一的id进行区分。

值对象value object

当一个对象用于对事物进行描述而没有唯一标识时,它被称作值对象。

在实践中,需要保证值对象创建后就不能被修改,即不允许外部再修改其属性。

例如:年龄,聊天表情符号( :stuck_out_tongue:: 吐舌 (U+1F61B))

习惯了使用数据库的数据建模后,很容易将所有对象看作实体

聚合根Aggregate Root

聚合是一组相关对象的集合,作为一个整体被外界访问,聚合根是这个聚合的根节点。

聚合由根实体,值对象和实体组成。(聚合根里面有多少个实体,由领域建模决定)

外部对象需要访问聚合内的实体时,只能通过聚合根进行访问,而不能直接访问

举例

一个订单是一个聚合根,订单购买的商品是实体,收货地址是值对象。

领域服务Domain Service

领域服务

一些既不是实体,也不是值对象的范畴的领域行为或操作,可以放到领域服务中。用来处理业务逻辑,协调领域对象来完成相关业务。

例如,有些业务逻辑不适合放到领域对象中,或实体之间的业务协调,这些业务逻辑都可以放到领域服务中。

特征

  • 与领域相关的操作如执行一个业务操作过程,但它又并不适合放入实体与值对象中
  • 操作是无状态的
  • 对领域对象进行转换,或以多个领域对象作为输入进行计算,结果产生一个值对象

当采用微服务架构风格,一切领域逻辑的对外暴露均需要通过领域服务来进行。

如原本由聚合根暴露的业务逻辑也需要依托于领域服务。

举例

必须通过订单领域服务来创建和访问订单

领域事件

领域事件是对领域内发生的活动进行的建模。捕获一些有价值的领域活动事件。

作用

  • 解耦:可以通过发布订阅模式,发布领域事件
  • 一致性:通过领域事件来达到最终一致性
  • 事件溯源

举例

发送聊天消息,这属于一个领域事件;撤回消息,也属于一个领域事件。

推送服务订阅消息事件,然后将消息推送给用户端。这样就解耦了消息服务与推送服务之间的强依赖关系。

资源库Repository

资源库用于保存和获取聚合对象。

领域模型 vs 数据模型

资源库介于领域模型(业务模型)和数据模型(数据库)之间,主要用于聚合对象的持久化和检索。

资源库隔离了领域模型和数据模型,以便上层只需要关注于领域模型而不需要考虑如何进行持久化。

分层架构

把一系列相同的对象进行分类放在同一层,然后根据他们之间的依赖关系再确定上下层次关系。

在实际决策时,我们需要知道各层的职责、意义以及相应的场景;

落实到代码层面时,我们还需要知道各层所包含的具体内容、各层的一些常见的具体策略/模式、层次之间的交互/依赖关系。

DDD经典分层架构

fcb1f03336db7c7e3e82474ccdeb0ed5.png
  • 用户接口层(interfaces):处理显示和用户请求,以及一些基本的参数检查,不包括业务逻辑
  • 应用层(application):主要协调领域对象的操作;处理持久化事务、发送消息、安全认证等
  • 领域层(domain):处理核心业务逻辑,不包括技术实现细节。领域层是业务软件的核心
  • 基础设施层(infrastructure):处理纯技术细节,为其他层提供技术支撑,也可用于封装调用的外部系统细节。例如:持久化的实现,消息中间件的实现,工具类,rpc等

个人理解:这种分层,既可以在一个单体应用中,也可以是微服务的形式。DDD分层并不一定要按微服务的服务粒度进行分层。

如果一个业务逻辑非常简单的子域,则可以将几层都放进一个单体应用中,在应用中进行分层。如果业务较为复杂,则可以按服务进行拆分,每层都有自己对应的服务。

其他架构

  • 对称性架构
  • 洋葱架构
  • 整洁架构
  • CQRS架构

DDD工程实践

以一个简化的社交领域的例子来实践DDD。

核心概念

  • 用户(User): 一个账户,并以用户id识别
  • 关系(Relationship):用户之间的关系
  • 动态(Feed): 用户发布文字,图片,视频,评论等内容
  • 会话(Conversation):用户之间的聊天会话

领域设计

战略建模

领域就是社交领域,核心问题和绝大部分社交系统一样。

子域

  • 核心域:聊天,动态
  • 支撑子域:反作弊,推广
  • 通用子域:用户,关系,消息推送

上下文

  • 消息上下文
  • 会话上下文
  • 动态上下文
  • 推送上下文
  • 用户上下文

战术建模

以会话上下文为例子来进行战术建模

会话上下文

  • 会话:聚合根用户:实体用户:实体消息列表:实体发送人:实体接收人:实体消息内容:值对象

消息在会话上下文属于实体,在消息上下文属于聚合根。

结构

以会话子域为例

架构分层

  • interfaces 接口层RESTfulRPC
  • application 应用层ConversationMessage
  • domain_service 领域服务层modelConversationMessagerepositoryConversationMessage
  • infrastructure 基础设施层storeConversationRepositoryMessageRepositorymessageSendMessageutils

领域

package domain// 聚合根type Conversation struct {    ID int    User1 User    User2 User    Messages list.List}// 实体type Message struct {    ID int    From User       // 实体    To User         Body Content    // 值对象}

用户接口层

type ChatInferface struct {    // 应用层    app app.ChatApplication}func (c *ChatInferface) Route() {    c.route("POST", "/api/message", c.SendMessage)    c.route("PATCH", "/api/message", c.RecallMessage)}// POST /api/messagefunc (c *ChatInferface) SendMessage(ctx *Context) {    if !c.validateRequest(ctx) {        return    }    message := c.parseMessage(ctx)    app.SendMessage(message)}func (c *ChatInferface) RecallMessage(ctx *Context) {    if !c.validateRequest(ctx) {        return    }    messageID := c.parseMessage(ctx)    app.RecallMessage(messageID)}

应用层

type ChatApplication struct {    user service.UserService    chat service.ChatService    // 这里领域事件由应用层发布    // publisher EventPublisher    lbs LBSFacade}func (c *ChatApplication) SendMessage(msg *Message) {    if !c.user.CheckUser(msg.UserID) {        return    }    c.chat.SendMessage(msg)}

领域服务层

type ChatService struct {    // 领域事件    publisher MessageEventPublisher    repo MessageRepository}func (c *ChatService SendMessage(msg *Message) {    // 业务逻辑    ...        // 领域资源持久化    c.repo.Save(msg)        // 发布领域事件    c.publisher.Publish(msg)}

基础设施层

package infrastructuretype MessageRepository struct {    db MessageDatabase    cache MessageCache}func (m *MessageRepository) Save(msg *Message) {    db.Save(m.ToPO(msg))}func (m MessageRepository) Get(msgID int) *Message {    msg := m.cache.Get(msgID)    if msg != nil {        return m.FromPO(msg)    }    return m.FromPO(m.db.Get(msgID))}

总结

在设计和实现一个系统的时候,这个系统所要处理问题的领域专家和开发人员以一套统一语言进行协作,共同完成该领域模型的构建,在这个过程中,业务架构和系统架构等问题都得到了解决,之后将领域模型中关于系统架构的主体映射为实现代码,完成系统的实现落地。

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

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

相关文章

为什么不应该用Stream forEach替换for循环的3个原因

太棒了! 我们正在将代码库迁移到Java8。我们将用函数替换所有内容。 扔掉设计模式。 删除面向对象。 对! 我们走吧! 等一下 Java 8已经问世了一年多,而这种兴奋又回到了日常业务中。 baeldung.com从2015年5月开始执行的一项非代…

使用NPOI库导入导出EXCEL

一、EXCEL 导入&#xff08;Excel 导入导出实际项目中会被封装成**Helper 本示例只对简单功能做演示&#xff09; NPOI 包引用 视图view {ViewBag.Title "NPOIExcel"; }<h2>NPOIExcel</h2> <form action"Url.Action("NPOIInport", &…

oracle12c ora 12547,Oracle 12c DBCA出现PRCR-1079 ORA-12547 CRS-5017

Oracle 12c用dbca创建数据库时出现了PRCR-1079 ORA-12547 CRS-5017不能启动数据库。因为这里安装了Oracle Restart&#xff0c;所以尝试使用srvctl start database命令来手动启动数据库&#xff0c;但是结果还是一样不能启动。[gridoracle12c 12.1]$ srvctl start database -db…

kmeans鸢尾花分类python代码_python实现鸢尾花三种聚类算法(K-means,AGNES,DBScan)

一.分散性聚类(kmeans) 算法流程: 1.选择聚类的个数k. 2.任意产生k个聚类&#xff0c;然后确定聚类中心&#xff0c;或者直接生成k个中心。 3.对每个点确定其聚类中心点。 4.再计算其聚类新中心。 5.重复以上步骤直到满足收敛要求。&#xff08;通常就是确定的中心点不再改变。…

akka_Akka的字数统计MapReduce

akka在我与Akka的日常工作中&#xff0c;我最近写了一个字数映射减少示例。 本示例实现了Map Reduce模型&#xff0c;该模型非常适合横向扩展设计方法。 流 客户端系统&#xff08;FileReadActor&#xff09;读取文本文件&#xff0c;并将每一行文本作为消息发送给ClientActor…

Log4j 2配置与IntelliJ IDEA控制台颜色

Log4j是Java平台上最好的日志组件了&#xff0c;Log4j 2升级了不少API&#xff0c;拓展性更好。使用的话只需要直接引入就可以了. <dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version> &…

用c3p0连接oracle怎么分页,JSP分页(MySql+c3p0+dbutils)

JSP分页(MySqlc3p0dbutils)来源&#xff1a;互联网作者&#xff1a;佚名时间&#xff1a;2015-02-16 17:27为什么要对数据进行分页&#xff1f;当数据较多时&#xff0c;页面就会变的很庞大&#xff0c;不仅会影响到用户的使用&#xff0c;而且还有加重服务器的负担。下面简单的…

Apache Storm的实时情绪分析示例

实时情感分析是指处理自然语言文本&#xff08;或语音&#xff09;流以提取主观信息。 琐碎的用例用于构建推荐引擎或查找社交媒体趋势。 我选择了Apache Storm作为实时处理引擎。 Storm非常强大&#xff08;我们正在生产中使用它&#xff09;&#xff0c;并且非常容易在其之上…

flink sql设置并行度_《从0到1学习Flink》—— Flink parallelism 和 Slot 介绍

前言之所以写这个是因为前段时间自己的项目出现过这样的一个问题&#xff1a;Caused by: akka.pattern.AskTimeoutException: Ask timed out on [Actor[akka://flink/user/taskmanager_0#15608456]] after [10000 ms]. Sender[null] sent message of type "org.apache.f…

zabbix3.2监控

自动化运维框架 运维标准流程监控管理容量管理、关联关系、任务管理、自动部署、分布式集群、传统集群、机器管理安全控制灾难管理 自动化监控 监控评估数据采集主动式数据采集: client、公共插件、自定义脚本被动式服务状态: 服务状态、程序状态、用户访问质量第三方信息 公…

fasttext 文本分类_一文综述经典的深度文本分类方法

作者 | 何从庆转载自AI算法之心(ID:AIHeartForYou)笔者整理最近几年比较经典的深度文本分类方法&#xff0c;希望帮助小伙伴们了解深度学习在文本分类中的应用。Convolutional Neural Networks for Sentence Classification (EMNLP 2014)Kim在EMNLP2014提出的TextCNN方法&…

vi/vim 编辑器详解

vi/vim &#xff1a; 强大的编辑器 进入vi的命令 vi filename :打开或新建文件&#xff0c;并将光标置于第一行首 vi n filename &#xff1a;打开文件&#xff0c;并将光标置于第n行首 vi filename &#xff1a;打开文件&#xff0c;并将光标置于最后一行首 vi /pattern …

实验三+067+冯艳芳

一、实验目的 掌握黑盒测试用例设计方法 二、实验要求 &#xff08;1&#xff09;对被测程序进行黑盒测试用例设计 &#xff08;2&#xff09;运用等价类、边界值、决策表、状态图法等进行测试用例设计。 &#xff08;3&#xff09;对手机上任意一款音乐软件进行黑盒测试实践。…

python越来越慢_为什么Python中的串联速度越来越慢?

为什么在某些情况下,Python 3中的连接似乎比Python 2中的连接慢&#xff1f; 影响最大的串联方法似乎是字节对象的连续串联,从O(n)到O(n?)操作. 我的分析代码大部分在这里&#xff1a; #!/usr/bin/env python from operator import concat from sys import version, version_i…

jvm gc策略_IBM JVM调整– gencon GC策略

jvm gc策略本文将向您详细介绍从Java虚拟机&#xff08;例如HotSpot或JRockit&#xff09;迁移到IBM JVM时重要的Java堆空间调整注意事项。 此调整建议基于我为我的一个IT客户端执行的最新故障排除和调整任务。 IBM JVM概述 正如您可能从其他文章中看到的那样&#xff0c;IBM …

芬兰高性能图表控件-免费试用并提供技术支持

图表控件对于很多技术研发人员、工程设计师来说肯定不陌生&#xff0c;但市面上已有的图表控件产品大多功能单一、性能也不稳定&#xff0c;很难满足不同人群在不同场合的使用需求。为此&#xff0c;专注于开发高性能和最先进的数据可视化工具公司Arction则给出了完美的解决方案…

linux open函数_Linux驱动开发 / 字符设备驱动内幕 (1)

哈喽&#xff0c;我是老吴&#xff0c;继续记录我的学习心得。一、保持专注的几个技巧将最重要的事放在早上做。待在无干扰环境下&#xff0c;比如图书馆。意识到刚坐下开始投入工作前&#xff0c;有点负面小情绪是特别正常的现象。让“开心一刻”成为计划的一部分。拥有合情合…

github删除文件_github 仓库中删除历史大文件

问题如果git中提交了大文件&#xff0c;而且保存到了版本库中&#xff0c;那在下载或者克隆git包的时候&#xff0c;速度会非常慢。再加上github在国内访问本来就很慢&#xff0c;可能会导致包无法下载(克隆)。为了提升下载(克隆)速度&#xff0c;可以永久的删除这些文件(包括该…

linux plc编程软件,基于Linux平台的可编程控制器软PLC设计

实例下面以一个简单的对3并口通道循环控制为例&#xff0c;说明软PLC 的工作流程。(1)梯形图编程。从软PLC 主界面进入后&#xff0c;启动梯形图编程&#xff0c;调用梯形图编程的主程序。梯形图编程共需要调用梯形图界面模块、关闭模块、IO 模块&#xff0c;这些均在配置文件中…

insert into语句_入门MySQL——DML语句篇

前言&#xff1a;在上篇文章中&#xff0c;主要为大家介绍的是DDL语句的用法&#xff0c;可能细心的同学已经发现了。本篇文章将主要聚焦于DML语句&#xff0c;为大家讲解表数据相关操作。这里说明下DDL与DML语句的分类&#xff0c;可能有的同学还不太清楚。DDL(Data Definitio…