clean-architecture

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Clean Architecture, Hexagonal Architecture & DDD for Spring Boot

适用于Spring Boot的Clean Architecture、Hexagonal Architecture & DDD

Overview

概述

This skill provides comprehensive guidance for implementing Clean Architecture, Hexagonal Architecture (Ports & Adapters), and Domain-Driven Design tactical patterns in Java 21+ Spring Boot 3.5+ applications. It ensures clear separation of concerns, framework-independent domain logic, and highly testable codebases through proper layering and dependency management.
本技能为Java 21+ Spring Boot 3.5+应用提供Clean Architecture、Hexagonal Architecture(端口与适配器)和Domain-Driven Design(DDD)战术模式的全面实施指南。通过合理的分层和依赖管理,确保关注点清晰分离、领域逻辑独立于框架,以及代码库具备高度可测试性。

When to Use

适用场景

  • Architecting new Spring Boot applications with clear separation of concerns
  • Refactoring tightly coupled code into testable, layered architectures
  • Implementing domain logic independent of frameworks and infrastructure
  • Designing ports and adapters for swappable implementations
  • Applying Domain-Driven Design tactical patterns (entities, value objects, aggregates)
  • Creating testable business logic without Spring context dependencies
  • Trigger phrases: "implement clean architecture", "ports and adapters pattern", "separate domain from infrastructure", "hexagonal architecture Spring Boot", "DDD aggregate design"
  • 构建关注点清晰分离的全新Spring Boot应用
  • 将紧耦合代码重构为可测试的分层架构
  • 实现独立于框架和基础设施的领域逻辑
  • 设计支持可替换实现的端口与适配器
  • 应用Domain-Driven Design战术模式(实体、值对象、聚合)
  • 创建无需Spring上下文依赖的可测试业务逻辑
  • 触发短语:"implement clean architecture", "ports and adapters pattern", "separate domain from infrastructure", "hexagonal architecture Spring Boot", "DDD aggregate design"

Instructions

实施步骤

1. Understand the Core Concepts

1. 理解核心概念

Clean Architecture Layers (Dependency Rule)

Clean Architecture分层(依赖规则)

Dependencies flow inward. Inner layers know nothing about outer layers.
LayerResponsibilitySpring Boot Equivalent
DomainEntities, value objects, domain events, repository interfaces
domain/
- no Spring annotations
ApplicationUse cases, application services, DTOs, ports
application/
- @Service, @Transactional
InfrastructureFrameworks, database, external APIs
infrastructure/
- @Repository, @Entity
AdapterControllers, presenters, external gateways
adapter/
- @RestController
依赖关系向内流动。内层对外层一无所知。
层级职责Spring Boot对应实现
Domain实体、值对象、领域事件、仓库接口
domain/
- 无Spring注解
Application用例、应用服务、DTO、端口
application/
- @Service、@Transactional
Infrastructure框架、数据库、外部API
infrastructure/
- @Repository、@Entity
Adapter控制器、展示器、外部网关
adapter/
- @RestController

Hexagonal Architecture (Ports & Adapters)

Hexagonal Architecture(Ports & Adapters)

  • Domain Core: Pure Java business logic, no framework dependencies
  • Ports: Interfaces defining contracts (driven and driving)
  • Adapters: Concrete implementations (JPA, REST, messaging)
  • Domain Core:纯Java业务逻辑,无框架依赖
  • Ports:定义契约的接口(驱动型和被驱动型)
  • Adapters:具体实现(JPA、REST、消息队列)

Domain-Driven Design Tactical Patterns

Domain-Driven Design战术模式

  • Entities: Objects with identity and lifecycle (e.g.,
    Order
    ,
    Customer
    )
  • Value Objects: Immutable, defined by attributes (e.g.,
    Money
    ,
    Email
    )
  • Aggregates: Consistency boundary with root entity
  • Domain Events: Capture significant business occurrences
  • Repositories: Persistence abstraction, implemented in infrastructure
  • Entities:具有标识和生命周期的对象(如:
    Order
    Customer
  • Value Objects:不可变、由属性定义的对象(如:
    Money
    Email
  • Aggregates:包含根实体的一致性边界
  • Domain Events:捕获重要业务事件
  • Repositories:持久化抽象,在基础设施层实现

2. Organize Package Structure

2. 组织包结构

Follow this feature-based package organization:
com.example.order/
├── domain/
│   ├── model/              # Entities, value objects
│   ├── event/              # Domain events
│   ├── repository/         # Repository interfaces (ports)
│   └── exception/          # Domain exceptions
├── application/
│   ├── port/in/            # Driving ports (use case interfaces)
│   ├── port/out/           # Driven ports (external service interfaces)
│   ├── service/            # Application services
│   └── dto/                # Request/response DTOs
├── infrastructure/
│   ├── persistence/        # JPA entities, repository adapters
│   └── external/           # External service adapters
└── adapter/
    └── rest/               # REST controllers
遵循基于功能的包组织方式:
com.example.order/
├── domain/
│   ├── model/              # Entities, value objects
│   ├── event/              # Domain events
│   ├── repository/         # Repository interfaces (ports)
│   └── exception/          # Domain exceptions
├── application/
│   ├── port/in/            # Driving ports (use case interfaces)
│   ├── port/out/           # Driven ports (external service interfaces)
│   ├── service/            # Application services
│   └── dto/                # Request/response DTOs
├── infrastructure/
│   ├── persistence/        # JPA entities, repository adapters
│   └── external/           # External service adapters
└── adapter/
    └── rest/               # REST controllers

3. Implement the Domain Layer (Framework-Free)

3. 实现Domain层(无框架依赖)

The domain layer must have zero dependencies on Spring or any framework.
  • Use Java records for immutable value objects with built-in validation
  • Place business logic in entities, not services (Rich Domain Model)
  • Define repository interfaces (ports) in the domain layer
  • Use strongly-typed IDs to prevent ID confusion
  • Implement domain events for decoupling side effects
  • Use factory methods for entity creation to enforce invariants
Domain层必须完全独立于Spring或任何框架。
  • 使用Java Records实现带内置验证的不可变值对象
  • 将业务逻辑放在实体中,而非服务里(富领域模型)
  • 在Domain层定义仓库接口(ports)
  • 使用强类型ID避免ID混淆
  • 实现领域事件以解耦副作用
  • 使用工厂方法创建实体以强制约束

4. Implement the Application Layer

4. 实现Application层

  • Create use case interfaces (driving ports) in
    application/port/in/
  • Create external service interfaces (driven ports) in
    application/port/out/
  • Implement application services with
    @Service
    and
    @Transactional
  • Use DTOs for request/response, separate from domain models
  • Publish domain events after successful operations
  • application/port/in/
    中创建用例接口(驱动型ports)
  • application/port/out/
    中创建外部服务接口(被驱动型ports)
  • 使用@Service和@Transactional实现应用服务
  • 使用DTO处理请求/响应,与领域模型分离
  • 操作成功后发布领域事件

5. Implement the Infrastructure Layer (Adapters)

5. 实现Infrastructure层(Adapters)

  • Create JPA entities in
    infrastructure/persistence/
  • Implement repository adapters that map between domain and JPA entities
  • Use MapStruct or manual mappers for domain-JPA conversion
  • Configure conditional beans for swappable implementations
  • Keep infrastructure concerns isolated from domain logic
  • infrastructure/persistence/
    中创建JPA实体
  • 实现仓库适配器,映射领域实体与JPA实体
  • 使用MapStruct或手动映射器完成领域-JPA转换
  • 配置条件Bean以支持可替换实现
  • 确保基础设施关注点与领域逻辑隔离

6. Implement the Adapter Layer (REST)

6. 实现Adapter层(REST)

  • Create REST controllers in
    adapter/rest/
  • Inject use case interfaces, not implementations
  • Use Bean Validation on DTOs
  • Return proper HTTP status codes and responses
  • Handle exceptions with global exception handlers
  • adapter/rest/
    中创建REST控制器
  • 注入用例接口而非具体实现
  • 在DTO上使用Bean Validation
  • 返回正确的HTTP状态码和响应
  • 使用全局异常处理器处理异常

7. Apply Best Practices

7. 应用最佳实践

  1. Dependency Rule: Domain has zero dependencies on Spring or other frameworks
  2. Immutable Value Objects: Use Java records for value objects with built-in validation
  3. Rich Domain Models: Place business logic in entities, not services
  4. Repository Pattern: Domain defines interface, infrastructure implements
  5. Domain Events: Decouple side effects from primary operations
  6. Constructor Injection: Mandatory dependencies via final fields
  7. DTO Mapping: Separate domain models from API contracts
  8. Transaction Boundaries: Place @Transactional in application services
  1. 依赖规则:Domain层无Spring或其他框架依赖
  2. 不可变值对象:使用Java Records实现带内置验证的不可变值对象
  3. 富领域模型:将业务逻辑放在实体中,而非服务里
  4. 仓库模式:Domain层定义接口,Infrastructure层实现
  5. 领域事件:使用领域事件解耦主操作的副作用
  6. 构造函数注入:通过final字段注入强制依赖
  7. DTO映射:领域模型与API契约分离
  8. 事务边界:将@Transactional放在应用服务层

8. Write Tests

8. 编写测试

  • Domain Tests: Pure unit tests without Spring context, fast execution
  • Application Tests: Unit tests with mocked ports using Mockito
  • Infrastructure Tests: Integration tests with @DataJpaTest and Testcontainers
  • Adapter Tests: Controller tests with @WebMvcTest
  • Domain测试:纯单元测试,无需Spring上下文,执行速度快
  • Application测试:使用Mockito模拟ports的单元测试
  • Infrastructure测试:结合@DataJpaTest和Testcontainers的集成测试
  • Adapter测试:使用@WebMvcTest的控制器测试

Examples

示例

Example 1: Domain Layer - Entity with Domain Events

示例1:Domain层 - 带领域事件的实体

java
// domain/model/Order.java
public class Order {
    private final OrderId id;
    private final List<OrderItem> items;
    private Money total;
    private OrderStatus status;
    private final List<DomainEvent> domainEvents = new ArrayList<>();

    private Order(OrderId id, List<OrderItem> items) {
        this.id = id;
        this.items = new ArrayList<>(items);
        this.status = OrderStatus.PENDING;
        calculateTotal();
    }

    public static Order create(List<OrderItem> items) {
        validateItems(items);
        Order order = new Order(OrderId.generate(), items);
        order.domainEvents.add(new OrderCreatedEvent(order.id, order.total));
        return order;
    }

    public void confirm() {
        if (status != OrderStatus.PENDING) {
            throw new DomainException("Only pending orders can be confirmed");
        }
        this.status = OrderStatus.CONFIRMED;
    }

    public List<DomainEvent> getDomainEvents() {
        return List.copyOf(domainEvents);
    }

    public void clearDomainEvents() {
        domainEvents.clear();
    }
}
java
// domain/model/Order.java
public class Order {
    private final OrderId id;
    private final List<OrderItem> items;
    private Money total;
    private OrderStatus status;
    private final List<DomainEvent> domainEvents = new ArrayList<>();

    private Order(OrderId id, List<OrderItem> items) {
        this.id = id;
        this.items = new ArrayList<>(items);
        this.status = OrderStatus.PENDING;
        calculateTotal();
    }

    public static Order create(List<OrderItem> items) {
        validateItems(items);
        Order order = new Order(OrderId.generate(), items);
        order.domainEvents.add(new OrderCreatedEvent(order.id, order.total));
        return order;
    }

    public void confirm() {
        if (status != OrderStatus.PENDING) {
            throw new DomainException("Only pending orders can be confirmed");
        }
        this.status = OrderStatus.CONFIRMED;
    }

    public List<DomainEvent> getDomainEvents() {
        return List.copyOf(domainEvents);
    }

    public void clearDomainEvents() {
        domainEvents.clear();
    }
}

Example 2: Domain Layer - Value Object with Validation

示例2:Domain层 - 带验证的Value Object

java
// domain/model/Money.java (Value Object)
public record Money(BigDecimal amount, Currency currency) {
    public Money {
        if (amount.compareTo(BigDecimal.ZERO) < 0) {
            throw new DomainException("Amount cannot be negative");
        }
    }

    public static Money zero() {
        return new Money(BigDecimal.ZERO, Currency.getInstance("EUR"));
    }

    public Money add(Money other) {
        if (!this.currency.equals(other.currency)) {
            throw new DomainException("Currency mismatch");
        }
        return new Money(this.amount.add(other.amount), this.currency);
    }
}
java
// domain/model/Money.java (Value Object)
public record Money(BigDecimal amount, Currency currency) {
    public Money {
        if (amount.compareTo(BigDecimal.ZERO) < 0) {
            throw new DomainException("Amount cannot be negative");
        }
    }

    public static Money zero() {
        return new Money(BigDecimal.ZERO, Currency.getInstance("EUR"));
    }

    public Money add(Money other) {
        if (!this.currency.equals(other.currency)) {
            throw new DomainException("Currency mismatch");
        }
        return new Money(this.amount.add(other.amount), this.currency);
    }
}

Example 3: Domain Layer - Repository Port

示例3:Domain层 - 仓库Port

java
// domain/repository/OrderRepository.java (Port)
public interface OrderRepository {
    Order save(Order order);
    Optional<Order> findById(OrderId id);
}
java
// domain/repository/OrderRepository.java (Port)
public interface OrderRepository {
    Order save(Order order);
    Optional<Order> findById(OrderId id);
}

Example 4: Application Layer - Use Case and Service

示例4:Application层 - 用例与服务

java
// application/port/in/CreateOrderUseCase.java
public interface CreateOrderUseCase {
    OrderResponse createOrder(CreateOrderRequest request);
}

// application/dto/CreateOrderRequest.java
public record CreateOrderRequest(
    @NotNull UUID customerId,
    @NotEmpty List<OrderItemRequest> items
) {}

// application/service/OrderService.java
@Service
@RequiredArgsConstructor
@Transactional
public class OrderService implements CreateOrderUseCase {
    private final OrderRepository orderRepository;
    private final PaymentGateway paymentGateway;
    private final DomainEventPublisher eventPublisher;

    @Override
    public OrderResponse createOrder(CreateOrderRequest request) {
        List<OrderItem> items = mapItems(request.items());
        Order order = Order.create(items);

        PaymentResult payment = paymentGateway.charge(order.getTotal());
        if (!payment.successful()) {
            throw new PaymentFailedException("Payment failed");
        }

        order.confirm();
        Order saved = orderRepository.save(order);
        publishEvents(order);

        return OrderMapper.toResponse(saved);
    }

    private void publishEvents(Order order) {
        order.getDomainEvents().forEach(eventPublisher::publish);
        order.clearDomainEvents();
    }
}
java
// application/port/in/CreateOrderUseCase.java
public interface CreateOrderUseCase {
    OrderResponse createOrder(CreateOrderRequest request);
}

// application/dto/CreateOrderRequest.java
public record CreateOrderRequest(
    @NotNull UUID customerId,
    @NotEmpty List<OrderItemRequest> items
) {}

// application/service/OrderService.java
@Service
@RequiredArgsConstructor
@Transactional
public class OrderService implements CreateOrderUseCase {
    private final OrderRepository orderRepository;
    private final PaymentGateway paymentGateway;
    private final DomainEventPublisher eventPublisher;

    @Override
    public OrderResponse createOrder(CreateOrderRequest request) {
        List<OrderItem> items = mapItems(request.items());
        Order order = Order.create(items);

        PaymentResult payment = paymentGateway.charge(order.getTotal());
        if (!payment.successful()) {
            throw new PaymentFailedException("Payment failed");
        }

        order.confirm();
        Order saved = orderRepository.save(order);
        publishEvents(order);

        return OrderMapper.toResponse(saved);
    }

    private void publishEvents(Order order) {
        order.getDomainEvents().forEach(eventPublisher::publish);
        order.clearDomainEvents();
    }
}

Example 5: Infrastructure Layer - JPA Entity and Adapter

示例5:Infrastructure层 - JPA实体与适配器

java
// infrastructure/persistence/OrderJpaEntity.java
@Entity
@Table(name = "orders")
public class OrderJpaEntity {
    @Id
    private UUID id;
    @Enumerated(EnumType.STRING)
    private OrderStatus status;
    private BigDecimal totalAmount;
    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    private List<OrderItemJpaEntity> items;
}

// infrastructure/persistence/OrderRepositoryAdapter.java
@Component
@RequiredArgsConstructor
public class OrderRepositoryAdapter implements OrderRepository {
    private final OrderJpaRepository jpaRepository;
    private final OrderJpaMapper mapper;

    @Override
    public Order save(Order order) {
        OrderJpaEntity entity = mapper.toEntity(order);
        return mapper.toDomain(jpaRepository.save(entity));
    }

    @Override
    public Optional<Order> findById(OrderId id) {
        return jpaRepository.findById(id.value()).map(mapper::toDomain);
    }
}
java
// infrastructure/persistence/OrderJpaEntity.java
@Entity
@Table(name = "orders")
public class OrderJpaEntity {
    @Id
    private UUID id;
    @Enumerated(EnumType.STRING)
    private OrderStatus status;
    private BigDecimal totalAmount;
    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    private List<OrderItemJpaEntity> items;
}

// infrastructure/persistence/OrderRepositoryAdapter.java
@Component
@RequiredArgsConstructor
public class OrderRepositoryAdapter implements OrderRepository {
    private final OrderJpaRepository jpaRepository;
    private final OrderJpaMapper mapper;

    @Override
    public Order save(Order order) {
        OrderJpaEntity entity = mapper.toEntity(order);
        return mapper.toDomain(jpaRepository.save(entity));
    }

    @Override
    public Optional<Order> findById(OrderId id) {
        return jpaRepository.findById(id.value()).map(mapper::toDomain);
    }
}

Example 6: Adapter Layer - REST Controller

示例6:Adapter层 - REST控制器

java
// adapter/rest/OrderController.java
@RestController
@RequestMapping("/api/orders")
@RequiredArgsConstructor
public class OrderController {
    private final CreateOrderUseCase createOrderUseCase;

    @PostMapping
    public ResponseEntity<OrderResponse> createOrder(
            @Valid @RequestBody CreateOrderRequest request) {
        OrderResponse response = createOrderUseCase.createOrder(request);
        URI location = ServletUriComponentsBuilder
            .fromCurrentRequest()
            .path("/{id}")
            .buildAndExpand(response.id())
            .toUri();
        return ResponseEntity.created(location).body(response);
    }
}
java
// adapter/rest/OrderController.java
@RestController
@RequestMapping("/api/orders")
@RequiredArgsConstructor
public class OrderController {
    private final CreateOrderUseCase createOrderUseCase;

    @PostMapping
    public ResponseEntity<OrderResponse> createOrder(
            @Valid @RequestBody CreateOrderRequest request) {
        OrderResponse response = createOrderUseCase.createOrder(request);
        URI location = ServletUriComponentsBuilder
            .fromCurrentRequest()
            .path("/{id}")
            .buildAndExpand(response.id())
            .toUri();
        return ResponseEntity.created(location).body(response);
    }
}

Example 7: Domain Tests (No Spring Context)

示例7:Domain测试(无Spring上下文)

java
class OrderTest {
    @Test
    void shouldCreateOrderWithValidItems() {
        List<OrderItem> items = List.of(
            new OrderItem(new ProductId(UUID.randomUUID()), 2, new Money("10.00", EUR))
        );

        Order order = Order.create(items);

        assertThat(order.getStatus()).isEqualTo(OrderStatus.PENDING);
        assertThat(order.getDomainEvents()).hasSize(1);
    }
}
java
class OrderTest {
    @Test
    void shouldCreateOrderWithValidItems() {
        List<OrderItem> items = List.of(
            new OrderItem(new ProductId(UUID.randomUUID()), 2, new Money("10.00", EUR))
        );

        Order order = Order.create(items);

        assertThat(order.getStatus()).isEqualTo(OrderStatus.PENDING);
        assertThat(order.getDomainEvents()).hasSize(1);
    }
}

Example 8: Application Tests (Unit with Mocks)

示例8:Application测试(带Mock的单元测试)

java
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
    @Mock OrderRepository orderRepository;
    @Mock PaymentGateway paymentGateway;
    @Mock DomainEventPublisher eventPublisher;

    @InjectMocks OrderService orderService;

    @Test
    void shouldCreateAndConfirmOrder() {
        when(paymentGateway.charge(any())).thenReturn(new PaymentResult(true, "tx-123"));
        when(orderRepository.save(any())).thenAnswer(i -> i.getArgument(0));

        OrderResponse response = orderService.createOrder(createRequest());

        assertThat(response.status()).isEqualTo(OrderStatus.CONFIRMED);
        verify(eventPublisher).publish(any(OrderCreatedEvent.class));
    }
}
java
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
    @Mock OrderRepository orderRepository;
    @Mock PaymentGateway paymentGateway;
    @Mock DomainEventPublisher eventPublisher;

    @InjectMocks OrderService orderService;

    @Test
    void shouldCreateAndConfirmOrder() {
        when(paymentGateway.charge(any())).thenReturn(new PaymentResult(true, "tx-123"));
        when(orderRepository.save(any())).thenAnswer(i -> i.getArgument(0));

        OrderResponse response = orderService.createOrder(createRequest());

        assertThat(response.status()).isEqualTo(OrderStatus.CONFIRMED);
        verify(eventPublisher).publish(any(OrderCreatedEvent.class));
    }
}

Best Practices

最佳实践

  1. Dependency Rule: Domain has zero dependencies on Spring or other frameworks - this is the most critical principle
  2. Immutable Value Objects: Use Java records for value objects with built-in validation in compact constructors
  3. Rich Domain Models: Place business logic in entities, not services - avoid anemic domain models
  4. Repository Pattern: Domain defines interface, infrastructure implements - never the reverse
  5. Domain Events: Decouple side effects from primary operations using event-driven patterns
  6. Constructor Injection: Mandatory dependencies via final fields with Lombok @RequiredArgsConstructor
  7. DTO Mapping: Separate domain models from API contracts - never expose entities directly
  8. Transaction Boundaries: Place @Transactional in application services, never in domain layer
  9. Factory Methods: Use static factory methods like
    Entity.create()
    for entity construction with invariant enforcement
  10. Separate JPA Entities: Keep domain entities separate from JPA entities with mappers between them
  1. 依赖规则:Domain层无Spring或其他框架依赖 - 这是最核心的原则
  2. 不可变值对象:使用Java Records实现带紧凑构造函数验证的不可变值对象
  3. 富领域模型:将业务逻辑放在实体中,而非服务里 - 避免贫血领域模型
  4. 仓库模式:Domain层定义接口,Infrastructure层实现 - 绝不能反过来
  5. 领域事件:使用事件驱动模式解耦主操作的副作用
  6. 构造函数注入:通过Lombok @RequiredArgsConstructor和final字段注入强制依赖
  7. DTO映射:领域模型与API契约分离 - 绝不要直接暴露实体
  8. 事务边界:将@Transactional放在应用服务层,绝不要放在Domain层
  9. 工厂方法:使用
    Entity.create()
    这类静态工厂方法创建实体以强制约束
  10. 分离JPA实体:将领域实体与JPA实体分离,通过映射器转换

Constraints and Warnings

约束与警告

Critical Constraints

核心约束

  • Domain Layer Purity: Never add Spring annotations (
    @Entity
    ,
    @Autowired
    ,
    @Component
    ) to domain classes
  • Dependency Direction: Dependencies must only point inward (domain <- application <- infrastructure/adapter)
  • Framework Isolation: All framework-specific code must stay in infrastructure and adapter layers
  • Domain层纯净性:绝不要在Domain类中添加Spring注解(
    @Entity
    @Autowired
    @Component
  • 依赖方向:依赖必须仅向内流动(Domain <- Application <- Infrastructure/Adapter)
  • 框架隔离:所有框架特定代码必须留在Infrastructure和Adapter层

Common Pitfalls to Avoid

需避免的常见陷阱

  • Anemic Domain Model: Entities with only getters/setters, logic in services - place business logic in entities
  • Framework Leakage:
    @Entity
    ,
    @Autowired
    in domain layer - keep domain framework-free
  • Lazy Loading Issues: Exposing JPA entities through domain model - use mappers to convert
  • Circular Dependencies: Between domain aggregates - use IDs instead of direct references
  • Missing Domain Events: Direct service calls instead of events for cross-aggregate communication
  • Repository Misplacement: Defining repository interfaces in infrastructure - they belong in domain
  • DTO Bypass: Exposing domain entities directly in API - always use DTOs for external contracts
  • 贫血领域模型:实体只有getter/setter,逻辑放在服务里 - 将业务逻辑放在实体中
  • 框架泄漏:Domain层出现
    @Entity
    @Autowired
    - 保持Domain层无框架依赖
  • 懒加载问题:通过领域模型暴露JPA实体 - 使用映射器进行转换
  • 循环依赖:领域聚合之间的循环依赖 - 使用ID代替直接引用
  • 缺失领域事件:直接调用服务而非使用事件进行跨聚合通信
  • 仓库位置错误:在Infrastructure层定义仓库接口 - 它们属于Domain层
  • 绕过DTO:在API中直接暴露领域实体 - 始终为外部契约使用DTO

Performance Considerations

性能考量

  • Separate JPA entities from domain models to avoid lazy loading issues
  • Use read-only transactions for query operations
  • Consider CQRS for complex read/write scenarios
  • 将JPA实体与领域模型分离以避免懒加载问题
  • 为查询操作使用只读事务
  • 复杂读写场景考虑使用CQRS

References

参考资料

  • references/java-clean-architecture.md
    - Java-specific patterns (records, sealed classes, strongly-typed IDs)
  • references/spring-boot-implementation.md
    - Spring Boot integration (DI patterns, JPA mapping, transaction management)
  • references/java-clean-architecture.md
    - Java特定模式(Records、密封类、强类型ID)
  • references/spring-boot-implementation.md
    - Spring Boot集成(DI模式、JPA映射、事务管理)