メモF#



### ドメインモデル

#### 値オブジェクト

```fsharp
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
```

#### エンティティ

```fsharp
module Entities =

    open ValueObjects

    type OrderLine = {
        ProductId: ProductId
        Quantity: Quantity
        Price: Price
    }

    type Order = {
        OrderId: OrderId
        OrderLines: OrderLine list
        Status: OrderStatus
    }

    let createOrder orderId orderLines =
        { OrderId = orderId; OrderLines = orderLines; Status = Pending }

    let calculateTotal order =
        order.OrderLines
        |> List.sumBy (fun line -> match line.Price with | Price p -> p * decimal (match line.Quantity with | Quantity q -> q))

    let payOrder order =
        match order.Status with
        | Pending -> Ok { order with Status = Paid }
        | _ -> Error "Order cannot be paid in the current state"

    let shipOrder order =
        match order.Status with
        | Paid -> Ok { order with Status = Shipped }
        | _ -> Error "Order cannot be shipped in the current state"

    let deliverOrder order =
        match order.Status with
        | Shipped -> Ok { order with Status = Delivered }
        | _ -> Error "Order cannot be delivered in the current state"

    let cancelOrder order =
        match order.Status with
        | Pending | Paid -> Ok { order with Status = Cancelled }
        | _ -> Error "Order cannot be cancelled in the current state"
```

### リポジトリ

```fsharp
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) =
                if orders.ContainsKey(orderId) then
                    Some(orders.[orderId])
                else
                    None

            member _.SaveOrder(order) =
                orders.[order.OrderId] <- order

    let createInMemoryOrderRepository() =
        InMemoryOrderRepository() :> IOrderRepository
```

### サービス

関数を返す関数を使用して、リポジトリを初期化し、操作結果を`Result`型で返す関数を提供します。

```fsharp
module Services =

    open Entities
    open ValueObjects
    open Repositories

    let createOrderService orderRepository =
        let placeOrder order =
            orderRepository.SaveOrder(order)
            Ok ()

        let payOrder orderId =
            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 =
            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 =
            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 =
            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
```

### 使用例

関数を返す関数を使用して、サービス関数を初期化し、`Result`型で操作結果を処理する例を示します。

```fsharp
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`型で操作結果を処理する例を示します。

この構造により、エラー処理が明示的かつ安全になり、関数型プログラミングスタイルでシンプルなAPIを提供できます。

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