函数响应式领域建模 读书笔记


title: 函数响应式领域建模 读书笔记
tags:

基础概念

Monad 定义
根据维基百科的定义,Monad 由以下三个部分组成:

 一个类型构造函数(M),可以构建出一元类型 M<T>。 
 一个类型转换函数(return or unit),能够把一个原始值装进 M 中。

	  unit(x) : T -> M T

   一个组合函数 bind,能够把 M 实例中的值取出来,放入一个函数中去执行,最终得到一个新的 M 实例。

 M<T> 执行 T-> M<U> 生成 M<U>

例如,java 8 中的 Optional 即是一个 Monad
函数的引用透明性(referential transparency)

初学程序设计时,比较容易混淆的两个概念是数学函数(math function)和程序中使用的函数。

在数学函数中 y=f(x),一个输入值有固定的输出值。例如,无论计算多少次,sinπ 的结果总是 0。如果 f(x)=x/2,那么 f(10) 无论计算 100 次还是 1000 次,其结果都是 5.

程序设计中的函数却不具备这种稳定的特性,因为函数的执行不仅依赖于输入值,而且会受到全局变量,输入文件,类的成员变量等诸多因素的影响。

int counter = 0;
int count(){
    return ++counter;
}

此函数输入没有输入值,但每次都返回不同的结果。当然,就像数学函数那样,程序中函数还可以设计成 “对同一输入值每次都返回相同结果” 的形式。

_函数的副作用

1.2.2 领域模型元素

  • 任何领域模型的核心都是不同领域元素之间行为或交互的集合。
  • 区分领域服务和实体、值对象的主要方式是粒度的层次。

设计函数式领域模型的通用原则

  • 将不可变状态建模为 代数数据类型 (ADT)
  • 在模块中将行为建模为函数,比如一个领域服务,行为比状态更好组合,因此,在模块中包含相关的行为有助于提升组合性。

1.8 函数遇上响应式

函数基本信条:1.用纯函数做设计 2.从纯函数中分离副作用

1.9 总结

  • 在模型内避免共享可变的状态: 共享可变状态非常难以管理,在语义上也会导致不确定性,进而导致并发的不可控
  • 引用透明
  • 有机增长
  • 聚焦在核心领域:在用领域驱动设计的原则建立模型时,我们拥有实体、值对象以及服务,并通过一些模式如仓储和工厂来组织它们。同时也可以使它函数化。易变性可能会使部分代码跑得太快,并且难以推导。在 DDD 代码的每一层都努力坚持不变性
  • 函数使响应更容易:对响应式建模来说,纯函数是理想的候选人。

2.3.2 引用透明的其他好处

  • 易测性
    纯函数式很容易测试的,因为不依赖于任何副作用或外部状态。
  • 并行执行
    如果代码不受副作用的影响,可以更有效率地使用并行数据结构,而不用考虑带来的并发问题。

2.5 局部用函数,全局用 OO

模块是所有这些问题的答案。不同功能需要划分到不同的模块。同时抽象需要做到小而紧凑,它们中的任何一个都只专注在完成某一个特定的事情上。

3 设计函数式领域模型

3.1 API 设计的代数

在使用类似 JAVA 语言的面向对象开发中,我们会用接口来做 API 设计,它将最终把模型的规则发布给终端用户。当准备好接口之后,就可以开始类和对象的具体实现。首先定义类,然后加入一些操作作为这个类的方法。在函数式编程中,将这个过程颠倒了一下——首先定义对应到基本领域行为的操作,然后将相关的操作组合成模块,形成更大的用例。

什么是代数?

在函数式领域模型中定义的模块是函数的集合,它们操作一系列的类型,遵从一系列被称为领域规则的不变式。用数学的术语来说,这就是模块的代数。

3.2 为领域服务定义代数

前文提过,与 OO 基于类的方式不同,模块中首先要有操作,才能使核心领域对象保持瘦小和扁平。行为不再和对象捆绑在一起,同时还可以通过将对象的类型作为它们所代表的代数内容来独立地演变行为。

3.2.1 赋值抽象

假设有一个数据类型 Account 作为函数 open 的返回类型,如果操作成功,将返回一个实例化的 Account 对象。如果失败,既可以返回 null,也可以抛出一个异常。在所有这些场景中,返回的是经过计算的值,同时也是这次赋值的结果。严格意义上来说,Option 和 Try 不是数据类型,它们是类型构造器,Try 抽象了失败的作用,而 Option 抽象了可选的作用。在我看来,Try 不仅仅是一个处理异常的手段,同时还提供了抽象能力,这样我们就可以在赋值之前进行组合,建立更大粒度的抽象来应对失败。

3.2.2 组合抽象

4.4.2 改进代数

  • kleisli 模式

6.0 响应式模型

  • 什么是响应式领域模型
    终极目标是使系统具备良好的响应性,这样用户才会有比较良好的体验。这意味着系统必须在特定时间延迟之内响应用户的请求,这也被称为边界延迟。而且系统的延迟边界同样还包含了失败,一个系统如果被失败卡住了,那显然它就不具备响应性。
    要使模型具备响应性,需要确保构成它的所有模型也都具有响应性。一个模型所需要遵守的响应式架构的所有原则同样适用于构成系统的其他模型。整体系统的响应性取决于组件中响应能力最差的那个,也就是我们常说的木桶效应。
    一个常见的问题是,一个模型内哪些组件需要成为响应式的?是否所有函数都真的需要成为非阻塞的?

7.1 响应式流模型

从迄今为止的学习中,不难想象领域模型是一个 图G,它的节点是各种组件,例如实体、聚合、值对象以及服务,这些组件通过领域行为交互的边缘建模被关联在一起。这里我们说的是一个具有特定领域和数据模型的边界上下文,而且边缘建模的交互可以是同步或异步的。就更高层次而言,也可以将多个边界上下文想象成另一个图形H,其中每个上下文形成一个节点,并且连接两个上下文的边界对它们如何交互进行建模。G 和 H 之间的主要区别在于,在H中连接多个上下文的边界几乎只会形成系统的异步边界。在第6章中,我们讨论了消息传递可以作为处理跨模型异步边界通信的一种方法。还看到基于明确消息传递使用actor模型设计API的缺点。actor提供了一个无类型的交互模型, 这导致领域模型无法推导。但是因为actor是轻量级的, 所以它们很容易扩展, 这与线程不同, 我们可以在标准笔记本电脑上运行上百万个actor, 传递实体间领域模型的交互事件。响应式流模型提供了一个两全其美的方法:使用构成声明式DSL的类型API.它具体化为actor模型的实现。可以对同步和异步边界统一建模, 以一个原则的方式处理失败, 但仍获得使用组合类型API进行推理的所有优势。作为响应式模型, 流将处理后端压力,确保从生产者流向消费者的数据与反向流动的需求之间保持最佳的平衡。在接下来的几节中,将详细了解响应式流以及如何将它们与其他并发模型相结合。还将看到,流模型作为建立响应式架构的完整解决方案,是如何解决弹性、灵活性和扩展性的问题的。

7.2何时使用流模型

一旦了解了什么是流模型,接下来的问题便是何时去使用它。毕竟流提供了一个具有一定成本的抽象。显然,我们更愿意用适合用例的最廉价模型。和其他抽象一样,流的使用也不是完全免费的。我们可以将领域模型的动态行为可视化为处理单元之间的交互图。在流计算的词汇表中,这些处理单元通常被称为运算符,而交互被称为管道.可以将一个业务用例可视化为流过管道的数据流并通过一组运算符进行转化。在下一节中将更多地讨论这样的用例及其基于流的建模。当我们有一个用例,它可以被建模成一系列转换并形成流程图,那就可以考虑用响应式流作为潜在的建模抽象。这个模型的本质是它使流成为一个头等类抽象,从而使运算符从数据何时到达何时离开的关注中解耦出来。数据可以以异步或同步的方式处理,我们的运算符完全忽略这一事实。这使模型更加模块化,因为我们己经将运算符与数据流解耦,并且两者都可以在模型中独立进化和扩展。作为示例,如果查看Akka Stams的架构, Flow 会对流处理的步骤进行建模, 并提供单独的 map 和 map Async 来描述运算符之间的数据流类型(同步或异步) map和 apA sync 是不同的运算符, 它们可以对 Flow 进行操作, 从而使整个架构更加模快化, 并将 Flow 从被操作中解耦出来。关于异步边界的定义,请参见6.3节。

7.3领域用例

让我们从个人银行领域建立一个用例,使用响应式流作为主要实现技术来建模,考虑银行前台与其他外部系统间的交互,以及后台处理所有合并并生成记录系统。以下是我们将要建模的工作流:

  • 聚合交易:前台将各个外部系统交易汇总后,得到一个合并的交易列表。假设此聚合将会与前台作为一个以逗号分隔的文件出现,它可以出现在任何地方(在消息队列或另一个持久性存储中)。但为了简单实现,我们假设它是一个文本文件。
  • 发送到后台:前台需要将交易数据发送到后台做进一步处理和结算。
  • 创建领域模型元素:后台从文件中接收交易信息,并创建领域对象,它可以通过适当的领域行为与其他的模型部分进行交互。
  • 净交易:后台需要每个帐户的净交易。交易可以是 debit 交易或 credit 交易。对于 debit 交易, 资金将从帐户中扣除, 而对于 credit 交易, 资金将被增加到账户中。
  • 持久化和通知:净交易需要保存到存储中。因为前台和后台之间的这种交互会持续很长时间,所以后台需要周期性地发布所有净交易的状态。在现实中,这种发布可能表现为仪表板的形式,但这里为了简单起见,可以考虑只做打印。这个用例在交互系统之间建立了一个共同的行为模型。我们需要处理前台和后台之间的异步边界,考虑的重点是这种交互的波形可能相当尖锐。在营业高峰时间,前台将被加载大量的交易,到后台的数据流量也会很高。重要的是后端系统能够处理交互请求的流量,这是响应式流处理的主要问题之一:后端压力。让我们跳到模型看看如何解决这个问题。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值