C++ 领域驱动设计(DDD):在复杂业务架构中利用 C++ 强类型系统表达业务不变式与生命周期规则

C++ 领域驱动设计 (DDD):在复杂业务架构中利用 C++ 强类型系统表达业务不变式与生命周期规则

各位编程领域的同仁们,大家好!

今天,我们将深入探讨一个既引人入胜又极具挑战性的话题:如何在 C++ 这种以性能和系统级控制著称的语言中,优雅地实现领域驱动设计(DDD)。许多人可能认为 C++ 和 DDD 似乎是两个不常并列的词语,毕竟 DDD 更多地与 Java、C# 这类拥有丰富运行时反射和动态特性的语言联系在一起。然而,正是 C++ 的“强类型系统”和“零成本抽象”能力,使其在表达复杂业务逻辑的“不变式”(Invariants)和“生命周期规则”方面,展现出独特而强大的潜力。

在高性能、低延迟、资源受限或对确定性行为有极高要求的复杂业务系统中,C++ 往往是不可替代的选择。想象一下金融交易系统、航空航天控制、高频数据处理或大型游戏引擎的后端,这些场景不仅需要极致的性能,更需要业务逻辑的精确无误和状态转换的严格控制。DDD 的核心思想是围绕核心业务领域构建软件,而 C++ 的类型系统恰好能将这些业务规则——那些“不能被打破的规矩”——编码到编译时,从而在系统运行时提供坚如磐石的保证。

本次讲座,我们将以深入浅出的方式,结合丰富的代码示例,揭示 C++ 如何成为 DDD 实践的强大盟友。

DDD 核心概念回顾

在深入探讨 C++ 的具体应用之前,让我们快速回顾一下领域驱动设计(DDD)的一些核心概念。这些是我们在构建复杂系统时赖以思考和沟通的基础。

  1. 领域 (Domain): 软件所要解决的业务范围。它是业务专家和开发人员共同理解的、关于业务的知识体系。
  2. 限界上下文 (Bounded Context): DDD 中最重要的概念之一。它定义了一个明确的业务边界,在这个边界内,特定的领域模型、术语和语言是统一且一致的。不同限界上下文之间通过明确的接口进行协作,避免了“大泥球”式的模型混淆。
  3. 通用语言 (Ubiquitous Language): 在特定限界上下文内,业务专家和开发人员共同使用的、无歧义的语言。它直接反映在代码的命名、模型和结构中。
  4. 实体 (Entity): 具有唯一标识符的对象,其身份在时间上持续存在。实体的属性可以变化,但其身份保持不变。例如,一个 Customer (客户) 或 Order (订单)。
  5. 值对象 (Value Object): 没有唯一标识符的对象,其特性完全由其属性值定义。值对象通常是不可变的。例如,Money (金额)、Address (地址) 或 DateRange (日期范围)。当所有属性都相同时,两个值对象被认为是相等的。
  6. 聚合 (Aggregate): 一组相关联的实体和值对象的集合,被视为一个单一的业务单元。聚合有一个“聚合根”(Aggregate Root),它是外部访问聚合的唯一入口。聚合内部的所有业务不变式都必须通过聚合根来维护。例如,一个 Order 聚合可能包含 OrderLineItem (订单项) 实体和 Address (地址) 值对象。
  7. 领域服务 (Domain Service): 当某些业务操作不自然地属于任何一个实体或值对象时,可以将其建模为领域服务。领域服务通常是无状态的,它协调多个领域对象完成一项业务任务。例如,OrderPlacementService (订单下单服务)。
  8. 仓储 (Repository): 提供了一种机制,用于存储和检索聚合。它抽象了数据持久化的具体实现细节,使得领域模型可以专注于业务逻辑。仓储操作的对象始终是聚合根。
  9. 领域事件 (Domain Event): 当领域中发生某个重要的事情时,可以发布一个领域事件来通知其他限界上下文或系统组件。领域事件是不可变的、已发生的业务事实。例如,OrderPlacedEvent (订单已下单事件)。

理解这些概念是运用 C++ 实现 DDD 的前提。现在,让我们看看 C++ 的强类型系统如何帮助我们精确表达这些概念。

C++ 强类型系统对业务不变式的表达

C++ 的核心优势之一在于其强大的静态类型系统。它允许我们在编译时捕获大量的逻辑错误,并强制执行设计约束。对于 DDD 而言,这意味着我们可以将业务规则和不变式直接编码到类型定义中,从而在代码层面提供强大的业务语义保证。

1. 值对象 (Value Objects) 的不可变性与安全性

值对象是 DDD 中的基石,它们通常代表某个概念的属性,并且是不可变的。在 C++ 中,我们可以通过以下方式实现高度安全和表达力的值对象:

  • 封装原始类型: 将原始类型(如 int, double, std::string)封装到自定义 structclass 中。这赋予了它们业务含义,并防止了类型混淆。
  • 不可变性: 确保值对象一旦创建就不能被修改。这意味着所有成员变量都是 const 或私有的,并且没有提供修改器(setter)方法。构造函数是唯一设置其值的地方。
  • 私有构造函数与工厂方法/静态成员函数: 对于需要验证其内部状态的值对象(例如,一个邮箱地址必须包含 @ 符号),可以将构造函数设为私有,并提供一个静态工厂方法来执行验证和创建。
  • 自定义操作符重载: 重载 ==!=<+ 等操作符,使值对象的行为符合业务直觉。

示例:Money 值对象

一个 Money 对象不应该只是一个 doubledouble 会带来浮点数精度问题,并且无法表达货币单位。

#include <string>
#include <stdexcept>
#include <ostream>
#include <functional> // For std::hash

// 定义货币单位,通常使用枚举或更复杂的 CurrencyCode 值对象
enum class CurrencyCode {
    USD, EUR, GBP, JPY
    // ... 更多货币
};

// Money 值对象
class Money {
public:
    // 静态工厂方法,用于创建 Money 对象并进行验证
    static Money create(long long amountInCents, CurrencyCode currency) {
        if (amountInCents < 0) {
            throw std::invalid_argument("Money amount cannot be negative.");
        }
        return Money(amountInCents, currency);
    }

    // 默认构造函数和拷贝构造函数/赋值操作符由编译器生成,因为没有裸指针或复杂资源
    // 但是,为了强调不可变性,我们可以明确声明它们,或者直接依赖默认行为

    // Getter 方法,返回 const 引用或按值返回副本
    long long getAmountInCents() const { return m_amountInCents; }
    CurrencyCode getCurrency() const { return m_currency; }

    // 重载相等运算符
    bool operator==(const Money& other) const {
        return m_amountInCents == other.m_amountInCents &&
               m_currency == other.m_currency;
    }

    bool operator!=(const Money& other) const {
        return !(*this == other);
    }

    // 重载加法运算符
    Money operator+(const Money& other) const {
        if (m_currency != other.m_currency) {
            throw std::invalid_argument("Cannot add Money of different currencies.");
        }
        // 检查溢出
        long long result = m_amountInCents + other.m_amountInCents;
        if ((m_amountInCents > 0 && other.m_amountInCents > 0 && result < m_amountInCents) ||
            (m_amountInCents < 0 && other.m_amountInCents < 0 && result > m_amountInCents)) {
            throw std::overflow_error("Money addition overflowed.");
        }
        return Money(result, m_currency);
    }

    // 重载减法运算符
    Money operator-(const Money& other) const {
        if (m_currency != other.m_currency) {
            throw std::invalid_argument("Cannot subtract Money of different currencies.");
        }
        long long result = m_amountInCents - other.m_amountInCents;
        // 注意:根据业务规则,减法结果可能允许为负,或者需要额外的验证。
        // 这里我们简化处理,允许负数。如果业务不允许,应在此处抛出异常。
        return Money(result, m_currency);
    }

    // 乘法,通常用于乘以数量
    Money operator*(int factor) const {
        return Money(m_amountInCents * factor, m_currency);
    }

    // 打印支持
    friend std::ostream& operator<<(std::ostream& os, const Money& money) {
        os << money.m_amountInCents / 100.0 << " " << static_cast<int>(money.m_currency); // 简化货币打印
        return os;
    }

private:
    long long m_amountInCents; // 以分为单位存储金额,避免浮点数问题
    CurrencyCode m_currency;

    // 私有构造函数,强制通过工厂方法创建
    Money(long long amountInCents, CurrencyCode currency)
        : m_amountInCents(amountInCents), m_currency(currency) {}
};

// 允许 Money 对象在哈希容器中使用
namespace std {
    template<> struct hash<Money> {
        size_t operator()(const Money& money) const {
            size_t h1 = hash<long long>{}(money.getAmountInCents());
            size_t h2 = hash<int>{}(static_cast<int>(money.getCurrency()));
            return h1 ^ (h2 << 1); // 简单的组合哈希
        }
    };
}

// 示例:EmailAddress
class EmailAddress {
public:
    // 静态工厂方法进行验证
    static std::optional<EmailAddress> create(const std::string& email) {
        // 简单的邮箱格式验证,实际应用中会更复杂
        if (email.empty() || email.find('@') == std::string::npos || email.find('.') == std::string::npos) {
            return std::nullopt; // 验证失败
        }
        return EmailAddress(email);
    }

    const std::string& getValue() const { return m_value; }

    bool operator==(const EmailAddress& other) const { return m_value == other.m_value; }
    bool operator!=(const EmailAddress& other) const { return !(*this == other); }

private:
    std::string m_value;
    EmailAddress(std::string email) : m_value(std::move(email)) {} // 私有构造函数
};

通过这种方式,我们确保了 Money 始终是有效的(非负、有货币单位),并且不能与其他货币直接相加,也不会出现浮点数精度问题。这些都是关键的业务不变式。

2. 实体 (Entities) 的身份与生命周期

实体有唯一的标识符,其身份在时间上是稳定的。在 C++ 中,我们通常使用一个值对象来表示实体的 ID,并将其作为实体的一部分。

  • 唯一标识符: 使用一个强类型的值对象来封装实体的 ID,而不是裸露的 intstd::string。这增加了代码的清晰度,并防止了 ID 的误用。
  • 封装状态: 实体的内部状态应私有化,并通过公共方法(行为)来修改。这些方法应确保状态转换符合业务规则。
  • 智能指针管理所有权: 对于复杂的实体或聚合,使用 std::unique_ptrstd::shared_ptr 来管理其生命周期和所有权,避免内存泄漏和悬空指针。

示例:CustomerId 值对象和 Customer 实体

#include <string>
#include <utility> // For std::move
#include <iostream>
#include <memory>  // For std::unique_ptr

// CustomerId 值对象
struct CustomerId {
    std::string value;

    explicit CustomerId(std::string val) : value(std::move(val)) {}

    bool operator==(const CustomerId& other) const { return value == other.value; }
    bool operator!=(const CustomerId& other) const { return !(*this == other); }

    // 方便打印
    friend std::ostream& operator<<(std::ostream& os, const CustomerId& id) {
        os << "CustomerId(" << id.value << ")";
        return os;
    }
    // 允许哈希
    // ... (类似 Money 的 hash 实现)
};

// Customer 实体
class Customer {
public:
    // 构造函数
    Customer(CustomerId id, std::string name, EmailAddress email)
        : m_id(std::move(id)), m_name(std::move(name)), m_email(std::move(email)) {}

    // Getter 方法
    const CustomerId& getId() const { return m_id; }
    const std::string& getName() const { return m_name; }
    const EmailAddress& getEmail() const { return m_email; }

    // 业务行为:修改客户名称
    void changeName(const std::string& newName) {
        if (newName.empty()) {
            throw std::invalid_argument("Customer name cannot be empty.");
        }
        m_name = newName;
        // 可以在这里发布一个 CustomerNameChangedEvent 领域事件
    }

    // 业务行为:修改客户邮箱
    void changeEmail(const EmailAddress& newEmail) {
        if (m_email == newEmail) {
            return; // 邮箱未变化
        }
        m_email = newEmail;
        // 可以在这里发布一个 CustomerEmailChangedEvent 领域事件
    }

private:
    CustomerId m_id;
    std::string m_name;
    EmailAddress m_email; // 使用前面定义的值对象
    // ... 其他属性
};

这里 CustomerId 作为值对象,确保了客户身份的强类型表达。Customer 实体通过公共方法 changeNamechangeEmail 来修改其状态,这些方法内部可以执行业务验证。

3. 聚合 (Aggregates) 的一致性边界

聚合是 DDD 中维护业务不变式的关键。一个聚合有且只有一个聚合根,外部只能通过聚合根来访问和修改聚合内部的实体和值对象。这确保了聚合内部的一致性。

  • 聚合根作为唯一入口: 聚合根是聚合中的一个实体,它是外部对象访问聚合的唯一入口。所有对聚合内部对象的修改都必须通过聚合根的方法进行。
  • 私有成员: 聚合内的其他实体和值对象应作为聚合根的私有成员,或者仅通过聚合根提供受控的访问。
  • 强一致性: 聚合内的所有业务不变式必须在聚合根的方法执行完毕后得到满足。

示例:Order 聚合

一个订单 Order 是一个典型的聚合根,它包含 OrderLineItem (订单项) 等。

#include <vector>
#include <numeric> // For std::accumulate
// 假设 ProductId, Quantity 也是值对象,类似 CustomerId

// ProductId 值对象
struct ProductId {
    std::string value;
    explicit ProductId(std::string val) : value(std::move(val)) {}
    bool operator==(const ProductId& other) const { return value == other.value; }
    bool operator!=(const ProductId& other) const { return !(*this == other); }
    friend std::ostream& operator<<(std::ostream& os, const ProductId& id) {
        os << "ProductId(" << id.value << ")"; return os;
    }
};

// Quantity 值对象
class Quantity {
public:
    static Quantity create(int value) {
        if (value <= 0) {
            throw std::invalid_argument("Quantity must be positive.");
        }
        return Quantity(value);
    }
    int getValue() const { return m_value; }
    bool operator==(const Quantity& other) const { return m_value == other.m_value; }
    bool operator!=(const Quantity& other) const { return !(*this == other); }
    friend std::ostream& operator<<(std::ostream& os, const Quantity& q) {
        os << "Quantity(" << q.m_value << ")"; return os;
    }
private:
    int m_value;
    Quantity(int value) : m_value(value) {}
};

// OrderId 值对象
struct OrderId {
    std::string value;
    explicit OrderId(std::string val) : value(std::move(val)) {}
    bool operator==(const OrderId& other) const { return value == other.value; }
    bool operator!=(const OrderId& other) const { return !(*this == other); }
    friend std::ostream& operator<<(std::ostream& os, const OrderId& id) {
        os << "OrderId(" << id.value << ")"; return os;
    }
};

// OrderLineItem 实体 (聚合内部实体)
class OrderLineItem {
public:
    // OrderLineItem 只需要内部 ID,不需要全局唯一性
    OrderLineItem(int lineNumber, ProductId productId, Quantity quantity, Money unitPrice)
        : m_lineNumber(lineNumber), m_productId(std::move(productId)),
          m_quantity(std::move(quantity)), m_unitPrice(std::move(unitPrice)) {}

    int getLineNumber() const { return m_lineNumber; }
    const ProductId& getProductId() const { return m_productId; }
    const Quantity& getQuantity() const { return m_quantity; }
    const Money& getUnitPrice() const { return m_unitPrice; }

    Money calculateLineTotal() const {
        return m_unitPrice * m_quantity.getValue(); // 假设 Money::operator* 存在
    }

    // 仅允许通过聚合根修改数量,这里只提供内部修改方法
    void updateQuantity(const Quantity& newQuantity) {
        m_quantity = newQuantity;
    }

private:
    int m_lineNumber;
    ProductId m_productId;
    Quantity m_quantity;
    Money m_unitPrice;
};

// 订单状态枚举 (后面会用作状态模式)
enum class OrderStatus {
    Pending, // 待处理
    Paid,    // 已支付
    Shipped, // 已发货
    Cancelled // 已取消
};

// Order 聚合根
class Order {
public:
    // 构造函数
    Order(OrderId id, CustomerId customerId)
        : m_id(std::move(id)), m_customerId(std::move(customerId)),
          m_status(OrderStatus::Pending) {}

    // Getter 方法
    const OrderId& getId() const { return m_id; }
    const CustomerId& getCustomerId() const { return m_customerId; }
    OrderStatus getStatus() const { return m_status; }
    const std::vector<OrderLineItem>& getLineItems() const { return m_lineItems; }

    // 业务行为:添加订单项
    void addLineItem(ProductId productId, Quantity quantity, Money unitPrice) {
        if (m_status != OrderStatus::Pending) {
            throw std::logic_error("Cannot add items to an order that is not pending.");
        }
        int nextLineNumber = m_lineItems.empty() ? 1 : m_lineItems.back().getLineNumber() + 1;
        m_lineItems.emplace_back(nextLineNumber, std::move(productId), std::move(quantity), std::move(unitPrice));
        // 可以在这里发布 OrderItemAddedEvent
    }

    // 业务行为:更新订单项数量
    void updateLineItemQuantity(int lineNumber, Quantity newQuantity) {
        if (m_status != OrderStatus::Pending) {
            throw std::logic_error("Cannot update items in an order that is not pending.");
        }
        auto it = std::find_if(m_lineItems.begin(), m_lineItems.end(),
                               [lineNumber](const OrderLineItem& item) { return item.getLineNumber() == lineNumber; });
        if (it == m_lineItems.end()) {
            throw std::invalid_argument("Line item not found.");
        }
        it->updateQuantity(newQuantity);
        // 可以在这里发布 OrderItemQuantityUpdatedEvent
    }

    // 业务行为:移除订单项
    void removeLineItem(int lineNumber) {
        if (m_status != OrderStatus::Pending) {
            throw std::logic_error("Cannot remove items from an order that is not pending.");
        }
        auto originalSize = m_lineItems.size();
        m_lineItems.erase(std::remove_if(m_lineItems.begin(), m_lineItems.end(),
                                         [lineNumber](const OrderLineItem& item) { return item.getLineNumber() == lineNumber; }),
                          m_lineItems.end());
        if (m_lineItems.size() == originalSize) {
            throw std::invalid_argument("Line item not found.");
        }
        // 可以在这里发布 OrderItemRemovedEvent
    }

    // 业务行为:计算总价
    Money calculateTotal() const {
        if (m_lineItems.empty()) {
            return Money::create(0, Money::create(0, CurrencyCode::USD).getCurrency()); // 假设所有订单都用 USD
        }
        Money total = Money::create(0, m_lineItems[0].getUnitPrice().getCurrency()); // 以第一个项目的货币为基准
        for (const auto& item : m_lineItems) {
            total = total + item.calculateLineTotal();
        }
        return total;
    }

    // 业务行为:支付订单
    void markAsPaid() {
        if (m_status != OrderStatus::Pending) {
            throw std::logic_error("Order must be in Pending status to be marked as Paid.");
        }
        if (m_lineItems.empty()) {
             throw std::logic_error("Cannot pay for an empty order.");
        }
        m_status = OrderStatus::Paid;
        // 可以在这里发布 OrderPaidEvent
    }

    // 业务行为:发货
    void markAsShipped() {
        if (m_status != OrderStatus::Paid) {
            throw std::logic_error("Order must be in Paid status to be marked as Shipped.");
        }
        m_status = OrderStatus::Shipped;
        // 可以在这里发布 OrderShippedEvent
    }

    // 业务行为:取消订单
    void cancel() {
        if (m_status == OrderStatus::Shipped) {
            throw std::logic_error("Cannot cancel a shipped order.");
        }
        if (m_status == OrderStatus::Cancelled) {
            return; // 已经取消
        }
        m_status = OrderStatus::Cancelled;
        // 可以在这里发布 OrderCancelledEvent
    }

private:
    OrderId m_id;
    CustomerId m_customerId;
    std::vector<OrderLineItem> m_lineItems;
    OrderStatus m_status; // 订单状态
};

Order 类作为聚合根,封装了 OrderLineItem 的列表和 OrderStatus。所有对订单项的操作(添加、修改数量、移除)都必须通过 Order 对象的方法来完成。这些方法内部包含了业务规则,例如“不能向已支付的订单添加商品”。

4. 领域服务 (Domain Services) 的无状态操作

当某个操作不自然地属于任何一个实体或值对象时,我们可以将其建模为领域服务。领域服务通常是无状态的,它协调多个领域对象来完成一项业务任务。

  • 无状态: 领域服务不应持有任何可变状态。它们应该像纯函数一样,只接受输入,执行逻辑,并产生输出。
  • 协调者: 领域服务负责协调一个或多个聚合完成复杂业务流程,而不是直接操作聚合的内部状态。
  • 依赖注入: 领域服务通常会依赖仓储或其他服务,这些依赖可以通过构造函数注入。

示例:OrderPlacementService

#include <memory> // For std::unique_ptr

// 前向声明仓储接口
class IOrderRepository;
class ICustomerRepository;
class IProductRepository; // 假设有商品仓储来获取商品信息

// 订单下单服务
class OrderPlacementService {
public:
    // 通过构造函数注入依赖的仓储
    OrderPlacementService(std::unique_ptr<IOrderRepository> orderRepo,
                          std::unique_ptr<ICustomerRepository> customerRepo,
                          std::unique_ptr<IProductRepository> productRepo)
        : m_orderRepo(std::move(orderRepo)),
          m_customerRepo(std::move(customerRepo)),
          m_productRepo(std::move(productRepo)) {}

    // 下单核心业务逻辑
    OrderId placeOrder(const CustomerId& customerId,
                       const std::vector<std::pair<ProductId, Quantity>>& items) {
        // 1. 验证客户是否存在 (通过客户仓储)
        // std::optional<Customer> customer = m_customerRepo->findById(customerId);
        // if (!customer) { throw std::invalid_argument("Customer not found."); }

        // 2. 创建新的订单聚合根
        OrderId newOrderId("ORDER-" + std::to_string(std::chrono::duration_cast<std::chrono::milliseconds>(
                                    std::chrono::system_clock::now().time_since_epoch()).count()));
        Order newOrder(newOrderId, customerId);

        // 3. 添加订单项并验证商品信息 (通过商品仓储)
        for (const auto& itemPair : items) {
            ProductId productId = itemPair.first;
            Quantity quantity = itemPair.second;
            // std::optional<Product> product = m_productRepo->findById(productId);
            // if (!product) { throw std::invalid_argument("Product not found: " + productId.value); }
            // Money unitPrice = product->getPrice(); // 从商品获取单价

            // 简化:假设单价已知,实际应从商品仓储获取
            Money unitPrice = Money::create(1000, CurrencyCode::USD); // 假设10.00 USD
            newOrder.addLineItem(productId, quantity, unitPrice);
        }

        // 4. 计算总价并进行其他业务逻辑 (例如库存检查,支付预授权等)
        Money total = newOrder.calculateTotal();
        // ... (此处可调用支付服务进行预授权)

        // 5. 将新订单持久化 (通过订单仓储)
        m_orderRepo->save(newOrder);

        // 6. 发布领域事件 (例如 OrderPlacedEvent)
        // EventBus::publish(OrderPlacedEvent(newOrderId, customerId, total));

        return newOrderId;
    }

private:
    std::unique_ptr<IOrderRepository> m_orderRepo;
    std::unique_ptr<ICustomerRepository> m_customerRepo;
    std::unique_ptr<IProductRepository> m_productRepo;
};

OrderPlacementService 不持有任何订单状态,它协调 CustomerProductOrder 聚合来完成下单过程,并利用仓储进行数据持久化。

5. 仓储 (Repositories) 的抽象与实现

仓储提供了一个抽象层,用于将领域模型与数据持久化机制解耦。在 C++ 中,通常通过抽象基类来定义仓储接口,然后提供具体的实现。

  • 接口定义: 使用纯虚函数定义仓储的接口(抽象基类),使其独立于具体的数据存储技术。
  • 操作聚合根: 仓储操作的对象始终是聚合根,而不是聚合内部的细节。它负责加载完整的聚合,并在修改后保存完整的聚合。
  • 返回智能指针或 std::optional 查找方法应返回 std::unique_ptr<AggregateRoot>std::optional<AggregateRoot>,以明确所有权语义或处理未找到的情况。

示例:IOrderRepository 接口与 InMemoryOrderRepository 实现

#include <map>
#include <vector>
#include <optional>

// IOrderRepository 接口
class IOrderRepository {
public:
    virtual ~IOrderRepository() = default;

    // 根据 OrderId 查找订单
    virtual std::optional<Order> findById(const OrderId& id) const = 0;

    // 保存订单 (通常是聚合根)
    virtual void save(const Order& order) = 0;

    // 移除订单
    virtual void remove(const OrderId& id) = 0;
};

// InMemoryOrderRepository 实现 (用于测试或简单场景)
class InMemoryOrderRepository : public IOrderRepository {
public:
    std::optional<Order> findById(const OrderId& id) const override {
        auto it = m_orders.find(id.value);
        if (it != m_orders.end()) {
            return it->second; // 返回副本
        }
        return std::nullopt;
    }

    void save(const Order& order) override {
        m_orders[order.getId().value] = order; // 存储副本
    }

    void remove(const OrderId& id) override {
        m_orders.erase(id.value);
    }

private:
    // 使用 map 模拟存储,键是 OrderId 的 string 表示
    // 实际应用中会考虑更高效的数据结构或持久化到数据库
    std::map<std::string, Order> m_orders;
};

// 假设还有 ICustomerRepository 和 IProductRepository
class ICustomerRepository { /* ... */ };
class IProductRepository { /* ... */ };

通过这种方式,OrderPlacementService 只需依赖 IOrderRepository 接口,而不需要关心订单是如何持久化的。这极大地提高了系统的可维护性和可测试性。

利用 C++ 特性表达业务生命周期规则

业务对象的生命周期往往伴随着状态的转换,并且在不同状态下,对象所能执行的操作也不同。C++ 提供了多种机制来表达和强制执行这些生命周期规则。

1. 状态模式 (State Pattern) 与类型安全状态机

状态模式是 DDD 中表达复杂生命周期规则的常用模式。它将对象的不同状态封装成独立的类,并允许对象在运行时改变其行为。C++ 的多态性非常适合实现这种模式。

  • 多态性: 将每个状态建模为一个继承自抽象基类的具体类,并通过虚函数实现状态特定的行为。
  • 状态转换: 状态类负责处理事件并决定下一个状态。
  • 编译期检查 (可选,使用 std::variantstd::visit): C++17 引入的 std::variant 可以表示一个对象在编译时只能是几种类型中的一种,结合 std::visit 可以提供更强的类型安全,确保所有可能的状态都被处理。

示例:Order 聚合的状态模式

我们之前在 Order 类中使用了 OrderStatus 枚举。现在,我们可以将其重构为更强大的状态模式。

// 抽象订单状态基类
class Order; // 前向声明 Order

class OrderState {
public:
    virtual ~OrderState() = default;
    virtual std::unique_ptr<OrderState> processPayment(Order& order) {
        throw std::logic_error("Payment not allowed in current state.");
    }
    virtual std::unique_ptr<OrderState> ship(Order& order) {
        throw std::logic_error("Shipping not allowed in current state.");
    }
    virtual std::unique_ptr<OrderState> cancel(Order& order) {
        throw std::logic_error("Cancellation not allowed in current state.");
    }
    virtual OrderStatus getStatus() const = 0; // 获取当前状态枚举值
};

// 具体状态:PendingOrderState
class PendingOrderState : public OrderState {
public:
    OrderStatus getStatus() const override { return OrderStatus::Pending; }

    std::unique_ptr<OrderState> processPayment(Order& order) override; // 定义在 Order.cpp 中
    std::unique_ptr<OrderState> cancel(Order& order) override;
};

// 具体状态:PaidOrderState
class PaidOrderState : public OrderState {
public:
    OrderStatus getStatus() const override { return OrderStatus::Paid; }

    std::unique_ptr<OrderState> ship(Order& order) override;
    std::unique_ptr<OrderState> cancel(Order& order) override;
};

// 具体状态:ShippedOrderState
class ShippedOrderState : public OrderState {
public:
    OrderStatus getStatus() const override { return OrderStatus::Shipped; }

    // Shipped 状态下不能支付、不能发货、不能取消
    // 默认行为就是抛出异常,所以无需重写
};

// 具体状态:CancelledOrderState
class CancelledOrderState : public OrderState {
public:
    OrderStatus getStatus() const override { return OrderStatus::Cancelled; }

    // Cancelled 状态下不能支付、不能发货、不能取消
    // 默认行为就是抛出异常,所以无需重写
};

// Order 聚合根 (更新为使用状态模式)
class Order {
public:
    Order(OrderId id, CustomerId customerId)
        : m_id(std::move(id)), m_customerId(std::move(customerId)),
          m_state(std::make_unique<PendingOrderState>()) {}

    // 可以在从仓储加载时使用这个构造函数,传入特定的状态
    Order(OrderId id, CustomerId customerId, OrderStatus status)
        : m_id(std::move(id)), m_customerId(std::move(customerId)) {
        setState(status); // 根据状态枚举设置具体状态对象
    }

    const OrderId& getId() const { return m_id; }
    const CustomerId& getCustomerId() const { return m_customerId; }
    OrderStatus getStatus() const { return m_state->getStatus(); } // 通过状态对象获取状态

    // ... addLineItem, updateLineItemQuantity, removeLineItem, calculateTotal 保持不变
    // 这些操作可能需要检查 m_state->getStatus() == OrderStatus::Pending

    // 业务行为:支付订单 (委托给当前状态对象)
    void markAsPaid() {
        m_state = m_state->processPayment(*this);
        // 发布 OrderPaidEvent
    }

    // 业务行为:发货 (委托给当前状态对象)
    void markAsShipped() {
        m_state = m_state->ship(*this);
        // 发布 OrderShippedEvent
    }

    // 业务行为:取消订单 (委托给当前状态对象)
    void cancel() {
        m_state = m_state->cancel(*this);
        // 发布 OrderCancelledEvent
    }

    // 设置状态的内部方法,用于从持久化加载时重建状态
    void setState(OrderStatus status) {
        switch (status) {
            case OrderStatus::Pending: m_state = std::make_unique<PendingOrderState>(); break;
            case OrderStatus::Paid: m_state = std::make_unique<PaidOrderState>(); break;
            case OrderStatus::Shipped: m_state = std::make_unique<ShippedOrderState>(); break;
            case OrderStatus::Cancelled: m_state = std::make_unique<CancelledOrderState>(); break;
            default: throw std::runtime_error("Unknown order status.");
        }
    }

private:
    OrderId m_id;
    CustomerId m_customerId;
    std::vector<OrderLineItem> m_lineItems; // 保持不变
    std::unique_ptr<OrderState> m_state; // 委托给状态对象处理
};

// 实现状态转换逻辑 (通常在 .cpp 文件中实现,以避免循环依赖)
std::unique_ptr<OrderState> PendingOrderState::processPayment(Order& order) {
    // 可以在这里添加更多业务逻辑,例如检查订单是否为空等
    if (order.getLineItems().empty()) {
        throw std::logic_error("Cannot pay for an empty order.");
    }
    std::cout << "Order " << order.getId() << " processed payment. Transitioning to Paid." << std::endl;
    return std::make_unique<PaidOrderState>();
}

std::unique_ptr<OrderState> PendingOrderState::cancel(Order& order) {
    std::cout << "Order " << order.getId() << " cancelled from Pending state." << std::endl;
    return std::make_unique<CancelledOrderState>();
}

std::unique_ptr<OrderState> PaidOrderState::ship(Order& order) {
    std::cout << "Order " << order.getId() << " shipped. Transitioning to Shipped." << std::endl;
    return std::make_unique<ShippedOrderState>();
}

std::unique_ptr<OrderState> PaidOrderState::cancel(Order& order) {
    std::cout << "Order " << order.getId() << " cancelled from Paid state. (Refund initiated)" << std::endl;
    // 可以在这里触发退款流程
    return std::make_unique<CancelledOrderState>();
}

通过状态模式,我们明确地将每个状态下的行为和转换规则封装起来,避免了大量的 if/else if 语句,使得业务逻辑更加清晰和可维护。

2. 策略模式 (Strategy Pattern) 与业务规则的灵活切换

策略模式允许我们定义一系列算法,将每一个算法封装起来,并使它们可以互相替换。这对于处理可变的业务规则非常有用,例如不同的折扣计算方式、运费计算策略等。

  • 抽象策略接口: 定义一个抽象基类或 std::function 作为策略接口。
  • 具体策略实现: 多个具体类实现该接口,封装不同的业务规则。
  • 运行时替换: 客户端代码可以在运行时选择并使用不同的策略。

示例:DiscountCalculationStrategy

#include <functional>

// 抽象折扣计算策略接口
class IDiscountCalculationStrategy {
public:
    virtual ~IDiscountCalculationStrategy() = default;
    virtual Money calculateDiscount(const Order& order) const = 0;
};

// 具体策略:无折扣
class NoDiscountStrategy : public IDiscountCalculationStrategy {
public:
    Money calculateDiscount(const Order& order) const override {
        // 假设 Money 构造函数允许 0
        return Money::create(0, order.getLineItems().empty() ? CurrencyCode::USD : order.getLineItems()[0].getUnitPrice().getCurrency());
    }
};

// 具体策略:百分比折扣
class PercentageDiscountStrategy : public IDiscountCalculationStrategy {
public:
    PercentageDiscountStrategy(double percentage) : m_percentage(percentage) {
        if (percentage < 0 || percentage > 1.0) {
            throw std::invalid_argument("Percentage must be between 0 and 1.");
        }
    }
    Money calculateDiscount(const Order& order) const override {
        Money total = order.calculateTotal();
        long long discountCents = static_cast<long long>(total.getAmountInCents() * m_percentage);
        return Money::create(discountCents, total.getCurrency());
    }
private:
    double m_percentage;
};

// Order 类可能需要一个方法来应用折扣,或者在 OrderPlacementService 中使用
// 假设 OrderPlacementService 需要应用折扣
// 在 OrderPlacementService 中可以这样使用:
/*
    // 在 placeOrder 方法中
    std::unique_ptr<IDiscountCalculationStrategy> discountStrategy;
    // 根据业务规则选择策略,例如:
    if (customer.isPremium()) {
        discountStrategy = std::make_unique<PercentageDiscountStrategy>(0.10); // 10% 折扣
    } else {
        discountStrategy = std::make_unique<NoDiscountStrategy>();
    }

    Money discount = discountStrategy->calculateDiscount(newOrder);
    // Money finalTotal = newOrder.calculateTotal() - discount;
    // ...
*/

策略模式使得折扣计算逻辑与核心订单逻辑解耦,我们可以轻松添加新的折扣类型而无需修改 Order 类本身。

3. 领域事件 (Domain Events) 的发布与订阅

领域事件是业务中已发生的、重要的事实,用于通知其他限界上下文或系统组件。C++ 可以通过观察者模式或事件总线机制实现领域事件。

  • 事件对象: 将事件本身建模为不可变的值对象,包含事件发生时的所有相关信息。
  • 事件发布器: 聚合根或领域服务在完成业务操作后发布事件。
  • 事件订阅器: 其他服务或聚合订阅感兴趣的事件,并在事件发生时执行相应逻辑。

示例:一个简单的事件总线

#include <vector>
#include <map>
#include <any> // C++17 for generic event data
#include <typeindex> // For mapping event types

// 抽象领域事件基类
class DomainEvent {
public:
    virtual ~DomainEvent() = default;
    // 可能包含事件时间戳、事件ID等通用属性
};

// 具体领域事件:订单已支付
class OrderPaidEvent : public DomainEvent {
public:
    OrderPaidEvent(OrderId orderId, Money amount)
        : m_orderId(std::move(orderId)), m_amount(std::move(amount)) {}
    const OrderId& getOrderId() const { return m_orderId; }
    const Money& getAmount() const { return m_amount; }
private:
    OrderId m_orderId;
    Money m_amount;
};

// 具体领域事件:订单已发货
class OrderShippedEvent : public DomainEvent {
public:
    OrderShippedEvent(OrderId orderId, CustomerId customerId)
        : m_orderId(std::move(orderId)), m_customerId(std::move(customerId)) {}
    const OrderId& getOrderId() const { return m_orderId; }
    const CustomerId& getCustomerId() const { return m_customerId; }
private:
    OrderId m_orderId;
    CustomerId m_customerId;
};

// 事件处理器接口
template<typename TEvent>
class IEventHandler {
public:
    virtual ~IEventHandler() = default;
    virtual void handle(const TEvent& event) = 0;
};

// 简单的事件总线实现
class EventBus {
public:
    // 注册处理器
    template<typename TEvent>
    static void subscribe(IEventHandler<TEvent>* handler) {
        getHandlers<TEvent>().push_back([handler](const DomainEvent& event) {
            handler->handle(static_cast<const TEvent&>(event));
        });
    }

    // 发布事件
    template<typename TEvent>
    static void publish(const TEvent& event) {
        for (const auto& handlerFunc : getHandlers<TEvent>()) {
            handlerFunc(event);
        }
    }

private:
    // 存储不同事件类型的处理器函数
    static std::map<std::type_index, std::vector<std::function<void(const DomainEvent&)>>> s_handlers;

    template<typename TEvent>
    static std::vector<std::function<void(const DomainEvent&)>>& getHandlers() {
        return s_handlers[std::type_index(typeid(TEvent))];
    }
};

// 静态成员初始化
std::map<std::type_index, std::vector<std::function<void(const DomainEvent&)>>> EventBus::s_handlers;

// 示例订阅者:库存服务
class InventoryService : public IEventHandler<OrderShippedEvent> {
public:
    void handle(const OrderShippedEvent& event) override {
        std::cout << "InventoryService received OrderShippedEvent for Order "
                  << event.getOrderId() << ". Adjusting stock for customer "
                  << event.getCustomerId() << std::endl;
        // 实际业务逻辑:减少库存
    }
};

// 示例订阅者:通知服务
class NotificationService : public IEventHandler<OrderPaidEvent> {
public:
    void handle(const OrderPaidEvent& event) override {
        std::cout << "NotificationService received OrderPaidEvent for Order "
                  << event.getOrderId() << ". Sending payment confirmation email." << std::endl;
        // 实际业务逻辑:发送邮件
    }
};

通过领域事件,我们可以实现不同限界上下文之间的松耦合通信,例如,订单服务发布 OrderShippedEvent,库存服务和客户通知服务分别订阅并处理该事件。

4. 模板元编程 (TMP) 与编译期规则验证 (高级话题)

C++ 的模板元编程可以在编译时执行计算和类型转换。对于 DDD 而言,它提供了一种在编译期强制执行某些业务规则(不变式)的极端方式。

  • 类型特征 (Type Traits): 定义编译期检查,例如 static_assert 结合 std::is_base_of 或自定义类型特征,确保某些类型符合特定约束。
  • 编译期计算: 在编译期根据类型参数计算出结果,例如根据不同货币类型推导出不同的精度。

示例:限制货币类型组合

假设我们有一个业务规则:不同货币的 Money 对象不能直接相加。虽然我们已经在 Money::operator+ 中做了运行时检查,但如果能强制在编译时就报错,那将更安全。

// 假设 Money 模板化以包含 CurrencyCode
template<CurrencyCode C>
class MoneyT {
public:
    static MoneyT<C> create(long long amountInCents) {
        if (amountInCents < 0) { throw std::invalid_argument("Amount cannot be negative."); }
        return MoneyT<C>(amountInCents);
    }
    long long getAmountInCents() const { return m_amountInCents; }
    CurrencyCode getCurrency() const { return C; }

    // 只有相同货币类型的 MoneyT 才能相加
    MoneyT<C> operator+(const MoneyT<C>& other) const {
        return MoneyT<C>(m_amountInCents + other.m_amountInCents);
    }
    // 注意:这里不再需要运行时检查货币类型是否相同,因为编译时就已通过类型参数 C 保证了
private:
    long long m_amountInCents;
    MoneyT(long long amountInCents) : m_amountInCents(amountInCents) {}
};

// 使用示例
// MoneyT<CurrencyCode::USD> usd1 = MoneyT<CurrencyCode::USD>::create(1000);
// MoneyT<CurrencyCode::USD> usd2 = MoneyT<CurrencyCode::USD>::create(500);
// MoneyT<CurrencyCode::USD> usdTotal = usd1 + usd2; // OK

// MoneyT<CurrencyCode::EUR> eur1 = MoneyT<CurrencyCode::EUR>::create(2000);
// MoneyT<CurrencyCode::USD> invalid = usd1 + eur1; // 编译错误!
                                                // error: invalid operands to binary expression ('MoneyT<CurrencyCode::USD>' and 'MoneyT<CurrencyCode::EUR>')

这种方法虽然强大,但会显著增加代码的复杂性,且可能导致冗长的编译错误信息。因此,应谨慎使用,仅限于那些绝对不能在运行时出错的核心不变式。对于大多数业务规则,运行时异常或 std::optional 结合清晰的错误处理已经足够。

C++ DDD 中的常见挑战与最佳实践

在 C++ 中实践 DDD 并非没有挑战,但通过一些最佳实践可以有效地应对。

1. 性能考量与 DDD 的平衡

DDD 通常倾向于创建更多的小对象(值对象、实体),这可能带来一定的内存开销和间接访问成本。

  • 值对象的复制开销: 频繁复制大型值对象可能影响性能。对于只读访问,应优先使用 const& 传递。对于需要修改(通常是创建新值对象)的场景,使用移动语义 (std::move) 可以减少复制。
  • 聚合的粒度: 聚合不宜过大,否则会影响并发性能和数据加载效率。但也不宜过小,否则无法维护自身的一致性。找到合适的边界是关键。
  • 内存管理: 智能指针(std::unique_ptrstd::shared_ptr)是管理领域对象生命周期的首选,但要警惕 std::shared_ptr 可能导致的循环引用。

2. 复杂性管理

DDD 引入了大量概念和模式,C++ 代码本身也可能很复杂。

  • 命名空间与模块化: 合理使用命名空间 (namespace) 隔离不同的限界上下文和领域概念。C++20 的模块 (modules) 将进一步提升模块化能力。
  • 清晰的目录结构: 按照限界上下文、领域概念(实体、值对象、服务、仓储)组织代码目录。
  • 单元测试与集成测试: 编写全面的测试是确保业务逻辑正确性的关键。单元测试验证单个领域对象的行为,集成测试验证多个领域对象或跨限界上下文的交互。
  • 文档: 领域模型、通用语言和业务规则的清晰文档至关重要,特别是对于 C++ 这种代码表现力可能不如某些高级语言直观的语言。

3. 与外部系统的集成

领域模型是纯粹的业务逻辑,不应直接与外部系统(如数据库、消息队列、UI)耦合。

  • 防腐层 (Anti-Corruption Layer – ACL): 在领域模型和外部系统之间建立一个转换层。ACL 负责将外部系统的模型转换为领域模型,反之亦然,以保护领域模型免受外部系统细节的“污染”。
  • 适配器模式: 仓储、领域服务通常通过适配器与外部技术(如数据库驱动、HTTP 客户端)进行交互。

4. 代码生成与元编程的权衡

对于一些重复性的代码(例如值对象的 operator==operator!=、哈希函数等),可以考虑使用代码生成工具或更高级的 C++ 元编程技术。

  • 代码生成: 使用 Python 脚本或类似的工具,根据简单的声明生成冗余的 C++ 代码。这在编译时提供性能,同时减少手动编写的工作量。
  • 元编程: 谨慎使用模板元编程来自动化某些模式,但要权衡其引入的复杂性。C++20 的 Concepts 可以简化模板的使用。

案例研究:一个简单的电子商务订单系统

让我们通过一个简化的电子商务订单系统来展示上述概念的综合应用。我们将关注核心的订单流程。

#include <iostream>
#include <vector>
#include <string>
#include <memory>
#include <map>
#include <algorithm>
#include <stdexcept>
#include <optional>
#include <functional>
#include <chrono>
#include <typeindex>

// 省略前面定义的所有 Value Objects 和 Enums (Money, CurrencyCode, ProductId, Quantity, CustomerId, OrderId, OrderStatus, EmailAddress)
// 以及 OrderLineItem 类

// OrderState 和具体的 OrderState 实现 (PendingOrderState, PaidOrderState, ShippedOrderState, CancelledOrderState)
// 以及 Order 聚合根 (包含 OrderState 逻辑)

// IOrderRepository 和 InMemoryOrderRepository 实现

// EventBus 和相关事件 (DomainEvent, OrderPaidEvent, OrderShippedEvent)
// 以及事件处理器 (InventoryService, NotificationService)

// --- 模拟外部依赖 ---
class MockCustomerRepository {
public:
    std::optional<Customer> findById(const CustomerId& id) const {
        if (id.value == "CUST-001") {
            return Customer(id, "Alice Smith", *EmailAddress::create("[email protected]"));
        }
        return std::nullopt;
    }
};

class MockProductRepository {
public:
    std::optional<std::tuple<std::string, Money>> getProductInfo(const ProductId& id) const {
        if (id.value == "PROD-A") {
            return std::make_tuple("Laptop", Money::create(120000, CurrencyCode::USD)); // $1200.00
        }
        if (id.value == "PROD-B") {
            return std::make_tuple("Mouse", Money::create(2500, CurrencyCode::USD));    // $25.00
        }
        return std::nullopt;
    }
};

// --- 订单服务,现在使用实际的仓储和事件总线 ---
class OrderPlacementService {
public:
    OrderPlacementService(std::unique_ptr<IOrderRepository> orderRepo,
                          std::unique_ptr<MockCustomerRepository> customerRepo, // 模拟客户仓储
                          std::unique_ptr<MockProductRepository> productRepo)   // 模拟商品仓储
        : m_orderRepo(std::move(orderRepo)),
          m_customerRepo(std::move(customerRepo)),
          m_productRepo(std::move(productRepo)) {}

    OrderId placeOrder(const CustomerId& customerId,
                       const std::vector<std::pair<ProductId, Quantity>>& items) {
        // 1. 验证客户
        if (!m_customerRepo->findById(customerId)) {
            throw std::invalid_argument("Customer not found: " + customerId.value);
        }

        // 2. 创建订单
        OrderId newOrderId("ORDER-" + std::to_string(std::chrono::duration_cast<std::chrono::milliseconds>(
                                    std::chrono::system_clock::now().time_since_epoch()).count()));
        Order newOrder(newOrderId, customerId);

        // 3. 添加订单项并验证商品
        for (const auto& itemPair : items) {
            ProductId productId = itemPair.first;
            Quantity quantity = itemPair.second;
            auto productInfo = m_productRepo->getProductInfo(productId);
            if (!productInfo) {
                throw std::invalid_argument("Product not found: " + productId.value);
            }
            Money unitPrice = std::get<1>(*productInfo);
            newOrder.addLineItem(productId, quantity, unitPrice);
        }

        // 4. 计算总价
        Money total = newOrder.calculateTotal();
        std::cout << "Order " << newOrderId << " total: " << total << std::endl;

        // 5. 将新订单持久化
        m_orderRepo->save(newOrder);

        // 6. 发布 OrderPlacedEvent (简化,此处未定义)
        std::cout << "Order " << newOrderId << " placed successfully." << std::endl;

        return newOrderId;
    }
private:
    std::unique_ptr<IOrderRepository> m_orderRepo;
    std::unique_ptr<MockCustomerRepository> m_customerRepo;
    std::unique_ptr<MockProductRepository> m_productRepo;
};

// Main 函数,模拟业务流程
int main() {
    // 注册事件处理器
    InventoryService inventoryService;
    NotificationService notificationService;
    EventBus::subscribe(&inventoryService);
    EventBus::subscribe(&notificationService);

    // 初始化仓储和服务
    auto orderRepo = std::make_unique<InMemoryOrderRepository>();
    auto customerRepo = std::make_unique<MockCustomerRepository>();
    auto productRepo = std::make_unique<MockProductRepository>();

    OrderPlacementService orderPlacementService(
        std::move(orderRepo), std::move(customerRepo), std::move(productRepo));

    // 模拟客户和商品
    CustomerId customerId("CUST-001");
    ProductId productA("PROD-A");
    ProductId productB("PROD-B");

    try {
        // 1. 客户下单
        std::vector<std::pair<ProductId, Quantity>> items;
        items.emplace_back(productA, Quantity::create(1));
        items.emplace_back(productB, Quantity::create(2));

        OrderId order1Id = orderPlacementService.placeOrder(customerId, items);
        std::cout << "--- " << std::endl;

        // 从仓储加载订单
        std::optional<Order> order1 = orderPlacementService.m_orderRepo->findById(order1Id);
        if (order1) {
            std::cout << "Order 1 Status: " << static_cast<int>(order1->getStatus()) << std::endl; // Pending
            order1->markAsPaid(); // 支付订单
            std::cout << "Order 1 Status after payment: " << static_cast<int>(order1->getStatus()) << std::endl; // Paid
            EventBus::publish(OrderPaidEvent(order1->getId(), order1->calculateTotal())); // 发布支付事件

            order1->markAsShipped(); // 发货
            std::cout << "Order 1 Status after shipping: " << static_cast<int>(order1->getStatus()) << std::endl; // Shipped
            EventBus::publish(OrderShippedEvent(order1->getId(), order1->getCustomerId())); // 发布发货事件

            // 尝试对已发货订单取消,应该抛出异常
            try {
                order1->cancel();
            } catch (const std::logic_error& e) {
                std::cout << "Attempted to cancel shipped order: " << e.what() << std::endl;
            }
            orderPlacementService.m_orderRepo->save(*order1); // 保存修改后的订单状态
        }

        std::cout << "--- " << std::endl;

        // 2. 尝试下空订单
        try {
            std::vector<std::pair<ProductId, Quantity>> emptyItems;
            orderPlacementService.placeOrder(customerId, emptyItems);
        } catch (const std::logic_error& e) {
            std::cout << "Attempted to place empty order: " << e.what() << std::endl;
        }

        std::cout << "--- " << std::endl;

        // 3. 客户再次下单,这次取消
        std::vector<std::pair<ProductId, Quantity>> items2;
        items2.emplace_back(productA, Quantity::create(1));
        OrderId order2Id = orderPlacementService.placeOrder(customerId, items2);

        std::optional<Order> order2 = orderPlacementService.m_orderRepo->findById(order2Id);
        if (order2) {
            std::cout << "Order 2 Status: " << static_cast<int>(order2->getStatus()) << std::endl; // Pending
            order2->cancel(); // 取消订单
            std::cout << "Order 2 Status after cancellation: " << static_cast<int>(order2->getStatus()) << std::endl; // Cancelled
            orderPlacementService.m_orderRepo->save(*order2);
        }

    } catch (const std::exception& e) {
        std::cerr << "An error occurred: " << e.what() << std::endl;
    }

    return 0;
}

运行输出示例 (部分):

Order Order-1701340000000 total: 1250.00 USD
Order Order-1701340000000 placed successfully.
--- 
Order 1 Status: 0
Order Order-1701340000000 processed payment. Transitioning to Paid.
Order 1 Status after payment: 1
NotificationService received OrderPaidEvent for Order Order-1701340000000. Sending payment confirmation email.
Order Order-1701340000000 shipped. Transitioning to Shipped.
Order 1 Status after shipping: 2
InventoryService received OrderShippedEvent for Order Order-1701340000000. Adjusting stock for customer CustomerId(CUST-001)
Attempted to cancel shipped order: Cancellation not allowed in current state.
--- 
Attempted to place empty order: Cannot pay for an empty order.
--- 
Order Order-1701340000001 total: 1200.00 USD
Order Order-1701340000001 placed successfully.
Order 2 Status: 0
Order Order-1701340000001 cancelled from Pending state.
Order 2 Status after cancellation: 3

(注:实际 OrderId 中的时间戳会变化,CurrencyCode 打印为 int 是简化处理。)

这个案例展示了 C++ 如何通过强类型系统(Money, OrderId 等值对象)、封装(Order 聚合根)、多态(状态模式)和事件机制来表达复杂的业务规则和生命周期。编译时和运行时相结合的验证,确保了业务逻辑的健壮性。

C++ DDD 的未来展望

C++ 语言本身也在不断演进,新的标准特性将为 DDD 实践带来更多可能性:

  • C++20 Modules: 模块化将极大地改善大型 C++ 项目的编译时间,并更好地封装限界上下文,减少头文件依赖带来的耦合。
  • C++20 Concepts: Concepts 允许我们对模板参数进行语义约束,使模板编程更加清晰和错误友好。这对于编写通用且类型安全的值对象和泛型算法将非常有益。
  • 协程 (Coroutines): 对于涉及复杂异步操作(如跨服务调用、长事务)的领域服务,协程可以简化异步代码的编写,使其更像同步代码,从而更容易理解业务流程。

结合这些现代 C++ 特性,C++ 中的 DDD 将变得更加优雅、更具表现力,同时继续保持其在性能和控制方面的独特优势。

结语

C++ 在领域驱动设计中的应用,证明了其不仅仅是一种高性能的系统编程语言,更是一种能够精确表达复杂业务逻辑的强大工具。通过充分利用其强类型系统、面向对象特性以及现代 C++ 标准的演进,开发者能够在编译时和运行时共同保障业务不变式和生命周期规则的正确性,从而构建出既高性能又具备高业务准确性和可维护性的复杂软件系统。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注