メモ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を提供できます。
この記事が気に入ったらサポートをしてみませんか?