もう一回。F#メモ
型を明示的に定義するように修正しました。
### ドメインモデル
#### 値オブジェクト
module ValueObjects =
type OrderId = OrderId of string
type ProductId = ProductId of string
type Quantity = Quantity of int
type Price = Price of decimal
type OrderStatus =
| Pending
| Paid
| Shipped
| Delivered
| Cancelled
#### エンティティ
module Entities =
open ValueObjects
type OrderLine = {
ProductId: ProductId
Quantity: Quantity
Price: Price
}
type Order = {
OrderId: OrderId
OrderLines: OrderLine list
Status: OrderStatus
}
let createOrder (orderId: OrderId) (orderLines: OrderLine list) : Order =
{ OrderId = orderId; OrderLines = orderLines; Status = OrderStatus.Pending }
let calculateTotal (order: Order) : decimal =
order.OrderLines
|> List.sumBy (fun line ->
let (Price p) = line.Price
let (Quantity q) = line.Quantity
p * decimal q)
let payOrder (order: Order) : Result<Order, string> =
match order.Status with
| OrderStatus.Pending -> Ok { order with Status = OrderStatus.Paid }
| _ -> Error "Order cannot be paid in the current state"
let shipOrder (order: Order) : Result<Order, string> =
match order.Status with
| OrderStatus.Paid -> Ok { order with Status = OrderStatus.Shipped }
| _ -> Error "Order cannot be shipped in the current state"
let deliverOrder (order: Order) : Result<Order, string> =
match order.Status with
| OrderStatus.Shipped -> Ok { order with Status = OrderStatus.Delivered }
| _ -> Error "Order cannot be delivered in the current state"
let cancelOrder (order: Order) : Result<Order, string> =
match order.Status with
| OrderStatus.Pending | OrderStatus.Paid -> Ok { order with Status = OrderStatus.Cancelled }
| _ -> Error "Order cannot be cancelled in the current state"
### リポジトリ
module Repositories =
open System.Collections.Generic
open Entities
open ValueObjects
type IOrderRepository =
abstract member GetOrderById: OrderId -> Order option
abstract member SaveOrder: Order -> unit
type InMemoryOrderRepository() =
let orders = Dictionary<OrderId, Order>()
interface IOrderRepository with
member _.GetOrderById(orderId: OrderId) : Order option =
if orders.ContainsKey(orderId) then
Some(orders.[orderId])
else
None
member _.SaveOrder(order: Order) : unit =
orders.[order.OrderId] <- order
let createInMemoryOrderRepository() : IOrderRepository =
InMemoryOrderRepository() :> IOrderRepository
### サービス
module Services =
open Entities
open ValueObjects
open Repositories
let createOrderService (orderRepository: IOrderRepository) =
let placeOrder (order: Order) : Result<unit, string> =
orderRepository.SaveOrder(order)
Ok ()
let payOrder (orderId: OrderId) : Result<Order, string> =
match orderRepository.GetOrderById(orderId) with
| Some(order) ->
match Entities.payOrder(order) with
| Ok updatedOrder ->
orderRepository.SaveOrder(updatedOrder)
Ok updatedOrder
| Error msg -> Error msg
| None -> Error "Order not found"
let shipOrder (orderId: OrderId) : Result<Order, string> =
match orderRepository.GetOrderById(orderId) with
| Some(order) ->
match Entities.shipOrder(order) with
| Ok updatedOrder ->
orderRepository.SaveOrder(updatedOrder)
Ok updatedOrder
| Error msg -> Error msg
| None -> Error "Order not found"
let deliverOrder (orderId: OrderId) : Result<Order, string> =
match orderRepository.GetOrderById(orderId) with
| Some(order) ->
match Entities.deliverOrder(order) with
| Ok updatedOrder ->
orderRepository.SaveOrder(updatedOrder)
Ok updatedOrder
| Error msg -> Error msg
| None -> Error "Order not found"
let cancelOrder (orderId: OrderId) : Result<Order, string> =
match orderRepository.GetOrderById(orderId) with
| Some(order) ->
match Entities.cancelOrder(order) with
| Ok updatedOrder ->
orderRepository.SaveOrder(updatedOrder)
Ok updatedOrder
| Error msg -> Error msg
| None -> Error "Order not found"
// サービス関数を返す
placeOrder, payOrder, shipOrder, deliverOrder, cancelOrder
### 使用例
open ValueObjects
open Entities
open Repositories
open Services
// リポジトリの初期化
let orderRepository = createInMemoryOrderRepository()
// サービス関数の初期化
let placeOrder, payOrder, shipOrder, deliverOrder, cancelOrder = createOrderService orderRepository
// 注文の作成
let order = Entities.createOrder(
OrderId "1",
[
{ ProductId = ProductId "P1"; Quantity = Quantity 2; Price = Price 10.0M }
{ ProductId = ProductId "P2"; Quantity = Quantity 1; Price = Price 20.0M }
]
)
match placeOrder order with
| Ok () -> printfn "Order placed: %A" order
| Error msg -> printfn "Failed to place order: %s" msg
// 注文の支払い
match payOrder (OrderId "1") with
| Ok updatedOrder -> printfn "Order paid: %A" updatedOrder
| Error msg -> printfn "Failed to pay order: %s" msg
// 注文の出荷
match shipOrder (OrderId "1") with
| Ok updatedOrder -> printfn "Order shipped: %A" updatedOrder
| Error msg -> printfn "Failed to ship order: %s" msg
// 注文の配送
match deliverOrder (OrderId "1") with
| Ok updatedOrder -> printfn "Order delivered: %A" updatedOrder
| Error msg -> printfn "Failed to deliver order: %s" msg
// 注文のキャンセル
match cancelOrder (OrderId "1") with
| Ok updatedOrder -> printfn "Order cancelled: %A" updatedOrder
| Error msg -> printfn "Failed to cancel order: %s" msg
### 説明
1. **値オブジェクト**: `OrderId`、`ProductId`、`Quantity`、`Price`、および `OrderStatus` を定義。
2. **エンティティ**: `Order` および `OrderLine` を定義し、注文の生成および状態遷移の関数をモジュール内に定義。これらの関数は`Result`型を返す。
3. **リポジトリ**: `IOrderRepository` インターフェースとインメモリの実装 `InMemoryOrderRepository` を定義。リポジトリのインスタンスを生成するための関数も提供。
4. **サービス**: 関数を返す関数を使用して、リポジトリを初期化し、`Result`型を返すサービス関数を提供。
5. **使用例**: サービス関数を初期化し、`Result`型で操作結果を処理する例を示す。
これにより、型が明示的に定義され、エラー処理がより安全かつ明確になります。
この記事が気に入ったらサポートをしてみませんか?