请求 - f#中的响应域建模

时间:2017-09-05 02:47:19

标签: f# functional-programming

我正在寻求使用F#中的函数方法建模基于请求 - 响应的系统的最佳实践。

例如广义案例要求:

  • 有许多请求/响应对类型:A,B,C等
  • 每个回复必须代表两种状态:succeedfailed
  • 每个请求都包含common数据和specific A / B / ...数据
  • 处理时,请求可能会因commonspecific原因而被拒绝

至于现在,我最终得到了以下设计:

type CommonData = {...}

type RequestMessage =
| ARequestMessage of CommonData * ARequestData
| BRequestMessage of CommonData * BRequestData
...

type Response<'S, 'F> =
| Success of 'S
| Failure of 'F

type ResponseMessage =
| AResponseMessage of Response<AResponseSuccess, AResponseFailure>
| BResponseMessage of Response<BResponseSuccess, BResponseFailure>
...

其中AResponseSuccess和BResponseSuccess包含相同的歧视联合案例或ResponseMessage类型涵盖GeneralResponseMessage类型

type GeneralResponseMessage =
| CommonFailure of CommonResponseFailure
| SpecificResponse of ResponseMessage

这两种情况看起来都很糟糕,我无法找到更清晰优雅的解决方案。

修改

系统以状态机方式处理单个演进状态的请求,因此不可能将不同类型的请求处理分开

1 个答案:

答案 0 :(得分:2)

这可能更适合https://codereview.stackexchange.com/,但让我试着在这里回答这个问题。

我对此的看法是垂直分组操作,而不是水平分组。我的意思是,不是将所有请求分为一种类型而将所有响应分组到另一种类型中,而是将相应的请求和响应类型配对,并为处理本身构建一个通用的包装器。

在您的情况下,处理从根本上是类型RequestMessage -> GeneralResponseMessage的函数。我建议将其概括为以下内容:

type ProcessRequest<'requestData, 'responseMessage, 'responseFailure> =
    CommonData -> 'requestData ->
        Result<'responseMessage, ErrorResponse<'responseFailure>>

and ErrorResponse<'responseFailure> = 
    | CommonFailure of CommonResponseFailure
    | SpecificResponse of 'responseFailure

请注意,我使用Result中的FSharp.Core代替Response类型,因为他们对完全相同的内容进行了建模。

然后您可以创建特定的实例,例如

type AProcessRequest =
    ProcessRequest<ARequestMessage, AResponseMessage, AResponseFailure>

然后可以使用它来处理特定的消息。在您的情况下,处理逻辑必须匹配RequestMessage情况并继续进行,在我的情况下,您必须在调用特定处理器之前进行此匹配,但如果您需要将其分组为一种类型,则可以总是做一些事情来将所有实现作为单个数据提供

type ProcessMessage =
    { A : AProcessRequest
      B : BProcessRequest
      C : ... }

您可以在组合根目录中提供实现。

如果您不喜欢这么多特定类型,则不必为通用处理器的具体实例指定名称,C : ProcessMessage<CRequestMessage, CResponseMessage, CResponseFailure>会很好,但如果有很多,可能会更不易读相似类型的实例。