もう一回。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`型で操作結果を処理する例を示す。

これにより、型が明示的に定義され、エラー処理がより安全かつ明確になります。

この記事が気に入ったらサポートをしてみませんか?