1. 项目目录结构
src/main/java/com/company/project/
├── application/ # 应用层
│ ├── command/ # 命令
│ │ ├── CreateUserCommand.java
│ │ └── UpdateUserCommand.java
│ ├── handler/ # 命令处理器
│ │ ├── CreateUserHandler.java
│ │ └── UpdateUserHandler.java
│ ├── service/ # 应用服务
│ │ └── UserApplicationService.java
│ ├── dto/ # 数据传输对象
│ │ ├── CreateUserDto.java
│ │ └── UserDto.java
│ └── query/ # 查询服务
│ └── UserQueryService.java
├── domain/ # 领域层
│ ├── entity/ # 实体
│ │ ├── User.java
│ │ └── Order.java
│ ├── valueobject/ # 值对象
│ │ ├── Email.java
│ │ ├── Money.java
│ │ └── UserId.java
│ ├── aggregate/ # 聚合根
│ │ └── OrderAggregate.java
│ ├── repository/ # 仓储接口
│ │ ├── UserRepository.java
│ │ └── OrderRepository.java
│ ├── service/ # 领域服务
│ │ └── UserDomainService.java
│ ├── event/ # 领域事件
│ │ ├── DomainEvent.java
│ │ ├── UserCreatedEvent.java
│ │ └── OrderConfirmedEvent.java
│ └── exception/ # 领域异常
│ ├── DomainException.java
│ ├── InvalidEmailException.java
│ └── DuplicateEmailException.java
├── infrastructure/ # 基础设施层
│ ├── repository/ # 仓储实现
│ │ ├── UserRepositoryImpl.java
│ │ └── OrderRepositoryImpl.java
│ ├── persistence/ # 持久化
│ │ ├── entity/
│ │ │ ├── UserJpaEntity.java
│ │ │ └── OrderJpaEntity.java
│ │ └── mapper/
│ │ ├── UserMapper.java
│ │ └── OrderMapper.java
│ ├── config/ # 配置
│ │ └── DatabaseConfig.java
│ └── external/ # 外部服务
│ └── EmailService.java
└── interfaces/ # 接口层
├── rest/ # REST控制器
│ └── UserController.java
├── dto/ # 接口DTO
│ ├── CreateUserRequest.java
│ └── UserResponse.java
└── config/ # Web配置
└── WebConfig.java
2. 领域层实现
2.1 值对象 (Value Objects)
// domain/valueobject/Email.java
package com.company.project.domain.valueobject;
import com.company.project.domain.exception.InvalidEmailException;
import java.util.Objects;
import java.util.regex.Pattern;
public final class Email {
private static final Pattern EMAIL_PATTERN =
Pattern.compile("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$");
private final String value;
public Email(String value) {
if (value == null || value.trim().isEmpty()) {
throw new InvalidEmailException("Email cannot be null or empty");
}
if (!EMAIL_PATTERN.matcher(value).matches()) {
throw new InvalidEmailException("Invalid email format: " + value);
}
this.value = value.toLowerCase().trim();
}
public String getValue() {
return value;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Email email = (Email) obj;
return Objects.equals(value, email.value);
}
@Override
public int hashCode() {
return Objects.hash(value);
}
@Override
public String toString() {
return value;
}
}
// domain/valueobject/UserId.java
package com.company.project.domain.valueobject;
import java.util.Objects;
import java.util.UUID;
public final class UserId {
private final String value;
public UserId(String value) {
if (value == null || value.trim().isEmpty()) {
throw new IllegalArgumentException("UserId cannot be null or empty");
}
this.value = value;
}
public static UserId generate() {
return new UserId(UUID.randomUUID().toString());
}
public String getValue() {
return value;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
UserId userId = (UserId) obj;
return Objects.equals(value, userId.value);
}
@Override
public int hashCode() {
return Objects.hash(value);
}
}
// domain/valueobject/Money.java
package com.company.project.domain.valueobject;
import java.math.BigDecimal;
import java.util.Objects;
public final class Money {
private final BigDecimal amount;
private final String currency;
public Money(BigDecimal amount, String currency) {
if (amount == null) {
throw new IllegalArgumentException("Amount cannot be null");
}
if (amount.compareTo(BigDecimal.ZERO) < 0) {
throw new IllegalArgumentException("Amount cannot be negative");
}
if (currency == null || currency.trim().isEmpty()) {
throw new IllegalArgumentException("Currency cannot be null or empty");
}
this.amount = amount;
this.currency = currency.toUpperCase();
}
public Money(double amount, String currency) {
this(BigDecimal.valueOf(amount), currency);
}
public Money add(Money other) {
if (!this.currency.equals(other.currency)) {
throw new IllegalArgumentException("Cannot add different currencies");
}
return new Money(this.amount.add(other.amount), this.currency);
}
public Money multiply(int multiplier) {
return new Money(this.amount.multiply(BigDecimal.valueOf(multiplier)), this.currency);
}
public BigDecimal getAmount() {
return amount;
}
public String getCurrency() {
return currency;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Money money = (Money) obj;
return Objects.equals(amount, money.amount) && Objects.equals(currency, money.currency);
}
@Override
public int hashCode() {
return Objects.hash(amount, currency);
}
}
2.2 实体 (Entities)
// domain/entity/User.java
package com.company.project.domain.entity;
import com.company.project.domain.valueobject.Email;
import com.company.project.domain.valueobject.UserId;
import com.company.project.domain.event.UserCreatedEvent;
import com.company.project.domain.exception.InvalidUserNameException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
public class User {
private final UserId id;
private Email email;
private String name;
private final LocalDateTime createdAt;
private LocalDateTime updatedAt;
private boolean active;
private List<DomainEvent> domainEvents = new ArrayList<>();
// 构造函数 - 创建新用户
public User(Email email, String name) {
this.id = UserId.generate();
this.email = email;
this.name = validateAndSetName(name);
this.createdAt = LocalDateTime.now();
this.updatedAt = LocalDateTime.now();
this.active = true;
// 添加领域事件
this.addDomainEvent(new UserCreatedEvent(this.id, this.email));
}
// 重建构造函数 - 从存储重建
public User(UserId id, Email email, String name, LocalDateTime createdAt,
LocalDateTime updatedAt, boolean active) {
this.id = id;
this.email = email;
this.name = name;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
this.active = active;
}
public void changeName(String newName) {
String validatedName = validateAndSetName(newName);
if (!this.name.equals(validatedName)) {
this.name = validatedName;
this.updatedAt = LocalDateTime.now();
}
}
public void changeEmail(Email newEmail) {
if (!this.email.equals(newEmail)) {
this.email = newEmail;
this.updatedAt = LocalDateTime.now();
}
}
public void activate() {
if (!this.active) {
this.active = true;
this.updatedAt = LocalDateTime.now();
}
}
public void deactivate() {
if (this.active) {
this.active = false;
this.updatedAt = LocalDateTime.now();
}
}
private String validateAndSetName(String name) {
if (name == null || name.trim().isEmpty()) {
throw new InvalidUserNameException("Name cannot be null or empty");
}
if (name.trim().length() > 100) {
throw new InvalidUserNameException("Name cannot exceed 100 characters");
}
return name.trim();
}
private void addDomainEvent(DomainEvent event) {
this.domainEvents.add(event);
}
public List<DomainEvent> getDomainEvents() {
return new ArrayList<>(domainEvents);
}
public void clearDomainEvents() {
this.domainEvents.clear();
}
// Getters
public UserId getId() { return id; }
public Email getEmail() { return email; }
public String getName() { return name; }
public LocalDateTime getCreatedAt() { return createdAt; }
public LocalDateTime getUpdatedAt() { return updatedAt; }
public boolean isActive() { return active; }
}
2.3 聚合根 (Aggregate Root)
// domain/aggregate/OrderAggregate.java
package com.company.project.domain.aggregate;
import com.company.project.domain.entity.OrderItem;
import com.company.project.domain.valueobject.*;
import com.company.project.domain.event.OrderConfirmedEvent;
import com.company.project.domain.exception.OrderModificationException;
import com.company.project.domain.exception.EmptyOrderException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
public class OrderAggregate {
private final OrderId id;
private final UserId customerId;
private final List<OrderItem> items;
private OrderStatus status;
private final LocalDateTime createdAt;
private LocalDateTime updatedAt;
private List<DomainEvent> domainEvents = new ArrayList<>();
// 创建新订单
public OrderAggregate(UserId customerId) {
this.id = OrderId.generate();
this.customerId = customerId;
this.items = new ArrayList<>();
this.status = OrderStatus.PENDING;
this.createdAt = LocalDateTime.now();
this.updatedAt = LocalDateTime.now();
}
// 从存储重建
public OrderAggregate(OrderId id, UserId customerId, List<OrderItem> items,
OrderStatus status, LocalDateTime createdAt, LocalDateTime updatedAt) {
this.id = id;
this.customerId = customerId;
this.items = new ArrayList<>(items);
this.status = status;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
}
public void addItem(ProductId productId, String productName,
Money unitPrice, int quantity) {
if (status != OrderStatus.PENDING) {
throw new OrderModificationException("Cannot modify order in " + status + " status");
}
// 检查是否已存在该商品
OrderItem existingItem = findItemByProductId(productId);
if (existingItem != null) {
existingItem.changeQuantity(existingItem.getQuantity() + quantity);
} else {
OrderItem newItem = new OrderItem(productId, productName, unitPrice, quantity);
items.add(newItem);
}
this.updatedAt = LocalDateTime.now();
}
public void removeItem(ProductId productId) {
if (status != OrderStatus.PENDING) {
throw new OrderModificationException("Cannot modify order in " + status + " status");
}
items.removeIf(item -> item.getProductId().equals(productId));
this.updatedAt = LocalDateTime.now();
}
public void confirm() {
if (items.isEmpty()) {
throw new EmptyOrderException("Cannot confirm empty order");
}
if (status != OrderStatus.PENDING) {
throw new OrderModificationException("Order is already " + status);
}
this.status = OrderStatus.CONFIRMED;
this.updatedAt = LocalDateTime.now();
// 发布领域事件
this.addDomainEvent(new OrderConfirmedEvent(this.id, this.customerId, getTotalAmount()));
}
public void cancel() {
if (status == OrderStatus.SHIPPED || status == OrderStatus.DELIVERED) {
throw new OrderModificationException("Cannot cancel " + status + " order");
}
this.status = OrderStatus.CANCELLED;
this.updatedAt = LocalDateTime.now();
}
public Money getTotalAmount() {
if (items.isEmpty()) {
return new Money(0, "USD");
}
return items.stream()
.map(OrderItem::getSubtotal)
.reduce(Money::add)
.orElse(new Money(0, "USD"));
}
private OrderItem findItemByProductId(ProductId productId) {
return items.stream()
.filter(item -> item.getProductId().equals(productId))
.findFirst()
.orElse(null);
}
private void addDomainEvent(DomainEvent event) {
this.domainEvents.add(event);
}
// Getters
public OrderId getId() { return id; }
public UserId getCustomerId() { return customerId; }
public List<OrderItem> getItems() { return new ArrayList<>(items); }
public OrderStatus getStatus() { return status; }
public LocalDateTime getCreatedAt() { return createdAt; }
public LocalDateTime getUpdatedAt() { return updatedAt; }
public List<DomainEvent> getDomainEvents() { return new ArrayList<>(domainEvents); }
public void clearDomainEvents() { this.domainEvents.clear(); }
}
2.4 仓储接口 (Repository Interface)
// domain/repository/UserRepository.java
package com.company.project.domain.repository;
import com.company.project.domain.entity.User;
import com.company.project.domain.valueobject.Email;
import com.company.project.domain.valueobject.UserId;
import java.util.List;
import java.util.Optional;
public interface UserRepository {
Optional<User> findById(UserId id);
Optional<User> findByEmail(Email email);
List<User> findByNameContaining(String name);
void save(User user);
void delete(UserId id);
boolean existsByEmail(Email email);
}
2.5 领域服务 (Domain Service)
// domain/service/UserDomainService.java
package com.company.project.domain.service;
import com.company.project.domain.entity.User;
import com.company.project.domain.repository.UserRepository;
import com.company.project.domain.valueobject.Email;
import com.company.project.domain.exception.DuplicateEmailException;
public class UserDomainService {
private final UserRepository userRepository;
public UserDomainService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public void validateEmailUniqueness(Email email) {
if (userRepository.existsByEmail(email)) {
throw new DuplicateEmailException("Email " + email.getValue() + " is already in use");
}
}
public boolean canUserChangeEmail(User user, Email newEmail) {
if (user.getEmail().equals(newEmail)) {
return true; // 没有改变
}
return !userRepository.existsByEmail(newEmail);
}
}
2.6 领域事件 (Domain Events)
// domain/event/DomainEvent.java
package com.company.project.domain.event;
import java.time.LocalDateTime;
public abstract class DomainEvent {
private final LocalDateTime occurredOn;
protected DomainEvent() {
this.occurredOn = LocalDateTime.now();
}
public LocalDateTime getOccurredOn() {
return occurredOn;
}
}
// domain/event/UserCreatedEvent.java
package com.company.project.domain.event;
import com.company.project.domain.valueobject.Email;
import com.company.project.domain.valueobject.UserId;
public class UserCreatedEvent extends DomainEvent {
private final UserId userId;
private final Email email;
public UserCreatedEvent(UserId userId, Email email) {
super();
this.userId = userId;
this.email = email;
}
public UserId getUserId() { return userId; }
public Email getEmail() { return email; }
}
3. 应用层实现
3.1 命令 (Commands)
// application/command/CreateUserCommand.java
package com.company.project.application.command;
public class CreateUserCommand {
private final String email;
private final String name;
public CreateUserCommand(String email, String name) {
this.email = email;
this.name = name;
}
public String getEmail() { return email; }
public String getName() { return name; }
}
3.2 命令处理器 (Command Handlers)
// application/handler/CreateUserHandler.java
package com.company.project.application.handler;
import com.company.project.application.command.CreateUserCommand;
import com.company.project.domain.entity.User;
import com.company.project.domain.repository.UserRepository;
import com.company.project.domain.service.UserDomainService;
import com.company.project.domain.valueobject.Email;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@Component
public class CreateUserHandler {
private final UserRepository userRepository;
private final UserDomainService userDomainService;
public CreateUserHandler(UserRepository userRepository,
UserDomainService userDomainService) {
this.userRepository = userRepository;
this.userDomainService = userDomainService;
}
@Transactional
public void handle(CreateUserCommand command) {
Email email = new Email(command.getEmail());
// 使用领域服务验证业务规则
userDomainService.validateEmailUniqueness(email);
// 创建用户实体
User user = new User(email, command.getName());
// 保存用户
userRepository.save(user);
// 处理领域事件(通常由框架自动处理)
// eventPublisher.publishEvents(user.getDomainEvents());
// user.clearDomainEvents();
}
}
3.3 应用服务 (Application Services)
// application/service/UserApplicationService.java
package com.company.project.application.service;
import com.company.project.application.command.CreateUserCommand;
import com.company.project.application.dto.CreateUserDto;
import com.company.project.application.dto.UserDto;
import com.company.project.application.handler.CreateUserHandler;
import com.company.project.domain.entity.User;
import com.company.project.domain.repository.UserRepository;
import com.company.project.domain.valueobject.Email;
import com.company.project.domain.valueobject.UserId;
import org.springframework.stereotype.Service;
import java.util.Optional;
@Service
public class UserApplicationService {
private final CreateUserHandler createUserHandler;
private final UserRepository userRepository;
public UserApplicationService(CreateUserHandler createUserHandler,
UserRepository userRepository) {
this.createUserHandler = createUserHandler;
this.userRepository = userRepository;
}
public UserDto createUser(CreateUserDto dto) {
CreateUserCommand command = new CreateUserCommand(dto.getEmail(), dto.getName());
createUserHandler.handle(command);
// 返回创建的用户信息
Optional<User> user = userRepository.findByEmail(new Email(dto.getEmail()));
return user.map(this::mapToDto)
.orElseThrow(() -> new RuntimeException("User creation failed"));
}
public Optional<UserDto> getUserById(String id) {
Optional<User> user = userRepository.findById(new UserId(id));
return user.map(this::mapToDto);
}
private UserDto mapToDto(User user) {
return new UserDto(
user.getId().getValue(),
user.getEmail().getValue(),
user.getName(),
user.isActive(),
user.getCreatedAt()
);
}
}
3.4 数据传输对象 (DTOs)
// application/dto/CreateUserDto.java
package com.company.project.application.dto;
public class CreateUserDto {
private String email;
private String name;
public CreateUserDto() {}
public CreateUserDto(String email, String name) {
this.email = email;
this.name = name;
}
// Getters and Setters
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
// application/dto/UserDto.java
package com.company.project.application.dto;
import java.time.LocalDateTime;
public class UserDto {
private final String id;
private final String email;
private final String name;
private final boolean active;
private final LocalDateTime createdAt;
public UserDto(String id, String email, String name, boolean active, LocalDateTime createdAt) {
this.id = id;
this.email = email;
this.name = name;
this.active = active;
this.createdAt = createdAt;
}
// Getters
public String getId() { return id; }
public String getEmail() { return email; }
public String getName() { return name; }
public boolean isActive() { return active; }
public LocalDateTime getCreatedAt() { return createdAt; }
}
4. 基础设施层实现
4.1 JPA实体 (JPA Entities)
// infrastructure/persistence/entity/UserJpaEntity.java
package com.company.project.infrastructure.persistence.entity;
import javax.persistence.*;
import java.time.LocalDateTime;
@Entity
@Table(name = "users")
public class UserJpaEntity {
@Id
@Column(name = "id", length = 36)
private String id;
@Column(name = "email", unique = true, nullable = false)
private String email;
@Column(name = "name", nullable = false, length = 100)
private String name;
@Column(name = "active", nullable = false)
private boolean active;
@Column(name = "created_at", nullable = false)
private LocalDateTime createdAt;
@Column(name = "updated_at", nullable = false)
private LocalDateTime updatedAt;
protected UserJpaEntity() {}
public UserJpaEntity(String id, String email, String name, boolean active,
LocalDateTime createdAt, LocalDateTime updatedAt) {
this.id = id;
this.email = email;
this.name = name;
this.active = active;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
}
// Getters and Setters
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public boolean isActive() { return active; }
public void setActive(boolean active) { this.active = active; }
public LocalDateTime getCreatedAt() { return createdAt; }
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
public LocalDateTime getUpdatedAt() { return updatedAt; }
public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
}
4.2 仓储实现 (Repository Implementation)
// infrastructure/repository/UserRepositoryImpl.java
package com.company.project.infrastructure.repository;
import com.company.project.domain.entity.User;
import com.company.project.domain.repository.UserRepository;
import com.company.project.domain.valueobject.Email;
import com.company.project.domain.valueobject.UserId;
import com.company.project.infrastructure.persistence.entity.UserJpaEntity;
import com.company.project.infrastructure.persistence.mapper.UserMapper;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@Repository
public class UserRepositoryImpl implements UserRepository {
private final UserJpaRepository jpaRepository;
private final UserMapper mapper;
public UserRepositoryImpl(UserJpaRepository jpaRepository, UserMapper mapper) {
this.jpaRepository = jpaRepository;
this.mapper = mapper;
}
@Override
public Optional<User> findById(UserId id) {
return jpaRepository.findById(id.getValue())
.map(mapper::toDomain);
}
@Override
public Optional<User> findByEmail(Email email) {
return jpaRepository.findByEmail(email.getValue())
.map(mapper::toDomain);
}
@Override
public List<User> findByNameContaining(String name) {
return jpaRepository.findByNameContainingIgnoreCase(name)
.stream()
.map(mapper::toDomain)
.collect(Collectors.toList());
}
@Override
public void save(User user) {
UserJpaEntity entity = mapper.toJpaEntity(user);
jpaRepository.save(entity);
}
@Override
public void delete(UserId id) {
jpaRepository.deleteById(id.getValue());
}
@Override
public boolean existsByEmail(Email email) {
return jpaRepository.existsByEmail(email.getValue());
}
}
interface UserJpaRepository extends JpaRepository<UserJpaEntity, String> {
Optional<UserJpaEntity> findByEmail(String email);
boolean existsByEmail(String email);
List<UserJpaEntity> findByNameContainingIgnoreCase(String name);
}
4.3 映射器 (Mapper)
// infrastructure/persistence/mapper/UserMapper.java
package com.company.project.infrastructure.persistence.mapper;
import com.company.project.domain.entity.User;
import com.company.project.domain.valueobject.Email;
import com.company.project.domain.valueobject.UserId;
import com.company.project.infrastructure.persistence.entity.UserJpaEntity;
import org.springframework.stereotype.Component;
@Component
public class UserMapper {
public User toDomain(UserJpaEntity entity) {
return new User(
new UserId(entity.getId()),
new Email(entity.getEmail()),
entity.getName(),
entity.getCreatedAt(),
entity.getUpdatedAt(),
entity.isActive()
);
}
public UserJpaEntity toJpaEntity(User user) {
return new UserJpaEntity(
user.getId().getValue(),
user.getEmail().getValue(),
user.getName(),
user.isActive(),
user.getCreatedAt(),
user.getUpdatedAt()
);
}
}
5. 接口层实现
5.1 REST控制器 (REST Controller)
// interfaces/rest/UserController.java
package com.company.project.interfaces.rest;
import com.company.project.application.dto.CreateUserDto;
import com.company.project.application.dto.UserDto;
import com.company.project.application.service.UserApplicationService;
import com.company.project.domain.exception.DuplicateEmailException;
import com.company.project.domain.exception.InvalidEmailException;
import com.company.project.interfaces.dto.CreateUserRequest;
import com.company.project.interfaces.dto.UserResponse;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserApplicationService userApplicationService;
public UserController(UserApplicationService userApplicationService) {
this.userApplicationService = userApplicationService;
}
@PostMapping
public ResponseEntity<UserResponse> createUser(@Valid @RequestBody CreateUserRequest request) {
try {
CreateUserDto dto = new CreateUserDto(request.getEmail(), request.getName());
UserDto userDto = userApplicationService.createUser(dto);
UserResponse response = mapToResponse(userDto);
return ResponseEntity.status(HttpStatus.CREATED).body(response);
} catch (InvalidEmailException | DuplicateEmailException e) {
return ResponseEntity.badRequest().build();
}
}
@GetMapping("/{id}")
public ResponseEntity<UserResponse> getUser(@PathVariable String id) {
return userApplicationService.getUserById(id)
.map(this::mapToResponse)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
private UserResponse mapToResponse(UserDto dto) {
return new UserResponse(
dto.getId(),
dto.getEmail(),
dto.getName(),
dto.isActive(),
dto.getCreatedAt()
);
}
}
5.2 请求/响应DTO
// interfaces/dto/CreateUserRequest.java
package com.company.project.interfaces.dto;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
public class CreateUserRequest {
@NotBlank(message = "Email is required")
@Email(message = "Invalid email format")
private String email;
@NotBlank(message = "Name is required")
@Size(max = 100, message = "Name cannot exceed 100 characters")
private String name;
// Getters and Setters
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
6. 配置类
6.1 Spring配置
// infrastructure/config/DomainConfig.java
package com.company.project.infrastructure.config;
import com.company.project.domain.repository.UserRepository;
import com.company.project.domain.service.UserDomainService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class DomainConfig {
@Bean
public UserDomainService userDomainService(UserRepository userRepository) {
return new UserDomainService(userRepository);
}
}
6.2 maven依赖
<!-- pom.xml -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
7. 总结
这种Java DDD实现具有以下特点:
- 清晰的分层架构:严格按照DDD分层原则组织代码
- 丰富的类型系统:使用值对象确保数据完整性
- 领域逻辑封装:业务规则集中在领域层
- 依赖倒置:基础设施层实现领域层定义的接口
- 事件驱动:支持领域事件处理
- 可测试性:每层都可以独立进行单元测试