DDD分层架构实践、要了解DDD分层架构,首先先了解传统的三层架构(1)

文章介绍了在Java系统设计中,如何通过聚合根(如交易对象)管理业务操作,保持数据一致性,以及如何运用JPA进行对象关系映射。文章还探讨了领域建模、服务接口设计、事务控制和事件驱动模型,强调设计模式在专业交流和代码维护中的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

聚合根:聚合中可代表整个业务操作的实体对象,通过它提供对外访问操作,它维护聚合内部的数据一致性,它是聚合中对象的管理者

代码示例:

// 交易

public class TradeRecord extends BaseEntity {

/**

  • 交易号

*/

@Column(unique = true)

private String tradeNumber;

/**

  • 交易金额

*/

private BigDecimal tradeAmount;

/**

  • 交易类型

*/

@Enumerated(EnumType.STRING)

private TradeType tradeType;

/**

  • 交易余额

*/

private BigDecimal balance;

/**

  • 钱包

*/

@ManyToOne

private Wallet wallet;

/**

  • 交易状态

*/

@Enumerated(EnumType.STRING)

private TradeStatus tradeStatus;

@DomainEvents

public List domainEvents() {

return Collections.singletonList(new TradeEvent(this));

}

}

// 钱包

public class Wallet extends BaseEntity {

/**

  • 钱包ID

*/

@Id

private String walletId;

/**

  • 密码

*/

private String password;

/**

  • 状态

*/

@Enumerated(EnumType.STRING)

private WalletStatus walletStatus = WalletStatus.AVAILABLE;

/**

  • 用户Id

*/

private Integer userId;

/**

  • 余额

*/

private BigDecimal balance = BigDecimal.ZERO;

}

  • 钱包交易例子的系统设计中,钱包的任何操作如:充值、消息等都是通过交易对象驱动钱包余额的变化

  • 交易对象钱包对象均为实体对象且组成聚合关系,交易对象是钱包交易业务模型的聚合根,代表聚合向外提供调用服务

  • 经过分析交易对象钱包对象为1对多关系(@ManyToOne),这里采用了JPA做ORM架构,更多JPA实践请参考>>

  • 这里的领域建模使用的是贫血模型,结构简单,职责单一,相互隔离性好但缺乏面向对象设计思想,关于领域建模可参考《领域建模的贫血模型与充血模型》

  • domainEvents()为领域事件发布的一种实现,作用是交易对象任何的数据操作都将触发事件的发布,再配合事件订阅实现事件驱动设计模型,当然也可以有别的实现方式

2)领域服务

======

/**

  • Created on 2020/9/7 11:40 上午

  • @author barry

  • Description: 交易服务

*/

public interface TradeService {

/**

  • 充值

  • @param tradeRecord

  • @return

*/

TradeRecord recharge(TradeRecord tradeRecord);

/**

  • 消费

  • @param tradeRecord

  • @return

*/

TradeRecord consume(TradeRecord tradeRecord);

}

先定义服务接口,接口的定义需要遵循现实业务的操作,切勿以程序逻辑或数据库逻辑来设计定义出增删改查

  • 主要的思考方向是交易对象对外可提供哪些服务,这种服务的定义是粗粒度高内聚的,切勿将某些具体代码实现层面的方法定义出来

  • 接口的输入输出参数尽量考虑以对象的形式,充分兼容各种场景变化

  • 关于前端需要的复杂查询方法可不在此定义,一般情况下查询并非是一种领域服务且没有数据变化,可单独处理

  • 领域服务的实现主要关注逻辑实现,切勿包含技术基础类代码,比如缓存实现,数据库实现,远程调用等

3)基础设施接口

========

public interface TradeRepository {

/**

  • 保存

  • @param tradeRecord

  • @return

*/

TradeRecord save(TradeRecord tradeRecord);

/**

  • 查询订单

  • @param tradeNumber

  • @return

*/

TradeRecord findByTradeNumber(String tradeNumber);

/**

  • 发送MQ事件消息

  • @param tradeEvent

*/

void sendMQEvent(TradeEvent tradeEvent);

/**

  • 获取所有

  • @return

*/

List findAll();

}

  • 基础设施接口放在领域层主要的目的是减少领域层对基础设施层的依赖

  • 接口的设计是不可暴露实现的技术细节,如不能将拼装的SQL作为参数

4. 应用层实现(application)

======================

// 交易服务

@Component

public class TradeManager {

private final TradeService tradeService;

public TradeManager(TradeService tradeService) {

this.tradeService = tradeService;

}

// 充值

@Transactional(rollbackFor = Exception.class)

public TradeRecord recharge(TradeRecord tradeRecord) {

return tradeService.recharge(tradeRecord);

}

// 消费

@Transactional(rollbackFor = Exception.class)

public TradeRecord consume(TradeRecord tradeRecord) {

return tradeService.consume(tradeRecord);

}

}

// 交易事件订阅

@Component

public class TradeEventProcessor {

@Autowired

private TradeRepository tradeRepository;

@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT, condition = “# tradeEvent.tradeStatus.name() == ‘SUCCEED’”)

public void TradeSucceed(TradeEvent tradeEvent) {

tradeRepository.sendMQEvent(tradeEvent);

}

}

// 交易消息订阅

@Component

public class TradeMQReceiver {

@RabbitListener(queues = “ddd-trade-succeed”)

public void receiveTradeMessage(TradeEvent tradeEvent){

System.err.println(“MQ Receiver====”);

System.err.println(tradeEvent);

}

}

应用服务

  • 应用层是很薄的一层,主要用于调用和组合领域服务,切勿包含任何业务逻辑

  • 可包括少量的流程参数判断

  • 由于可能是多个领域服务组合操作调用,如果存在原子性要求可以增加**@Transactional**事务控制

事件订阅

  • 事件订阅是进程内多个领域操作协作解耦的一种实现方式,它也是进程内所有后续操作的接入口

  • 它与应用服务的组合操作用途不一样,组合是根据场景需求可增可减,但事件订阅后的操作是相对固化的,主要是满足逻辑的一致性要求

TransactionPhase.AFTER_COMMIT配置是在前一操作事务完成后再调用,从而减少后续操作对前操作的影响

  • 事件订阅可能会有多个消息主体,为了方便管理最好统一在一个类里处理

  • MQ消息发布一般放在事件订阅中

消息订阅

  • 消息订阅是多个微服务间协作解耦的一步实现方式

  • 消息体尽量以统一的对象包装进行传递,降低对象异构带来的处理难度

5. 基础设施层(infrastructure)

=========================

@Repository

public class TradeRepositoryImpl implements TradeRepository {

private final JpaTradeRepository jpaTradeRepository;

private final RabbitMQSender rabbitMQSender;

private final Redis redis;

public TradeRepositoryImpl(JpaTradeRepository jpaTradeRepository, RabbitMQSender rabbitMQSender, Redis redis) {

this.jpaTradeRepository = jpaTradeRepository;

this.rabbitMQSender = rabbitMQSender;

this.redis = redis;

}

@Override

public TradeRecord save(TradeRecord tradeRecord) {

return jpaTradeRepository.save(tradeRecord);

}

/**

  • 查询订单

*/

@Override

public TradeRecord findByTradeNumber(String tradeNumber) {

TradeRecord tradeRecord = redis.getTrade(tradeNumber);

if (tradeRecord == null){

tradeRecord = jpaTradeRepository.findFirstByTradeNumber(tradeNumber);

// 缓存

redis.cacheTrade(tradeRecord);

}

return tradeRecord;

}

/**

  • 发送事件消息

  • @param tradeEvent

*/

@Override

public void sendMQEvent(TradeEvent tradeEvent) {

// 发送消息

rabbitMQSender.sendMQTradeEvent(tradeEvent);

}

/**

  • 获取所有

*/

@Override

public List findAll() {

return jpaTradeRepository.findAll();

}

}

  • 基础设施层是数据的输出向,主要包含数据库、缓存、消息队列、远程访问等的技术实现

  • 基础设计层对外隐藏技术实现细节,提供粗粒度的数据输出服务

  • 数据库操作:领域层传递的是数据对象,在这里可以按数据表的实现方式进行拆分实现

6. 用户接口层(controller)

=====================

@RequestMapping(“/trade”)

@RestController

public class TradeController {

@Autowired

private TradeManager tradeManager;

@Autowired

private TradeRepository tradeRepository;

@PostMapping(path = “/recharge”)

public TradeDTO recharge(@RequestBody TradeDTO tradeDTO) {

return TradeDTO.toDto(tradeManager.recharge(tradeDTO.toEntity()));

}

@PostMapping(path = “/consume”)

public TradeDTO consume(@RequestBody TradeDTO tradeDTO) {

return TradeDTO.toDto(tradeManager.consume(tradeDTO.toEntity()));

}

@GetMapping(path = “/{tradeNumber}”)

public TradeDTO findByTradeNumber(@PathVariable(“tradeNumber”) String tradeNumber){

return TradeDTO.toDto(tradeRepository.findByTradeNumber(tradeNumber));

}

}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

言尽于此,完结

无论是一个初级的 coder,高级的程序员,还是顶级的系统架构师,应该都有深刻的领会到设计模式的重要性。

  • 第一,设计模式能让专业人之间交流方便,如下:

程序员A:这里我用了XXX设计模式

程序员B:那我大致了解你程序的设计思路了

  • 第二,易维护

项目经理:今天客户有这样一个需求…

程序员:明白了,这里我使用了XXX设计模式,所以改起来很快

  • 第三,设计模式是编程经验的总结

程序员A:B,你怎么想到要这样去构建你的代码

程序员B:在我学习了XXX设计模式之后,好像自然而然就感觉这样写能避免一些问题

  • 第四,学习设计模式并不是必须的

程序员A:B,你这段代码使用的是XXX设计模式对吗?

程序员B:不好意思,我没有学习过设计模式,但是我的经验告诉我是这样写的

image

从设计思想解读开源框架,一步一步到Spring、Spring5、SpringMVC、MyBatis等源码解读,我都已收集整理全套,篇幅有限,这块只是详细的解说了23种设计模式,整理的文件如下图一览无余!

image

搜集费时费力,能看到此处的都是真爱!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
;" />

言尽于此,完结

无论是一个初级的 coder,高级的程序员,还是顶级的系统架构师,应该都有深刻的领会到设计模式的重要性。

  • 第一,设计模式能让专业人之间交流方便,如下:

程序员A:这里我用了XXX设计模式

程序员B:那我大致了解你程序的设计思路了

  • 第二,易维护

项目经理:今天客户有这样一个需求…

程序员:明白了,这里我使用了XXX设计模式,所以改起来很快

  • 第三,设计模式是编程经验的总结

程序员A:B,你怎么想到要这样去构建你的代码

程序员B:在我学习了XXX设计模式之后,好像自然而然就感觉这样写能避免一些问题

  • 第四,学习设计模式并不是必须的

程序员A:B,你这段代码使用的是XXX设计模式对吗?

程序员B:不好意思,我没有学习过设计模式,但是我的经验告诉我是这样写的

[外链图片转存中…(img-6olVtgAm-1713076100760)]

从设计思想解读开源框架,一步一步到Spring、Spring5、SpringMVC、MyBatis等源码解读,我都已收集整理全套,篇幅有限,这块只是详细的解说了23种设计模式,整理的文件如下图一览无余!

[外链图片转存中…(img-iDKCpjKU-1713076100760)]

搜集费时费力,能看到此处的都是真爱!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值