残りのステップの実装
価格設定のステップ
再掲
code:fsharp
type GetProductPrice = ProductCode -> Price
type PriceOrder =
GetProductPrice // 依存関係
-> ValidatedOrder // 入力
-> PricedOrder // 出力
実装
code:fsharp
let priceOrder: PriceOrder =
fun getProductPrice validatedOrder ->
let lines =
validatedOrder.Lines
|> List.map (toPricedOrderLine getProductPrice)
let amountToBill =
lines
|> List.map (fun line -> line.LinePrice) // 価格のリストを生成する
|> BillingAmount.sumPrices // 合計して請求総額にする
let pricedOrder: PricedOrder = {
OrderId = validatedOrder.OrderId
CustomerInfo = validatedOrder.CustomerInfo
ShippingAddress = validatedOrder.ShippingAddress
BillingAddress = validatedOrder.BillingAddress
Lines = lines
AmountToBill = amountToBill
}
pricedOrder
2 つのヘルパ関数
BillingAmount.sumPrices
Price のリストを合計し、BillingAmount 型にラップして返すヘルパ関数
code:fsharp
// 価格のリストを合計して請求総額にする
// 合計が範囲外の場合は例外を発生させる (BillinAmount.create)
let sumPrices prices =
let totael = prices |> List.map Price.value |> List.sum
create total
なぜ Price ではなく BillingAmount 型か
Price と異なる検証ルールを持つ可能性があるので別の型として定義
toPricedOrderLine
1 行のみを変換するヘルパ関数
code:fsharp
let toPricedOrderLine getProductPrice (line: ValidatedOrderLine): PricedOrderLine =
let qty = lines.Quantity |> OrderQuantity.value
let price = lines.ProductCode |> getProductPrice
let linePrice = price |> Price.multiply qty
{
OrderLineId = line.OrderLineId
ProductCode = line.ProductCode
Quantity = line.Quantity
LinePrice = linePrice
}
Price.multiply ヘルパ関数
Price に数量をかけ合わせた値(Price)を返す
code:fsharp
let multiple qty (Price p) =
create (qty * p)
注文確認のステップ
再掲
code:fsharp
type HtmlString = HtmlString of string
type CreateOrderAcknowledgmentLetter = PricedOrder -> HtmlString
type OrderAcknowledgment = {
EmailAddress: EmailAddress
Letter: HtmlString
}
type SendResult = Sent | NotSent
type SendOrderAcknowledgment =
OrderAcknowledgment -> SendResult
type OrderAcknowledgmentSent = {
OrderId: OrderId
EmailAddress: EmailAddress
}
type AcknowledgeOrder =
CreateOrderAcknowledgmentLetter // 依存関係
-> SendOrderAcknowledgment // 依存関係
-> PricedOrder // 入力
-> OrderAcknowledgmentSent option // 出力
実装
code:fsharp
let acknowledgeOrder: AcknowledgeOrder =
fun createOrderAcknowledgmentLetter sendOrderAcknowledgment pricedOrder ->
let letter = createOrderAcknowledgmentLetter pricedOrder
let acknowledgment = {
EmailAddress = pricedOrder.CustomerInfo.EmailAddress
Letter = letter
}
match sendOrderAcknowledgment acknowledgment with
// 確認書が正常に送信された場合は対応するイベントを返す
| Sent ->
let event = {
OrderId = pricedOrder.OrderId
EmailAddress = pricedOrder.CustomerInfo.EmailAddress
}
Some event
// そうでない None を返す
| NotSent -> None
依存関係(sendOrderAcknowledgment )は後で考えても、コードの大部分はビルドして組み立てられるので問題ない
イベントの作成
再掲
code:fsharp
type OrderPlaced = PricedOrder
type BillableOrderPlaced = {
OrderId: OrderId
BillingAddress: Address
AmountToBill: BillingAmount
}
type PlaceOrderEvent =
| OrderPlaced of OrderPlaced
| BillableOrderPlaced of BillableOrderPlaced
| OrderAcknowledgmentSent of OrderAcknowledgmentSent
「請求額が 0 より大きい場合のみ請求イベントを送信する」という仕様を足す
これにより、CreateEvents の定義は以下のようになる
code:fsharp
type CreateEvents =
PricedOrder
-> OrderAcknowledgmentSent option
-> PlaceOrderEvent list
実装
createBillingEvent
BillableOrderPlaced イベントを作成するヘルパ関数
送信しないこともあるので、戻り値は option にする
code:fsharp
let createBillingEvent (pricedOrder: PricedOrder) : BillableOrderPlaced option =
let billingAmount = pricedOrder.AmountToBill |> BillingAmount.value
if billingAmount > 0M then
let order = {
OrderId = pricedOrder.OrderId
BillingAddress = pricedOrder.BillingAddress
AmountToBill = pricedOrder.AmountToBill
}
order
else
None
戻り値の構築
code:fsharp
let createEvents: CreateEvents =
fun pricedOrder orderAcknowledgmentSentOpt ->
let event1 =
pricedOrder
// 共通の選択型に変換する
|> PlaceOrderEvent.OrderPlaced
let event2opt =
orderAcknowledgmentSentOpt
// 共通の選択型に変換する
|> Option.map PlaceOrderEvent.OrderAcknowledgmentSent
let event3opt =
pricedOrder
|> createBillingEvent
// 共通の選択型に変換する
|> Option.map PlaceOrderEvent.BillableOrderPlaced
option 型は Option.map を用いて変換する
ここでは list
戻り値が list だから ?? radish-miyazaki.icon option を list に変換するヘルパ関数を準備する
code:fsharp
let listOfOption opt =
match opt with
| None -> []
適用
code:fsharp
let createEvents: CreateEvents =
fun pricedOrder orderAcknowledgmentSentOpt ->
let event1 =
pricedOrder
// 共通の選択型に変換する
|> PlaceOrderEvent.OrderPlaced
// リストに変換する
|> List.singleton
let event2 =
orderAcknowledgmentSentOpt
// 共通の選択型に変換する
|> Option.map PlaceOrderEvent.OrderAcknowledgmentSent
// リストに変換する
|> listOfOption
let event3 =
pricedOrder
|> createBillingEvent
// 共通の選択型に変換する
|> Option.map PlaceOrderEvent.BillableOrderPlaced
// リストに変換する
|> listOfOption
すべて同じ型になったので、別のリストに入れて展開することで返せるように
code:fsharp
let createEvents: CreateEvents =
fun pricedOrder orderAcknowledgmentSentOpt ->
// ...
[
yield! events1
yield! events2
yield! events3
]
未実装のステップがあってもコンパイルを通したい
パイプラインに多くのステップがあり未実装だが、プロジェクトのコンパイルを通したい
以下のように「未実装」の例外を発生させれば良い
code:fsharp
let priceOrder: PriceOrder =
fun getProductPrice validatedOrder ->
failwith "not implemented"