建构和解构单一案例与计量单位的判分性联合



我有两种类型:

  • DTO 用于脏传输对象;)用于反序列化 JSON 有效负载
  • 使我的数据在域内保持一致和干净的域。

我有两个函数可以将记录从一个空间转换为另一个空间。我想使用度量单位,但解决方案似乎很笨拙。我想知道实现这一点的其他选择是什么。

module MyApp.Api.Dto
open System
[<CLIMutable>]
type Feed = 
{
volume: decimal
entryTime: DateTime
}
module MyApp.Api.Domain
[<Measure>]
type ml
type FeedVolume = private FeedVolume of decimal<ml>
module FeedVolume =
let value (FeedVolume f) = f
let create dec = 
if dec <= 0m<ml> then Error "Volume must not be negative"
else Ok (Volume dec)

module Feed =
let toDomain (dto:Dto.Feed) : Result<Feed,string> =
result {
let! volume = dto.volume |> FeedVolume.create 
let! entryTime = dto.entryTime |> EntryTime.create 
return {
Volume = volume
EntryTime = entryTime
}
}

错误:

Type mismatch. Expecting a
'decimal -> Result<'a,string>'    
but given a
'decimal<ml> -> Result<FeedVolume,string>'    
The type 'decimal' does not match the type 'decimal<ml>'

我已将构造函数和解构函数更改为以下内容:

module FeedVolume =
let value (FeedVolume f) = f / 1.0m<ml>
let create (dec:decimal) = 
if dec <= 0m then Error "FeedVolume must not be negative"
else Ok (FeedVolume (dec * 1.0m<ml>))

。但我不确定这是否是正确的方法

如果您使用 Newtonsoft 来反序列化 JSON,我只会将度量单位放在 DTO 定义中。create函数应验证域的不变量,而不是单位。 如果要从没有单位的原始基元创建经过验证的域类型,则应使用单独的函数(如unsafeCreate(强制基元使用正确的单位。

下面是一个使用 Newtonsoft 反序列化 DTO 中单位的 JSON 的示例:

[<Measure>] type kg
[<Measure>] type ml
type DtoWithUnitsOfMeasure =
{ 
Volume: decimal<ml>
Weight: decimal<kg>
}

[<Test>]
let ``DTOs with units of measure should deserialize into measured values`` () =
let json = """{ "volume": 3.2348, "weight": 0.682 }"""
let dto = json |> JsonConvert.DeserializeObject<DtoWithUnitsOfMeasure>
dto.Volume |> should equal 3.2348M<ml>
dto.Weight |> should equal 0.682M<kg>

这个测试对我来说通过了。

最新更新