Executable domain-driven design: documenting a price model
Let's consider a problem that might appear in a retail system: modeling prices. Example-driven development tutorial offers a tutorial on this subject. Here we want to look at how we can communicate the details as soon as a model like this is already in place.
Prerequisites
First, we need the concept of money. This is an example of such an implementation. See :
bagWithEurosAndDollars <gtExample> | bag | bag := self fortyTwoEuros + self fortyTwoDollars. self assert: (bag isKindOf: GtDMoneyBag). self assert: (bag monies size = 2). ^ bag
But prices are more than money. Prices, for example, can be discounted in different ways. Two such discounts can be by percentage or by a fix amount. These discounts can be added arbitrarily and in combination.
Kinds of prices
The simplest kind of price is one specified concretely by money.
concretePrice <gtExample> | price | price := 100 gtEUR asPrice. self assert: price = 100 gtEUR asPrice. ^ price
Any discounted price eventually relies on a concrete base price. A discounted price can be by an amount:
concretePriceDiscountedByMoney <gtExample> | price discountedPrice | price := self concretePrice. discountedPrice := price discountedBy: 10 gtEUR. self assert: discountedPrice = 90 gtEUR asPrice. ^ discountedPrice
Or it can be by percentage:
concretePriceDiscountedByPercentage <gtExample> | price | price := GtDPriceDiscountedByPercentage new price: self concretePrice; discountPercentage: 10 percent. self assert: price money = 90 gtEUR. ^ price
And of course, the discounts can be combined:
concretePriceDiscountedByMoneyAndDiscountedByPercentage <gtExample> | price discountedPrice | price := self concretePriceDiscountedByMoney. discountedPrice := price discountedBy: 10 percent. self assert: discountedPrice = 81 gtEUR asPrice. ^ discountedPrice