news 2026/4/28 9:59:46

别再写Service地狱了!用DDD重构我的项目(3)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
别再写Service地狱了!用DDD重构我的项目(3)

仓储,封装持久化数据

Repository(仓储)模式是一种设计模式,它用于将数据访问逻辑封装起来,使得领域层可以通过一个简单、一致的接口来访问聚合根或实体对象。这个模式的关键在于提供了一个抽象的接口,领域层通过这个接口与数据存储层进行交互,而不需要知道背后具体的实现细节。

特性

  1. 封装持久化操作:Repository负责封装所有与数据源交互的操作,如创建、读取、更新和删除(CRUD)操作。这样,领域层的代码就可以避免直接处理数据库或其他存储机制的复杂性。
  2. 领域对象的集合管理:Repository通常被视为领域对象的集合,提供了查询和过滤这些对象的方法,使得领域对象的获取和管理更加方便。
  3. 抽象接口:Repository定义了一个与持久化机制无关的接口,这使得领域层的代码可以在不同的持久化机制之间切换,而不需要修改业务逻辑。

用途

  1. 数据访问抽象:Repository为领域层提供了一个清晰的数据访问接口,使得领域对象可以专注于业务逻辑的实现,而不是数据访问的细节。
  2. 领域对象的查询和管理:Repository使得对领域对象的查询和管理变得更加方便和灵活,支持复杂的查询逻辑。
  3. 领域逻辑与数据存储分离:通过Repository模式,领域逻辑与数据存储逻辑分离,提高了领域模型的纯粹性和可测试性。
  4. 优化数据访问:Repository实现可以包含数据访问的优化策略,如缓存、批处理操作等,以提高应用程序的性能。

实现手段

在实践中,Repository模式通常通过以下方式实现:

  1. 定义Repository接口:在领域层定义一个或多个Repository接口,这些接口声明了所需的数据访问方法。
  2. 实现Repository接口:在基础设施层或数据访问层实现这些接口,具体实现可能是使用ORM(对象关系映射)框架,如MyBatis、Hibernate等,或者直接使用数据库访问API,如JDBC等。
  3. 依赖注入:在应用程序中使用依赖注入(DI)来将具体的Repository实现注入到需要它们的领域服务或应用服务中。这样做可以进一步解耦领域层和数据访问层,同时也便于单元测试。
  4. 使用规范模式(Specification Pattern):有时候,为了构建复杂的查询,可以结合使用规范模式,这是一种允许将业务规则封装为单独的业务逻辑单元的模式,这些单元可以被Repository用来构建查询。

案例

在这个例子中,我们将创建一个简单的用户管理系统,其中包含用户实体和用户仓储接口,以及一个基于内存的仓储实现。

首先,我们定义一个用户实体:

public class User { private Long id; private String username; private String email; // 构造函数、getter和setter省略 }

接下来,我们定义用户仓储的接口:

public interface UserRepository { User findById(Long id); List<User> findAll(); void save(User user); void delete(User user); }

然后,我们提供一个基于内存的仓储实现:

public class InMemoryUserRepository implements UserRepository { private Map<Long, User> database = new HashMap<>(); private AtomicLong idGenerator = new AtomicLong(); @Override public User findById(Long id) { return database.get(id); } @Override public List<User> findAll() { return new ArrayList<>(database.values()); } @Override public void save(User user) { if (user.getId() == null) { user.setId(idGenerator.incrementAndGet()); } database.put(user.getId(), user); } @Override public void delete(User user) { database.remove(user.getId()); } }

最后,我们可以在应用服务中使用这个仓储:

public class UserService { private final UserRepository userRepository; public UserService(UserRepository userRepository) { this.userRepository = userRepository; } public User getUserById(Long id) { return userRepository.findById(id); } public void createUser(String username, String email) { User newUser = new User(); newUser.setUsername(username); newUser.setEmail(email); userRepository.save(newUser); } // 其他业务逻辑方法... }

在实际应用中,我们通常会使用依赖注入框架(如Spring)来自动注入仓储的实现。这里为了简单起见,我们可以手动创建服务和仓储的实例:

public class Application { public static void main(String[] args) { UserRepository userRepository = new InMemoryUserRepository(); UserService userService = new UserService(userRepository); userService.createUser("XiaoFuGe", "xiaofuge@qq.com"); User user = userService.getUserById(1L); System.out.println("User found: " + user.getUsername()); } }

适配(端口),调用外部接口

在领域驱动设计(DDD)的上下文中,适配器(Adapter)模式扮演着至关重要的角色。适配器模式允许将不兼容的接口转换为另一个预期的接口,从而使原本由于接口不兼容而不能一起工作的类可以协同工作。在DDD中,适配器通常与端口(Port)概念结合使用,形成"端口和适配器"(Ports and Adapters)架构,也称为"六边形架构"(Hexagonal Architecture)。这种架构风格旨在将应用程序的核心逻辑与外部世界的交互解耦。

概念

Port 在这种架构中代表了应用程序的一个入口或出口点。它定义了一个与外部世界交互的接口,但不关心具体的实现细节。端口可以是驱动端口(Driving Ports,通常是输入端口)或被驱动端口(Driven Ports,通常是输出端口)。

特性

  1. 抽象性:端口提供了服务行为的抽象描述,明确了服务的功能和外部依赖。
  2. 独立性:端口独立于具体实现,允许服务实现的灵活替换或扩展。
  3. 灵活性:可以为同一端口提供不同的适配器实现,以适应不同的运行环境或需求。

用途

  1. 标准定义:端口和适配器定义了服务的标准行为和外部依赖,提高了代码的可读性和可维护性。
  2. 隔离变化:当外部系统变化时,只需更换或修改适配器,无需改动核心业务逻辑。
  3. 促进测试:可以使用模拟适配器来测试核心逻辑,而不依赖真实的外部系统。

实现

实现端口和适配器架构通常涉及以下步骤:

  1. 定义端口:在领域层定义清晰的接口,这些接口代表了应用程序与外部世界的交互点。
  2. 创建适配器:在基础层或应用层实现适配器,这些适配器负责将端口的抽象操作转换为具体的外部调用。
  3. 依赖倒置:应用程序的核心逻辑依赖于端口接口,而不是适配器的具体实现。这样,适配器可以随时被替换,而不影响核心逻辑。
  4. 配置和组装:在应用程序启动时,根据需要将适配器与相应的端口连接起来。

案例

在这个例子中,我们将创建一个简单的支付系统,其中包含一个支付端口和一个适配器,该适配器负责调用外部支付服务的接口。

首先,我们定义一个支付端口(Port),它是一个接口,描述了支付服务应该提供的操作:

public interface PaymentPort { boolean processPayment(double amount); }

接下来,我们创建一个适配器,它实现了支付端口,并负责调用外部支付服务的接口:

public class ExternalPaymentService { public boolean makePayment(double amount) { // 这里是外部支付服务的具体调用逻辑 System.out.println("Calling external payment service for amount: " + amount); // 假设支付总是成功 return true; } } public class PaymentAdapter implements PaymentPort { private ExternalPaymentService externalPaymentService; public PaymentAdapter(ExternalPaymentService externalPaymentService) { this.externalPaymentService = externalPaymentService; } @Override public boolean processPayment(double amount) { // 调用外部支付服务的接口 return externalPaymentService.makePayment(amount); } }

现在,我们可以在应用程序的核心逻辑中使用支付端口,而不依赖于适配器的具体实现。这样,如果将来需要更换外部支付服务,我们只需提供一个新的适配器实现即可:

public class PaymentService { private PaymentPort paymentPort; public PaymentService(PaymentPort paymentPort) { this.paymentPort = paymentPort; } public void processUserPayment(double amount) { if (paymentPort.processPayment(amount)) { System.out.println("Payment processed successfully."); } else { System.out.println("Payment failed."); } } }

最后,我们在应用程序的启动或配置阶段组装这些组件:

public class Application { public static void main(String[] args) { // 创建外部支付服务的实例 ExternalPaymentService externalPaymentService = new ExternalPaymentService(); // 创建适配器的实例,注入外部支付服务 PaymentAdapter paymentAdapter = new PaymentAdapter(externalPaymentService); // 创建支付服务的实例,注入适配器 PaymentService paymentService = new PaymentService(paymentAdapter); // 处理用户支付 paymentService.processUserPayment(100.0); } }

在这个例子中,PaymentAdapter 负责调用外部的支付接口 ExternalPaymentService.makePayment。PaymentService 使用 PaymentPort 接口与外部世界交互,这样就实现了领域逻辑与外部服务之间的解耦。如果需要更换支付服务提供商,我们只需要实现一个新的 PaymentAdapter,而不需要修改 PaymentService 的代码。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/28 9:57:30

Yew函数式编程:不可变数据和纯函数终极指南

Yew函数式编程&#xff1a;不可变数据和纯函数终极指南 【免费下载链接】yew Rust / Wasm framework for creating reliable and efficient web applications 项目地址: https://gitcode.com/gh_mirrors/ye/yew Yew是一个基于Rust和WebAssembly的现代Web框架&#xff0c…

作者头像 李华
网站建设 2026/4/28 9:57:29

忍者像素绘卷惊艳效果:硬边阴影+高饱和16色电影感绘卷实拍

忍者像素绘卷惊艳效果&#xff1a;硬边阴影高饱和16色电影感绘卷实拍 1. 视觉革命&#xff1a;当忍者美学遇上像素艺术 忍者像素绘卷是一款基于Z-Image-Turbo深度优化的图像生成工作站&#xff0c;它彻底颠覆了传统漫画创作方式。这款工具最令人惊叹的地方在于&#xff0c;它…

作者头像 李华
网站建设 2026/4/28 9:55:44

基于vue的IT学习视频网站[vue]-计算机毕业设计源码+LW文档

摘要&#xff1a;随着信息技术的飞速发展&#xff0c;IT学习需求日益增长&#xff0c;在线学习视频网站成为学习者获取知识的重要途径。本文介绍基于Vue框架开发的IT学习视频网站&#xff0c;阐述其设计思路、技术架构及功能实现。系统利用Vue的组件化、响应式等特性构建用户界…

作者头像 李华
网站建设 2026/4/28 9:54:55

FakeLocation终极指南:Android应用级位置模拟的完整解决方案

FakeLocation终极指南&#xff1a;Android应用级位置模拟的完整解决方案 【免费下载链接】FakeLocation Xposed module to mock locations per app. 项目地址: https://gitcode.com/gh_mirrors/fak/FakeLocation 在移动应用开发测试和隐私保护场景中&#xff0c;精准控制…

作者头像 李华
网站建设 2026/4/28 9:51:46

终极Android Jetpack指南:掌握现代化开发组件的完整教程

终极Android Jetpack指南&#xff1a;掌握现代化开发组件的完整教程 【免费下载链接】android_guides Extensive Open-Source Guides for Android Developers 项目地址: https://gitcode.com/gh_mirrors/an/android_guides Android Jetpack是Google推出的一套组件库&…

作者头像 李华