当前位置: 首页 > news >正文

六边形架构实现:领域驱动设计 + 端口适配器模式

下面是一个更详细的六边形架构实现方案,包含清晰的架构图和完整代码实现。这个方案严格遵循领域驱动设计(DDD)和六边形架构原则,实现环境无关的数据库服务接口。

架构图

graph TD
subgraph 外部适配器
A[API 控制器] -->|使用| B[应用服务]
C[数据库适配器] -->|实现| D[领域端口]
E[消息队列适配器] -->|实现| D
end

subgraph 应用层
B -->|依赖| F[领域服务]
end

subgraph 领域层
F -->|依赖| D[DatabasePort]
G[实体] --> D
H[值对象] --> G
end

subgraph 基础设施
I[环境配置] --> C
J[外部服务] --> E
end

style D fill:#f9f,stroke:#333
style F fill:#9f9,stroke:#333
style G fill:#ff9,stroke:#333
style C fill:#f99,stroke:#333

完整代码实现

项目结构

src/
├── main/
│   ├── java/
│   │   ├── com/
│   │   │   └── example/
│   │   │       ├── domain/            # 领域层
│   │   │       │   ├── model/         # 领域模型
│   │   │       │   │   ├── User.java
│   │   │       │   │    └── UserId.java
│   │   │       │   ├── service/       # 领域服务
│   │   │       │   │    └── UserService.java
│   │   │       │   └── port/          # 领域端口
│   │   │       │        └── DatabasePort.java
│   │   │       ├── application/       # 应用层
│   │   │       │   └── UserAppService.java
│   │   │       ├── adapter/           # 适配器层
│   │   │       │   ├── in/            # 输入适配器
│   │   │       │   │    └── web/       # Web控制器
│   │   │       │   │        └── UserController.java
│   │   │       │   └── out/           # 输出适配器
│   │   │       │       ├── persistence/ # 持久化适配器
│   │   │       │       │   ├── jdbc/  # JDBC实现
│   │   │       │       │   │    └── JdbcUserRepository.java
│   │   │       │       │   ├── mongo/ # MongoDB实现
│   │   │       │       │   │    └── MongoUserRepository.java
│   │   │       │       │    └── env/   # 环境选择适配器
│   │   │       │       │       └── EnvironmentAwareRepository.java
│   │   │       │       └── messaging/ # 消息适配器
│   │   │       │            └── KafkaEventPublisher.java
│   │   │       ├── config/            # 配置
│   │   │       │   └── AppConfig.java
│   │   │        └── MainApplication.java # 启动类
│   └── resources/
│        └── application.properties      # 配置文件
└── test/                              # 测试目录

代码实现

1. 领域层 - 模型与端口
// src/main/java/com/example/domain/model/UserId.java
package com.example.domain.model;import java.util.Objects;
import java.util.UUID;public final class UserId {private final String value;public UserId(String value) {if (value == null || value.isBlank()) {throw new IllegalArgumentException("User ID cannot be null or empty");}this.value = value;}public static UserId generate() {return new UserId(UUID.randomUUID().toString());}public String getValue() {return value;}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;UserId userId = (UserId) o;return Objects.equals(value, userId.value);}@Overridepublic int hashCode() {return Objects.hash(value);}
}// src/main/java/com/example/domain/model/User.java
package com.example.domain.model;import java.time.LocalDateTime;public class User {private final UserId id;private String username;private String email;private final LocalDateTime createdAt;public User(UserId id, String username, String email) {this.id = id;this.username = username;this.email = email;this.createdAt = LocalDateTime.now();}// Getter方法public UserId getId() { return id; }public String getUsername() { return username; }public String getEmail() { return email; }public LocalDateTime getCreatedAt() { return createdAt; }// 领域行为public void updateEmail(String newEmail) {if (!isValidEmail(newEmail)) {throw new IllegalArgumentException("Invalid email format");}this.email = newEmail;}private boolean isValidEmail(String email) {return email != null && email.contains("@");}
}// src/main/java/com/example/domain/port/DatabasePort.java
package com.example.domain.port;import com.example.domain.model.User;
import com.example.domain.model.UserId;import java.util.Optional;public interface DatabasePort {Optional<User> findById(UserId userId);User save(User user);void delete(UserId userId);
}
2. 领域服务
// src/main/java/com/example/domain/service/UserService.java
package com.example.domain.service;import com.example.domain.model.User;
import com.example.domain.model.UserId;
import com.example.domain.port.DatabasePort;
import com.example.domain.port.EventPublisher;public class UserService {private final DatabasePort databasePort;private final EventPublisher eventPublisher;public UserService(DatabasePort databasePort, EventPublisher eventPublisher) {this.databasePort = databasePort;this.eventPublisher = eventPublisher;}public User createUser(String username, String email) {UserId userId = UserId.generate();User user = new User(userId, username, email);User savedUser = databasePort.save(user);eventPublisher.publish("USER_CREATED", savedUser);return savedUser;}public User updateUserEmail(UserId userId, String newEmail) {return databasePort.findById(userId).map(user -> {user.updateEmail(newEmail);User updatedUser = databasePort.save(user);eventPublisher.publish("USER_UPDATED", updatedUser);return updatedUser;}).orElseThrow(() -> new RuntimeException("User not found"));}
}
3. 应用层服务
// src/main/java/com/example/application/UserAppService.java
package com.example.application;import com.example.domain.model.User;
import com.example.domain.model.UserId;
import com.example.domain.service.UserService;public class UserAppService {private final UserService userService;public UserAppService(UserService userService) {this.userService = userService;}public User createUser(String username, String email) {return userService.createUser(username, email);}public User updateUserEmail(String userId, String newEmail) {return userService.updateUserEmail(new UserId(userId), newEmail);}public User getUser(String userId) {return userService.getUser(new UserId(userId)).orElseThrow(() -> new RuntimeException("User not found"));}
}
4. 适配器层 - 输入适配器 (Web控制器)
// src/main/java/com/example/adapter/in/web/UserController.java
package com.example.adapter.in.web;import com.example.application.UserAppService;
import com.example.domain.model.User;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/users")
public class UserController {private final UserAppService userAppService;public UserController(UserAppService userAppService) {this.userAppService = userAppService;}@PostMappingpublic User createUser(@RequestBody CreateUserRequest request) {return userAppService.createUser(request.username(), request.email());}@PatchMapping("/{userId}/email")public User updateEmail(@PathVariable String userId, @RequestBody String newEmail) {return userAppService.updateUserEmail(userId, newEmail);}@GetMapping("/{userId}")public User getUser(@PathVariable String userId) {return userAppService.getUser(userId);}record CreateUserRequest(String username, String email) {}
}
5. 适配器层 - 输出适配器 (持久化实现)
// src/main/java/com/example/adapter/out/persistence/jdbc/JdbcUserRepository.java
package com.example.adapter.out.persistence.jdbc;import com.example.domain.model.User;
import com.example.domain.model.UserId;
import com.example.domain.port.DatabasePort;
import org.springframework.jdbc.core.JdbcTemplate;import java.util.Optional;public class JdbcUserRepository implements DatabasePort {private final JdbcTemplate jdbcTemplate;private final String environment;public JdbcUserRepository(JdbcTemplate jdbcTemplate, String environment) {this.jdbcTemplate = jdbcTemplate;this.environment = environment;}@Overridepublic Optional<User> findById(UserId userId) {System.out.println("Querying JDBC database in " + environment + " environment");String sql = "SELECT * FROM users WHERE id = ?";return jdbcTemplate.query(sql, (rs, rowNum) -> new User(new UserId(rs.getString("id")),rs.getString("username"),rs.getString("email")), userId.getValue()).stream().findFirst();}@Overridepublic User save(User user) {System.out.println("Saving to JDBC database in " + environment + " environment");String sql = "INSERT INTO users (id, username, email) VALUES (?, ?, ?) " +"ON DUPLICATE KEY UPDATE username = ?, email = ?";jdbcTemplate.update(sql, user.getId().getValue(), user.getUsername(), user.getEmail(),user.getUsername(), user.getEmail());return user;}@Overridepublic void delete(UserId userId) {System.out.println("Deleting from JDBC database in " + environment + " environment");jdbcTemplate.update("DELETE FROM users WHERE id = ?", userId.getValue());}
}// src/main/java/com/example/adapter/out/persistence/mongo/MongoUserRepository.java
package com.example.adapter.out.persistence.mongo;import com.example.domain.model.User;
import com.example.domain.model.UserId;
import com.example.domain.port.DatabasePort;
import org.springframework.data.mongodb.core.MongoTemplate;import java.util.Optional;public class MongoUserRepository implements DatabasePort {private final MongoTemplate mongoTemplate;private final String environment;public MongoUserRepository(MongoTemplate mongoTemplate, String environment) {this.mongoTemplate = mongoTemplate;this.environment = environment;}@Overridepublic Optional<User> findById(UserId userId) {System.out.println("Querying MongoDB in " + environment + " environment");return Optional.ofNullable(mongoTemplate.findById(userId.getValue(), User.class));}@Overridepublic User save(User user) {System.out.println("Saving to MongoDB in " + environment + " environment");return mongoTemplate.save(user);}@Overridepublic void delete(UserId userId) {System.out.println("Deleting from MongoDB in " + environment + " environment");mongoTemplate.remove(findById(userId).orElseThrow());}
}
6. 环境感知适配器
// src/main/java/com/example/adapter/out/persistence/env/EnvironmentAwareRepository.java
package com.example.adapter.out.persistence.env;import com.example.domain.model.User;
import com.example.domain.model.UserId;
import com.example.domain.port.DatabasePort;
import java.util.Optional;public class EnvironmentAwareRepository implements DatabasePort {private final DatabasePort devRepository;private final DatabasePort testRepository;private final String activeEnvironment;public EnvironmentAwareRepository(DatabasePort devDataSource, DatabasePort testDataSource,String activeEnvironment) {this.devRepository = devDataSource;this.testRepository = testDataSource;this.activeEnvironment = activeEnvironment;}@Overridepublic Optional<User> findById(UserId userId) {return getActiveRepository().findById(userId);}@Overridepublic User save(User user) {return getActiveRepository().save(user);}@Overridepublic void delete(UserId userId) {getActiveRepository().delete(userId);}private DatabasePort getActiveRepository() {switch(activeEnvironment.toLowerCase()) {case "test":return testRepository;case "dev":default:return devRepository;}}
}
7. 配置类
// src/main/java/com/example/config/AppConfig.java
package com.example.config;import com.example.adapter.out.persistence.env.EnvironmentAwareRepository;
import com.example.adapter.out.persistence.jdbc.JdbcUserRepository;
import com.example.adapter.out.persistence.mongo.MongoUserRepository;
import com.example.domain.port.DatabasePort;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.data.mongodb.core.MongoTemplate;@Configuration
public class AppConfig {@Value("${app.environment:dev}")private String environment;@Value("${app.database.type:jdbc}")private String databaseType;private static final String JDBC = "jdbc";private static final String MONGO = "mongo";@Beanpublic DatabasePort databasePort(JdbcTemplate jdbcTemplate, MongoTemplate mongoTemplate) {// 根据不同环境创建不同的数据库适配器DatabasePort devAdapter = createDatabaseAdapter("dev", JDBC.equals(databaseType) ? jdbcTemplate : mongoTemplate);DatabasePort testAdapter = createDatabaseAdapter("test", JDBC.equals(databaseType) ? jdbcTemplate : mongoTemplate);// 返回环境感知的适配器return new EnvironmentAwareRepository(devAdapter, testAdapter, environment);}private DatabasePort createDatabaseAdapter(String env, Object template) {if (template instanceof JdbcTemplate) {return new JdbcUserRepository((JdbcTemplate) template, env);} else if (template instanceof MongoTemplate) {return new MongoUserRepository((MongoTemplate) template, env);}throw new IllegalArgumentException("Unsupported database template type");}
}

架构优势说明

  1. 领域核心隔离‌:

    • 领域模型(User, UserId)完全独立,不依赖任何框架或数据库
    • 领域服务(UserService)只依赖抽象端口(DatabasePort)
  2. 环境透明性‌:

    • 通过EnvironmentAwareRepository实现环境自动切换
    • 业务代码完全不知道底层使用的具体环境
  3. 可扩展性‌:

    • 支持多种数据库类型(JDBC/MongoDB)
    • 轻松添加新环境(如生产环境)或新数据库类型
  4. 可测试性‌:

    • 领域核心可独立测试,无需数据库连接
    • 可使用内存数据库或Mock对象进行测试
  5. 运行时配置‌:

    • 通过application.properties配置环境和数据库类型
    • 无需修改代码即可切换环境

配置示例 (application.properties)

# 可选值: dev, test
app.environment=dev# 可选值: jdbc, mongo
app.database.type=jdbc# 其他环境特定配置...

这种架构设计确保了业务代码与底层实现的完全解耦,调用方只需要通过统一的接口访问服务,不需要关心具体使用哪个环境或哪个数据库。当需要添加新环境或切换数据库时,只需扩展适配器层,核心业务逻辑完全不受影响。

http://www.dtcms.com/a/423470.html

相关文章:

  • 六安网站建设定制全国最大的源码平台
  • Qt Linux交叉编译字节数目不一样
  • 概率统计中的数学语言与术语1
  • 微服务项目->在线oj系统(Java-Spring)--增删改
  • 空间设计网站yahoo搜索引擎
  • 网站建设合同英文软件外包公司名单
  • Java基础(①Tomcat + Servlet + JSP)
  • 连云港百度推广总代理上海谷歌seo公司
  • ssl外贸网站网站空间托管
  • k8s kubelet 10250监控端口访问配置
  • 十二、伪分布式配置
  • VScode通过跳板机连接内网服务器
  • wordpress小说下载站建设银行网站如何下载u盾
  • 餐饮行业做微信网站有什么好处网站上图怎么用ps做
  • 设计的网站都有哪些功能辛集哪做网站
  • PostgreSQL的逻辑复制spill溢出案例和启停库逻辑
  • OpenMQTTGateway 技术全解:统一多协议到 MQTT 的开源网关
  • 数据结构——二叉树学习
  • 动规:回文串问题
  • PostgreSQL WAL 日志发展史 - pg7
  • 商丘企业网站建设团队网站设计的内容以及步骤
  • 网站域名所有人wordpress 子域名
  • ListenHub:AI播客平台,一句话生成播客
  • 知名的媒体发稿代理有哪些
  • PyTorch nn.Linear 终极详解:从零理解线性层的一切(含可视化+完整代码)
  • 大型企业级金融信贷平台需求报告
  • 【算法】小点:List.remove
  • 文件扩展名.js .jsx .ts .tsx区别(JavaScript扩展名、React扩展名、TypeScript扩展名)
  • MySQL 在金融系统中的应用:强一致性与高可用架构实战
  • 销售型网站营销目标查网址是否安全