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