我正在创建一种“信封”,以跟踪我在应用程序中通过RabbitMQ发送的内容(命令和事件)如下:
type Envelope<'T> = {
Id: Guid
CausationId: Guid
CorrelationId: Guid
Payload: 'T
Timestamp: DateTimeOffset
}'T通常是表单的命令或事件:
type Commands =
| DoA of Stuff
| DoB of Stuff
| DoC of Stuff
type Events =
| AHappened of Stuff
| BHappened of Stuff
| CHappened of Stuff注意:我故意使用相对通用的名称。
但是,我也可以使用特定类型:
type CausationId = CausationId of Guid
type CorrelationId = CorrelationId of Guid
type CommandId = CommandId of Guid
type EventId = EventId of Guid
type Timestamp = Timestamp of DateTimeOffset
type DataToDoA = DataToDoA of Stuff
type DataToDoB = DataToDoB of Stuff
type DataToDoC = DataToDoC of Stuff
type Commands =
| DoA of DataToDoA
| DoB of DataToDoB
| DoC of DataToDoC
type DataLeftByA = DataLeftByA of Stuff
type DataLeftByB = DataLeftByB of Stuff
type DataLeftByC = DataLeftByC of Stuff
type Events =
| AHappened of DataLeftByA
| BHappened of DataLeftByB
| CHappened of DataLeftByC这将导致:
type CommandEnvelope<`T> = {
Id: CommandId
CausationId: CausationId
CorrelationId: CorrelationId
Payload: `T
Timestamp: Timestamp
}
type EventEnvelope<`T> = {
Id: EventId
CausationId: CausationId
CorrelationId: CorrelationId
Payload: `T
Timestamp: Timestamp
}有没有经验法则来决定什么时候使用这些特定的类型/别名?
发布于 2019-05-31 11:17:49
我不认为这是一条经验法则,但有一种系统化的思考方式是考虑是否需要在代码中单独传递由这些额外类型表示的值。如果不是,那你就不需要那些额外的类型了。如果是的话,他们可能会有用。
假设您只有Stuff,并且:
type DataLeftByA = DataLeftByA of Stuff
type DataLeftByB = DataLeftByB of Stuff
type Events =
| AHappened of DataLeftByA
| BHappened of DataLeftByB这样做的一件事就是编写一个函数:
let processDataA (DataLeftByA stuff) = (...)这个函数接受包含一些DataLeftByA的Stuff。但是,该类型清楚地表明,该函数只应用于由A引起的事件。以下将是类型错误:
let handleEvent = function
| AHappened adata -> processDataA adata
| BHappened bdata -> processDataA bdata // Type error here!如果您将Events定义为只是连接相同Stuff的事件
type Events =
| AHappened of Stuff
| BHappened of Stuff那么您的事件携带的数据是相同的,但是您正在失去定义像processDataA这样的函数的能力,因为事件A所携带的数据没有单独的类型。您可以只定义processStuff,但是可以在A和B的情况下调用它。
我认为这是这两个版本之间真正有实际区别的唯一东西。因此,经验法则是-您是否需要定义像processDataA这样的函数?
发布于 2019-05-31 11:24:58
F#的乐趣和利润的Scott在他的“带类型的设计”系列中谈到了这一点。在关于单个案例联合类型的文章中,他解释说,使用单用例联合类型的主要原因之一是您想要添加一些验证逻辑。这使您可以进一步完善您的类型。
他举的例子是电子邮件。您可以使用type Person = { ... Email: string ... },但这不能保证字符串是电子邮件地址。为它创建一个新类型( type EmailAddress = Email of string ),那么您只能从首先验证它的函数中创建电子邮件地址。这可能是一个良好的理智检查,并有助于确保您没有分配电子邮件地址的东西不是地址/链接。
TL;DR单用例联合类型对于在代码中添加进一步的语义/验证非常有用。
https://stackoverflow.com/questions/56393781
复制相似问题