【问题标题】:Shopping context modelling购物环境建模
【发布时间】:2014-10-03 10:47:57
【问题描述】:

我想以 DDD 的方式编写我的第一个应用程序(电子商务),我想知道我是否一切正常,所以我希望您对我的建模提出意见 - 如何改进它,应该如何改进我正在寻找等。每一个反馈将不胜感激。

我已将我的应用程序划分为几个有界上下文(目录、购物、订购),下面的示例基于购物上下文。我也在尝试使用 CQRS(因此是 AddProductToCart 命令)。

我的业务需求如下:

  • 客户应该能够将产品添加到他的购物车
  • 添加产品的价格应针对用户,并基于以下几个因素:(全球折扣、用户折扣和客户所在国家/地区(某些国家/地区降低了基本价格)

我认识了以下参与者(请注意方法和类的 cmets):

/**
 * A customer (Aggregate Root) having an unique cart
 */
class Customer
{
    /**
     * @var int
     */
    private $customerId;

    /**
     * @var string
     */
    private $country;

    /**
     * @var Cart
     */
    private $cart;

    /**
     * @var PriceProvider
     */
    private $priceProvider;

    /**
     * Adds product to customers cart with user-specific price
     *
     * @param $productId
     * @return CartLine
     */
    public function addProductToCart($productId)
    {
        $price = $this->priceProvider->priceForProduct($productId, $this->customerId, $this->country);
        return $this->cart->addLine($productId, $price);
    }
}

/**
 * Simple CartLine object for persisting purposes
 */
class CartLine
{
    public $productId;
    public $price;
    public $cartId;

    function __construct($cartId, $productId, $price)
    {
        $this->cartId    = $cartId;
        $this->price     = $price;
        $this->productId = $productId;
    }
}

class Cart
{
    private $cartId;

    public function addLine($productId, $price)
    {
        return new CartLine($this->cartId, $productId, $price);
    }
}

/**
 * Provides price for specific country
 */
class PriceProvider
{
    public function priceForProduct($productId, $userId, $country)
    {
        // Logic for determining product price for customer
        // Based on available global discounts, user discounts and country
    }
}

/**
 * Command for adding product to cart
 */
class AddProductToCart
{
    public $customerId;
    public $productId;
}

/**
 * An application service to bind everything together
 */
class CustomerService
{
    public function addProductToCart(AddProductToCart $command)
    {
        /** @var Customer $customer */
        $customer = $this->customerRepository->customerOfId($command->customerId);
        $cartLine = $customer->addProductToCart($command->productId);

        $this->cartLineRepository->save($cartLine);
    }
}

这是正确的方法吗?我在这里违反了任何 DDD 原则吗?我可以改进吗?

【问题讨论】:

  • 你也需要一个事件:AddProductToCartCommand-->ProductAddedToCartEvent
  • 只是出于好奇,这是打算作为学习应用还是生产应用?
  • @Sudarshan,它可能最终会成为小型生产设备。 ;)
  • 你为什么要问?我应该改变什么吗?

标签: oop design-patterns domain-driven-design modeling cqrs


【解决方案1】:

CQRS 通常涉及命令处理程序
命令与“命令式”动词相关联。
CustomerService 过于宽泛,因此不代表“命令”的意图。

我会将CustomerService 更改为AddProductToCart

class AddProductToCart
{
    public function handle(AddProductToCartCommand $command) {
      ...
    }
}

顺便说一句,通过将所有不同的功能拆分为适当的命令,如果需要,您可以制作一个特定命令的多个版本。

【讨论】:

  • 你说得对,我应该在那里使用处理程序。我假设这个处理程序应该是域而不是应用层的一部分?
  • 应用层,它还是一个应用服务
【解决方案2】:

所以按稀疏顺序(部分取决于编程语言):

  • 应该明确标记您的聚合根(例如使用 IAggregateRoot 接口),因此如果您的语言支持泛型,您可以只实现聚合根的存储库。
  • 您的构造函数应该始终是私有的,而是使用工厂。仅将构造函数用于初始化问题。在您的下一个未来,Customer 的构建规则可能会增加复杂性,而工厂将为您提供灵活性。
  • 不变逻辑是域逻辑。取决于用例的逻辑是应用程序逻辑(即PriceProvider 与域逻辑无关)

【讨论】:

  • 如何确定哪些规则适用于哪个产品来确定其价格而不是域逻辑?也许它不应该驻留在这个 AR 中(但如果不是,那么在哪里?),但对我来说这绝对是一个领域层。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-10-19
  • 1970-01-01
  • 1970-01-01
  • 2011-11-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多