Product Class Responsibility

In my company we have a very specific pricing strategy:

  • Each Productin our catalog has baseUsdPrice, for example, the Foo product has a base price of USD 9.99 $.
  • This does not necessarily mean that you will pay you $ 9.99. We check your country except the price - for example, GB Foo costs $ 8.99
  • Then you can choose the currency you want to pay - if you are in GB, you can pay either in US dollars (mentioned $ 8.99) or in local currency (in this case GBP).
  • If you decide to pay your local currency, we will calculate the equivalent of $ 8.99 for British pounds based on a fixed price matrix (for example, there will be £ 3.99).
  • This is the price you pay.

How should I create my main array Productin DDD in order to be clean, connected, decoupled, and easy to change?

  • Should it paymentPricebe calculated by the domain service and its result placed as part of the unit Product? If that means mine ProductRepositorywill have methods likeproduct(productId, countryId, currency)
  • Should I put all the calculations in a domain service class PaymentPriceCalculatorand use a visitor template, for example getPaymentPrice(Country, Currency)? What if I need to use paymentPricefrom my object to perform some business rule checks?

I try to circle my head around and I think I overdid it and it hurts.;)

+4
source share
4 answers

PaymentPriceCalculator. , , - .

  • Product, . . , 2 "", ?

  • .

, $ . , , , , . , / .. .

+2

, , :

interface ProductPriceCalculator {

    public function determineProductPrice($productId, Currency $currency = null);
}

basePrice, , :

class Product {

    private $id;

    /**
     * Initially base price
     * @var Money
     */
    private $price;

    public function modifyPrice(ProductPriceCalculator $calculator, Currency $currency = null) {
        $newPrice = $calculator->determineProductPrice($this->id, $currency);

        if ($newPrice !== null) {
            $this->price = $newPrice;
        }
    }

}

, . , , , :

class Country implements ProductPriceCalculator {

    private $id;

    /**
     * @var Currency
     */
    private $currency;

    /**
     * Hashmap<String, Money> where product id evaluates to price in $this country
     * @var array
     */
    private $productPrices = array();

    /**
     * @param string $productId
     * @param Currency $currency
     * @return Money
     */
    public function determineProductPrice($productId, Currency $currency = null) {
        if (array_key_exists($productId, $this->productPrices)) {
            $productPrice = clone $this->productPrices[$productId];

            if ($currency !== null) {
                $currency = $this->currency;
            }

            return $productPrice->convertTo($currency);
        }
    }

}

:

class Money {

    private $value;
    private $currency;

    public function __construct($amount, Currency $currency) {
        $this->value = $amount;
        $this->currency = $currency;
    }

    public function convertTo(Currency $newCurrency) {
        if (!$this->currency->equalTo($newCurrency)) {
            $this->value *= $newCurrency->ratioTo($this->currency);
            $this->currency = $newCurrency;
        }
    }

}

class Currency {

    private $code;
    private static $conversionTable = array();

    public function equalTo(Currency $currency) {
        return $this->code == $currency->code;
    }

    public function ratioTo(Currency $currency) {
        return self::$conversionTable[$this->code . '-' . $currency->code];
    }

}

, , :

class SomeClient {

    public function someAction() {
        //initialize $product (it has the base price)
        //initialize $selectedCountry with prices for this product
        //initialize $selectedCurrency

        $product->modifyPrice($selectedCountry, $selectedCurrency);

        //here $product contains the price for that country
    }

}
0

@dkatzel, .

, :

product.paymentPrice(countryId, currency, fixedPriceMatrix)

, , .

countryId = ...
currency = ...
fixedPriceMatrix = ...
basePrice = ...
countryPrice = ...
product = new Product(id, basePrice, countryPrice...)
paymentPrice = product.paymentPrice(countryId, currency, fixedPriceMatrix)

( ), .

Value PaymentPrice

//in unit test
paymentPrice = new PaymentPrice(basePriceForYourCountry, currency, fixedPriceMatrix)
value = paymentPrice.value()

//the product now holds countryPrice calculation
countryPrice = new Product(id, basePrice).countryPrice(countryId);

PaymentPriceCalculator factory:

class PaymentPriceCalculator {
    PaymentPrice paymentPrice(product, countryId, currency) {
        fixedPriceMatrix = fixedPriceMatrixStore.get()
        return new PaymentPrice(product.countryPrice(countryId), currency, fixedPriceMatrix())
    }
}

:

  • ( PaymentPrice )

  • . . countryId, .

0

, .

, , , .

-

-

, , Factory Composite, , .

0

All Articles