基于DDD的编码实践

news2024/11/24 8:46:08

分层设计

领域驱动设计(Domain-driven design, DDD) 作为一种复杂软件系统的应对方案,在设计和编码提供了一种新的解决方式,即领域驱动,要求程序员在设计和编码时从领域专家的角度出发来实现架构/代码,做到代码即业务。同时利用各种方式拆解复杂模块,常用的方式有拆分子域、构建富血对象。

设计时,需要建立统一语言,确保领域中的业务概念处于同一个限界上下文,比如在一套电商系统中,用户买了一个东西,对应后台有一个订单,此时订单指代订单域的一项数据,当该订单需要发货时,在物流域中也会接受订单域输入并产生发货订单,此时,物流域的订单和订单域的订单就不处于一个限界上下文。建立统一语言有助于后续的产品和研发之间的高效沟通,打破代码和业务的语义鸿沟。领域模型的设计方法有 用例分析、事件风暴,领域模型需要提取出核心功能,并保证一定的扩展性,往往该过程是最重要也是最困难的。

进入编码阶段,构建聚合、聚合根、实体、值对象。虽然领域层与业务逻辑强关联,但是为了技术实现,在设计时也会有一些妥协,如,聚合不宜设计的过大,聚合的设计需要考虑实体之间的一致性要求,同时有一些事务、锁的使用在某些时候会侵入领域层(并非不能这样,实践中往往在实现时会借鉴DDD的思想,但不会全套照搬);除此之外,结合事件驱动的方式,可以让领域层代码保留一定的扩展性,实现上可以参考文章SpringEvent扩展性利器;领域层作为核心不应该依赖具体实现,借鉴六边形架构,领域层中定义了仓储协议(Repository接口),业务逻辑只需要从仓储接口中获取数据,至于实现领域层并不关系,而具体的实现由其他模块如infrastructure层来实现;同时,在实际处理输入时(http,rpc,job…)通常涉及与其他域的交互,DDD中通过构建防腐层来应对外部变化。

最终得到的代码分层结构如下图,Maven archetype代码参见:ddd-spring-web-maven-archetype:

在这里插入图片描述

编码tips

构建富血实体

经典的MVC架构基于贫血对象构建,贫血对象只作为data class,其业务含义丢失,通过构建富血对象将业务实体的
逻辑内聚,不在分散在各个service中,一是业务含义清晰,二是能够单点控制。

比如,判断ExpressAggregate物流聚合的发货状态,其含有字段如下:

@Data
public class ExpressAggregate {
    
    private ExpressNumber expressNumber;
    // 状态
    private ExpressStatus expressStatus;
    ...
}

基于贫血对象,判断该物流实体是否发货需要在service中调用ExpressAggregate做判断:

ExpressAggregate expressAggregate = ...;
if (Objects.equals(express.status,...)){
  // bisiness logic
  ...
}

而基于富血对象,我们可以将是否发货的逻辑内置与ExpressAggregate中:

@Data
public class ExpressAggregate {

    private ExpressNumber expressNumber;
    private ExpressStatus expressStatus;
    ...

    /**
     * 判断是否发货
     * @return
     */
    public boolean hasSent() {
        return Objects.equals(this.expressStatus, ExpressStatus.SENT)
                   || Objects.equals(this.expressStatus, ExpressStatus.RECEIVED)
                   || Objects.equals(this.expressStatus, ExpressStatus.RETURN);
    }
}

这样,调用方直接使用 expressAggregate.hasSent() 即可知道结果,避免了判断逻辑散落各处。

值对象不可变

使用值对象表示无唯一标识(id)含义的实体,其各项属性相等即视为同一个值对象,因此值对象不可变。在实现层面,
值对象不应有setter:

// 无setter
@Getter
@AllArgsConstructor(staticName = "of")
public class ExpressNumber {
    private String expressNumber;
}

// usage
ExpressNumber expressNum = ExpressNumber.of("abc123");

相比于直接使用String expressNumber , 在业务代码中使用ExpressNumber具有更强的业务含义,且作为方法入参时不易与其他String类型参数弄混。

层间对象转换

不同层的对象不应混用,层间调用应使用转换器转换,转换工作由谁来做?谁有转换需求谁来做。

CQRS

CQRS(Command Query Responsibility Segregation) 将输入分为 Command 和 Query,
Command作为变更系统状态的输入由领域层(聚合根)处理,而Query可不走领域层。

在这里插入图片描述

图片来源:Axon Framework : Architecture Overview

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

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

相关文章

随机产生一些江河上的坐标数据

不久前收到一个需求,说要随机创建约一百个某段江河上的坐标点,用于做一些数据呈现。 我首先是想到用AI直接给我一点数据,没想到给出来的坐标,有许多都落在陆地上,根本不符合我的要求。后来结合AI给出的建议&#xff0…

爬抖音直播间观众数据

打开抖音,稍微看了下买房直播间,突然很好奇是那些用户在观看,想拿下这些用户数据,再通过用户等级、在线观看时长排序,筛选出优质客户。 普及一下抖音用户等级: 抖音用户的提升与用户在直播间消费的金额直接…

打造基于大模型的AI产品

我要飞往印度进行短暂旅行,因此花了一个小时的时间处理在线签证申请流程。完成后,由于我现在知道涉及的内容,我向 ChatGPT 4o 询问了相关问题。这些观点中的大多数都是部分或完全错误的。 NSDT工具推荐: Three.js AI纹理开发包 - …

Linux C编译器从零开发三

AST语法树 BNF抽象 expr equality equality relational ("" relational | "!" relational)* relational add ("<" add | "<" add | ">" add | ">" add)* add mul ("" …

苹果电脑下载vite包错

苹果电脑下载vite包错/Users/lili/.npm/_cacache/index-v5/c5/50/b451703d03b3802b9ee6b7ff2b0bde4de7f26830eb52c904d6911c137cf8包错解决方式 解决方式&#xff1a;sudo chown -R 501:20 "/Users/wangxin/.npm"

2024/6/18(RBAC,查询用户权限,细粒度授权,选课,支付宝生成二维码支付,支付结果查询需要内网穿透)

黑马程序员【学成在线项目】,P141 测试沙箱支付宝_黑马学成在线支付宝沙箱-CSDN博客 需要内网穿透

甘特图如何画以及具体实例详解

甘特图如何画以及具体实例详解 甘特图是一种常见的项目管理工具又称为横道图、条状图(Bar chart)。是每一位项目经理和PMO必须掌握的项目管理工具。甘特图通过条状图来显示项目、进度和其他时间相关的系统进展的内在关系随着时间进展的情况。但是多项目经理和PMO虽然考了各种证…

OSPF开销、协议优先级、定时器(华为)

#交换设备 OSPF开销值 如果没有定义OSPF接口的开销值&#xff0c;OSPF会根据该接口的带宽自动计算其开销值。 计算公式&#xff1a; 接口开销 带宽参考值 / 接口带宽 &#xff08;取整数部分&#xff0c;结果小于1时取1&#xff09;通过改变带宽参考值可以间接改变接口的开…

多模态融合算法分析

多模态融合算法分析 多模态论文多模态融合早期融合晚期融合混合融合模型级融合 对比分析早期融合&#xff08;Feature-level Fusion&#xff09;晚期融合&#xff08;Decision-level Fusion&#xff09;混合融合&#xff08;Hybrid Fusion&#xff09;ML-LSTM&#xff08;Multi…

BC153 [NOIP2010]数字统计

数字统计 一.题目描述二.输入描述&#xff1a;三.输出描述&#xff1a;四.数字范围五.题目思路六.代码实现 一.题目描述 请统计某个给定范围[L, R]的所有整数中&#xff0c;数字2出现的次数。 比如给定范围[2, 22]&#xff0c;数字2在数2中出现了1次&#xff0c;在数12中出现1次…

[机器学习算法]线性回归

1. 理解基本概念 在开始学习线性回归之前&#xff0c;确保理解以下基本概念&#xff1a; 自变量&#xff08;特征&#xff09;&#xff1a;用来预测因变量&#xff08;目标&#xff09;的输入变量。 因变量&#xff08;目标&#xff09;&#xff1a;需要预测的输出变量。 回归…

网关助力边缘物联网

网关助力边缘物联网 在探讨网关如何助力边缘物联网&#xff08;IoT&#xff09;的议题时&#xff0c;我们不得不深入分析这一技术交汇点的复杂性与潜力。边缘计算与物联网的融合&#xff0c;通过将数据处理与分析能力推向网络边缘&#xff0c;即数据生成的地方&#xff0c;极大…

自我激励学习提升语言模型的推理能力

随着人工智能技术的快速发展&#xff0c;语言模型&#xff08;LMs&#xff09;在各种下游任务中展现出了卓越的能力。特别是在少样本&#xff08;few-shot&#xff09;和零样本&#xff08;zero-shot&#xff09;学习环境中&#xff0c;通过吸收特定任务的指令和示例&#xff0…

IDEA设置nacos权重

本地开发的时候&#xff0c;连接开发环境的NACOS&#xff0c;有时候会有其他请求发送到自己的机子上&#xff0c;由于本地代码会有更新不及时的情况&#xff0c;导致代码报错&#xff0c;同时也会影响本地的日志输出&#xff0c;此时只要在idea设置 spring.cloud.nacos.discov…

icloud 邮箱登入失败

APP NAME mail2HOSTING APP NAME cloudos2CLIENT TIME Tue Jun 11 2024 09:00:47 GMT0800 (中国标准时间) (1718067647802)USER AGENT Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36HOSTNAME www.icloud.…

掌握这三点软文营销技巧:轻松助力品牌传播

在营销方式层出不穷的今天&#xff0c;软文推广已不再只是简单的文字堆砌&#xff0c;而是成为了品牌与消费者深度沟通的桥梁。随着消费者获取信息的渠道越来越多元化&#xff0c;软文推广也迎来了新的趋势和挑战。今日投媒网将与您分享如何在新时代背景下&#xff0c;提升软文…

空气质量预报模式系统WRF-CMAQ

空气污染问题日益受到各级政府以及社会公众的高度重视&#xff0c;从实时的数据监测公布到空气质量数值预报及预报产品的发布&#xff0c;我国在空气质量监测和预报方面取得了一定进展。随着计算机技术的高速发展、空气污染监测手段的提高和人们对大气物理化学过程认识的深入&a…

已解决:geecg Column ‘id‘ in order clause is ambiguous

报错&#xff1a;Column id in order clause is ambiguous&#xff1b; MyBatis关联查询&#xff0c;相同字段名冲突&#xff0c;sql语句已经使用别名但仍然报错。 分析&#xff1a;写mapper映射文件时&#xff0c;在写到一对一关联&#xff0c;一对多关联时&#xff0c;由于两…

Maven 配置学习:存在两个本地私服如何配置

Maven 配置学习&#xff1a;存在两个本地私服如何配置 目录 Maven 配置学习&#xff1a;存在两个本地私服如何配置解释&#xff1a;1.本地仓库位置&#xff1a;2.Profiles 定义&#xff1a;3.Repositories 定义顺序&#xff1a;4.Active Profiles&#xff1a; 操作步骤&#xf…

掌握rpc、grpc并探究内在本质

文章目录 rpc是什么&#xff1f;又如何实现服务通信&#xff1f;理解rpcRPC的通信过程通信协议的选择小结RPC VS Restful net_rpc实践案例net/rpc包介绍创建服务端创建client 看看net_rpc的通信调度实现的内部原理明确目标基于自己实现的角度分析我会怎么做代码分析 grpc介绍与…