domain-driven-design

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Domain-Driven Design

领域驱动设计(Domain-Driven Design,DDD)

Core Concepts

核心概念

Ubiquitous Language

通用语言(Ubiquitous Language)

Use the same terminology as domain experts. Code should read like business documentation.
使用与领域专家一致的术语体系,代码应具备业务文档级的可读性。

Bounded Context

限界上下文(Bounded Context)

A boundary within which a particular domain model is defined and applicable.
定义特定领域模型的适用边界,模型仅在该边界内有效。

Context Map

上下文映射(Context Map)

Shows how bounded contexts relate to each other.
展示不同限界上下文之间的关联关系。

Building Blocks

构建块

Entity

Entity(实体)

Has identity that persists over time. Equality based on ID.
java
@Entity
@Data
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @EqualsAndHashCode.Include
    private Long id;

    @Embedded
    private Email email;

    private String name;

    @Builder.Default
    private Instant createdAt = Instant.now();
}
拥有持久化的唯一标识,基于ID判断相等性。
java
@Entity
@Data
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @EqualsAndHashCode.Include
    private Long id;

    @Embedded
    private Email email;

    private String name;

    @Builder.Default
    private Instant createdAt = Instant.now();
}

Value Object

Value Object(值对象)

Immutable, equality based on attributes.
java
@Embeddable
@Value
public class Email {
    private String value;

    private Email(String value) {
        this.value = value;
    }

    public static Email of(String value) {
        if (value == null || !value.contains("@")) {
            throw new IllegalArgumentException("Invalid email");
        }
        return new Email(value);
    }

    // Lombok @Value makes it immutable and generates equals/hashCode
}
具备不可变性,基于属性判断相等性。
java
@Embeddable
@Value
public class Email {
    private String value;

    private Email(String value) {
        this.value = value;
    }

    public static Email of(String value) {
        if (value == null || !value.contains("@")) {
            throw new IllegalArgumentException("Invalid email");
        }
        return new Email(value);
    }

    // Lombok @Value makes it immutable and generates equals/hashCode
}

Aggregate

Aggregate(聚合)

Cluster of entities and value objects with a root entity.
java
@Entity
@Data
public class Order { // Aggregate Root
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    @JoinColumn(name = "order_id")
    private List<OrderItem> items = new ArrayList<>();

    private Long userId;

    @Enumerated(EnumType.STRING)
    private OrderStatus status;

    // Business logic in domain model
    public void addItem(Long productId, int quantity) {
        // Business rules enforced here
        if (this.status != OrderStatus.DRAFT) {
            throw new IllegalStateException("Cannot add items to submitted order");
        }
        OrderItem item = OrderItem.builder()
            .productId(productId)
            .quantity(quantity)
            .build();
        this.items.add(item);
    }

    public Money getTotal() {
        return items.stream()
            .map(OrderItem::getSubtotal)
            .reduce(Money.zero(), Money::add);
    }

    public void submit() {
        if (items.isEmpty()) {
            throw new IllegalStateException("Cannot submit empty order");
        }
        this.status = OrderStatus.SUBMITTED;
    }
}
由实体和值对象组成的集群,包含一个根实体(Aggregate Root)。
java
@Entity
@Data
public class Order { // Aggregate Root
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    @JoinColumn(name = "order_id")
    private List<OrderItem> items = new ArrayList<>();

    private Long userId;

    @Enumerated(EnumType.STRING)
    private OrderStatus status;

    // Business logic in domain model
    public void addItem(Long productId, int quantity) {
        // Business rules enforced here
        if (this.status != OrderStatus.DRAFT) {
            throw new IllegalStateException("Cannot add items to submitted order");
        }
        OrderItem item = OrderItem.builder()
            .productId(productId)
            .quantity(quantity)
            .build();
        this.items.add(item);
    }

    public Money getTotal() {
        return items.stream()
            .map(OrderItem::getSubtotal)
            .reduce(Money.zero(), Money::add);
    }

    public void submit() {
        if (items.isEmpty()) {
            throw new IllegalStateException("Cannot submit empty order");
        }
        this.status = OrderStatus.SUBMITTED;
    }
}

Repository

Repository(仓库)

Abstracts data access for aggregates.
java
@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
    Optional<Order> findById(Long id);

    @Query("SELECT o FROM Order o LEFT JOIN FETCH o.items WHERE o.id = :id")
    Optional<Order> findByIdWithItems(@Param("id") Long id);

    List<Order> findByUserIdAndStatus(Long userId, OrderStatus status);
}
对聚合根的数据访问逻辑进行抽象封装。
java
@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
    Optional<Order> findById(Long id);

    @Query("SELECT o FROM Order o LEFT JOIN FETCH o.items WHERE o.id = :id")
    Optional<Order> findByIdWithItems(@Param("id") Long id);

    List<Order> findByUserIdAndStatus(Long userId, OrderStatus status);
}

Domain Event

Domain Event(领域事件)

Something that happened in the domain.
java
@Value
@Builder
public class OrderPlaced {
    Long orderId;
    Long userId;
    Instant occurredAt;
}

// Publishing domain events with Spring
@Service
@Transactional
public class OrderService {
    private final OrderRepository orderRepository;
    private final ApplicationEventPublisher eventPublisher;

    public void placeOrder(Long orderId) {
        Order order = orderRepository.findById(orderId)
            .orElseThrow(() -> new OrderNotFoundException(orderId));

        order.submit();
        orderRepository.save(order);

        // Publish domain event
        OrderPlaced event = OrderPlaced.builder()
            .orderId(order.getId())
            .userId(order.getUserId())
            .occurredAt(Instant.now())
            .build();
        eventPublisher.publishEvent(event);
    }
}

// Event listener
@Component
public class OrderEventHandler {
    @EventListener
    @Async
    public void handleOrderPlaced(OrderPlaced event) {
        // Send confirmation email, update inventory, etc.
    }
}
记录领域内发生的重要业务事件。
java
@Value
@Builder
public class OrderPlaced {
    Long orderId;
    Long userId;
    Instant occurredAt;
}

// Publishing domain events with Spring
@Service
@Transactional
public class OrderService {
    private final OrderRepository orderRepository;
    private final ApplicationEventPublisher eventPublisher;

    public void placeOrder(Long orderId) {
        Order order = orderRepository.findById(orderId)
            .orElseThrow(() -> new OrderNotFoundException(orderId));

        order.submit();
        orderRepository.save(order);

        // Publish domain event
        OrderPlaced event = OrderPlaced.builder()
            .orderId(order.getId())
            .userId(order.getUserId())
            .occurredAt(Instant.now())
            .build();
        eventPublisher.publishEvent(event);
    }
}

// Event listener
@Component
public class OrderEventHandler {
    @EventListener
    @Async
    public void handleOrderPlaced(OrderPlaced event) {
        // Send confirmation email, update inventory, etc.
    }
}

Strategic Patterns

战略模式

Anti-Corruption Layer

Anti-Corruption Layer(防腐层)

Translate between your model and external systems.
实现自身领域模型与外部系统之间的适配转换。

Shared Kernel

Shared Kernel(共享内核)

Shared subset of domain model between contexts.
不同限界上下文之间共享的领域模型子集。

Customer-Supplier

Customer-Supplier(客户-供应商)

Upstream provides what downstream needs.
上游系统按需提供下游系统所需的模型或服务。