如何构建F#类型的业务规则?
How to build F# type fulfilling business rules?
我正在尝试在 F# 中构建一个类型,当我获得该类型的对象时,我可以确定它处于有效状态。
该类型称为 JobId
,它只包含一个 Guid
.
业务规则是:它必须是一个 Guid - 但不能为空 Guid。
我已经在 C# 中实现了该类型,但现在我想将其移植到 F# class 库。
这是 C# 类型:
public sealed class JobId
{
public string Value { get; }
private JobId(string value)
=> Value = value;
public static JobId Create()
=> new JobId(Guid.NewGuid().ToString("N"));
public static Option<JobId> Create(Guid id)
=> id == Guid.Empty
? None
: Some(new JobId(id.ToString("N"));
public static Option<JobId> Create(string id)
{
try
{
var guid = new Guid(id);
return Create(guid);
}
catch (FormatException)
{
return None;
}
}
}
那么我该如何在 F# 中构建它呢?谢谢!
更新 1:
我试图将其实现为这样的可区分联合类型:
type JobId =
| JobId of string
但问题是,我无法使用该方法定义任何业务规则。
所以最后一个问题是:如何确保 JobId
中的 string
存在于
某种格式?
区分联合和 F# 记录保留内部表示 public,因此这仅适用于内部表示的所有值都有效的情况。如果您需要定义一个进行某些检查的原始类型,那么您需要一个隐藏其内部结构的类型。在这种特殊情况下,我将只使用与您的 C# 代码等效的相当直接的 F#:
type JobId private (id:string) =
member x.Value = id
static member Create() =
JobId(Guid.NewGuid().ToString("N"))
static member Create(id:Guid) =
if id = Guid.Empty then None
else Some(new JobId(id.ToString("N")))
static member Create(id:string) =
try JobId.Create(Guid(id))
with :? FormatException -> None
请注意,有两种情况您要防止 - 一种是 string
值实际上不是 Guid
,另一种是空 Guid
。您可以使用类型系统来防止第一种情况 - 只需创建一个值为 Guid
而不是 string
!
的 DU
type JobId =
| JobId of Guid
唉,没有办法确保这个 guid 不为空。但是,比上述更好的解决方案可能是定义 NonEmptyGuid
(使用上面的 class),它仅表示非空 guid。那么您的领域模型可能是:
type JobId =
| JobId of NonEmptyGuid
如果您在项目的其他地方使用 NonEmptyGuid
,这会特别好。
我修改了 Tomas 的答案,使用 DU 而不是 class 来保持适当的相等性和比较,例如,允许 JobId
作为分组键按预期工作。
[<AutoOpen>]
module JobId =
open System
type JobId = private JobId of string with
static member Create() = JobId(Guid.NewGuid().ToString("N"))
static member Create(id:Guid) =
if id = Guid.Empty then None
else Some(JobId(id.ToString("N")))
static member Create(id:string) =
try JobId.Create(Guid(id))
with :? FormatException -> None
您必须将类型放在模块内,然后您无法直接在该模块外访问 DU 构造函数:
JobId.Create (System.Guid.NewGuid()) // Some (JobId "1715d4ae776d441da357f0efb330be43")
JobId.Create System.Guid.Empty // None
JobId System.Guid.Empty // Compile error
我正在尝试在 F# 中构建一个类型,当我获得该类型的对象时,我可以确定它处于有效状态。
该类型称为 JobId
,它只包含一个 Guid
.
业务规则是:它必须是一个 Guid - 但不能为空 Guid。
我已经在 C# 中实现了该类型,但现在我想将其移植到 F# class 库。
这是 C# 类型:
public sealed class JobId
{
public string Value { get; }
private JobId(string value)
=> Value = value;
public static JobId Create()
=> new JobId(Guid.NewGuid().ToString("N"));
public static Option<JobId> Create(Guid id)
=> id == Guid.Empty
? None
: Some(new JobId(id.ToString("N"));
public static Option<JobId> Create(string id)
{
try
{
var guid = new Guid(id);
return Create(guid);
}
catch (FormatException)
{
return None;
}
}
}
那么我该如何在 F# 中构建它呢?谢谢!
更新 1:
我试图将其实现为这样的可区分联合类型:
type JobId =
| JobId of string
但问题是,我无法使用该方法定义任何业务规则。
所以最后一个问题是:如何确保 JobId
中的 string
存在于
某种格式?
区分联合和 F# 记录保留内部表示 public,因此这仅适用于内部表示的所有值都有效的情况。如果您需要定义一个进行某些检查的原始类型,那么您需要一个隐藏其内部结构的类型。在这种特殊情况下,我将只使用与您的 C# 代码等效的相当直接的 F#:
type JobId private (id:string) =
member x.Value = id
static member Create() =
JobId(Guid.NewGuid().ToString("N"))
static member Create(id:Guid) =
if id = Guid.Empty then None
else Some(new JobId(id.ToString("N")))
static member Create(id:string) =
try JobId.Create(Guid(id))
with :? FormatException -> None
请注意,有两种情况您要防止 - 一种是 string
值实际上不是 Guid
,另一种是空 Guid
。您可以使用类型系统来防止第一种情况 - 只需创建一个值为 Guid
而不是 string
!
type JobId =
| JobId of Guid
唉,没有办法确保这个 guid 不为空。但是,比上述更好的解决方案可能是定义 NonEmptyGuid
(使用上面的 class),它仅表示非空 guid。那么您的领域模型可能是:
type JobId =
| JobId of NonEmptyGuid
如果您在项目的其他地方使用 NonEmptyGuid
,这会特别好。
我修改了 Tomas 的答案,使用 DU 而不是 class 来保持适当的相等性和比较,例如,允许 JobId
作为分组键按预期工作。
[<AutoOpen>]
module JobId =
open System
type JobId = private JobId of string with
static member Create() = JobId(Guid.NewGuid().ToString("N"))
static member Create(id:Guid) =
if id = Guid.Empty then None
else Some(JobId(id.ToString("N")))
static member Create(id:string) =
try JobId.Create(Guid(id))
with :? FormatException -> None
您必须将类型放在模块内,然后您无法直接在该模块外访问 DU 构造函数:
JobId.Create (System.Guid.NewGuid()) // Some (JobId "1715d4ae776d441da357f0efb330be43")
JobId.Create System.Guid.Empty // None
JobId System.Guid.Empty // Compile error