「javacqrs框架」cqrs架构实现
今天给各位分享javacqrs框架的知识,其中也会对cqrs架构实现进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!
本文目录一览:
- 1、为什么我不再使用MVC框架
- 2、从横切到纵切,架构模式CQRS,提高系统进化能力
- 3、一个mvc框架的appsettings怎么设置
- 4、学习scala有哪些好的资源
- 5、Reactive Messaging Patterns with Actor Model — 1 从 Enterprise application说起
为什么我不再使用MVC框架
MVC 的辉煌过去与现存问题
在每个用户界面背后,我们都在使用 MVC 模式,也就是模型-视图-控制器(Model-View-Controller)。MVC
发明的时候,Web 尚不存在,当时的软件架构充其量是胖客户端在原始网络中直接与单一数据库会话。但是,几十年之后,MVC 依然在使用,持续地用于
OmniChannel 应用的构建。
Angular 2 正式版即将发布,在这个时间节点重估 MVC 模式及各种 MVC 框架为应用架构带来的贡献意义重大。
我第一次接触到 MVC 是在 1990 年,当时 NeXT 刚刚发布 Interface
Builder(让人惊讶的是,如今这款软件依然发挥着重大的作用)。当时,我们感觉 Interface Builder 和 MVC
是一个很大的进步。在 90 年代末期,MVC 模式用到了 HTTP 上的任务中(还记得 Struts 吗?),如今,就各个方面来讲,MVC
是所有应用架构的基本原则。
MVC 的影响十分深远,以致于 React.js 在介绍他们的框架时都委婉地与其划清界限:“React 实现的只是 MVC 中视图(View)的部分”。
当我去年开始使用 React 的时候,我感觉它在某些地方有着明显的不同:你在某个地方修改一部分数据,不需要显式地与 View 和
Model 进行交互,整个 UI 就能瞬间发生变化(不仅仅是域和表格中的值)。这也就是说,我很快就对 React
的编程模型感到了失望,在这方面,我显然并不孤独。我分享一下 Andre Medeiros 的观点:
React 在很多方面都让我感到失望,它主要是通过设计不佳的 API 来引导程序员[…]将多项关注点混合到一个组件之中。
作为服务端的 API 设计者,我的结论是没有特别好的方式将 API 调用组织到 React 前端中,这恰恰是因为 React 只关注 View,在它的编程模型中根本不存在控制器。
到目前为止,Facebook 一直致力于在框架层面弥合这一空白。React 团队起初引入了 Flux 模式,不过它依然令人失望,最近 Dan Abramov 又提倡另外一种模式,名为 Redux,在一定程度上来讲,它的方向是正确的,但是在将 API 关联到前端方面,依然比不上我下面所介绍的方案。
Google 发布过 GWT、Android SDK 还有 Angular,你可能认为他们的工程师熟知何为最好的前端架构,但是当你阅读
Angular 2 设计考量的文章时,便会不以为然,即便在 Google 大家也达成这样的共识,他们是这样评价之前的工作成果的:
Angular 1
并不是基于组件的理念构建的。相反,我们需要将控制器与页面上各种[元素]进行关联(attach),其中包含了我们的自定义逻辑。根据我们自定义的指令
如何对其进行封装(是否包含 isolate scope?),scope 会进行关联或继续往下传递。
基于组件的 Angular 2 看起来能简单一点吗?其实并没有好多少。Angular 2 的核心包本身就包含了 180 个语义(Semantics),整个框架的语义已经接近 500 个,这是基于 HTML5 和 CSS3 的。谁有那么多时间学习和掌握这样的框架来构建 Web 应用呢?当 Angular 3 出现的时候,情况又该是什么样子呢?
在使用过 React 并了解了 Angular 2 将会是什么样子之后,我感到有些沮丧:这些框架都系统性地强制我使用
BFF“页面可替换模式(Screen Scraping)”模式,按照这种模式,每个服务端的 API
要匹配页面上的数据集,不管是输入的还是输出的。
弃用 MVC 之后怎么走?
此时,我决定“让这一切见鬼去吧”。我构建了一个 Web 应用,没有使用 React、没有使用 Angular 也没有使用任何其他的 MVC 框架,通过这种方式,我看一下是否能够找到一种在 View 和底层 API 之间进行更好协作的方式。
就 React 来讲,我最喜欢的一点在于 Model 和 View 之间的关联关系。React 不是基于模板的,View 本身没有办法请求数据(我们只能将数据传递给 View),看起来,针对这一点进行探索是一个很好的方向。
如果看得足够长远的话,你会发现 React 唯一的目的就是将 View 分解为一系列(纯粹的)函数和 JSX 语法:
V params={M}/
它实际上与下面的格式并没有什么差别:
V = f ( M )
例如,我当前正在从事项目的 Web 站点, Gliiph,就是使用这种函数构建的:
图1:用于生成站点 Slider 组件 HTML 的函数
这个函数需要使用 Model 来填充数据:
图2:支撑 slider 的 Model
如果用简单的 JavaScript 函数就能完成任务,我们为什么还要用 React 呢?
虚拟 DOM(virtual-dom)?如果你觉得需要这样一种方案的话(我并不确定有很多的人需要这样),其实有这样的可选方案,我也期望开发出更多的方案。
GraphQL?并不完全如此。不要因为 Facebook
大量使用它就对其产生误解,认为它一定是对你有好处的。GraphQL 仅仅是以声明的方式来创建视图模型。强制要求 Model 匹配 View
会给你带来麻烦,而不是解决方案。React 团队可能会觉得使用“客户端指定查询(Client-specified
queries)”是没有问题的(就像反应型团队中那样):
GraphQL 完全是由 View 以及编写它们的前端工程师的需求所驱动的。[…]另一方面,GraphQL 查询会精确返回客户端请求的内容,除此之外,也就没什么了。
GraphQL 团队没有关注到 JSX 语法背后的核心思想:用函数将 Model 与 View 分离。与模板和“前端工程师所编写的查询”不同,函数不需要 Model 来适配 View。
当 View 是由函数创建的时候(而不是由模板或查询所创建),我们就可以按需转换 Model,使其按照最合适的形式来展现 View,不必在 Model 的形式上添加人为的限制。
例如,如果 View 要展现一个值v,有一个图形化的指示器会标明这个值是优秀、良好还是很差,我们没有理由将指示器的值放到 Model 中:函数应该根据 Model 所提供的v值,来进行简单的计算,从而确定指示器的值。
现在,把这些计算直接嵌入到 View 中并不是什么好主意,使 View-Model 成为一个纯函数也并非难事,因此当我们需要明确的 View-Model 时,就没有特殊的理由再使用 GraphQL 了:
V = f ( vm (M) )
作为深谙 MDE 之道的人,我相信你更善于编写代码,而不是元数据,不管它是模板还是像 GraphQL 这样的复杂查询语言。
这个函数式的方式能够带来多项好处。首先,与 React 类似,它允许我们将 View 分解为组件。它们创建的较为自然的界面允许我们为 Web 应用或 Web 站点设置“主题”,或者使用不同的技术来渲染 View(如原生的方式)。函数实现还有可能增强我们实现反应型设计的方式。
在接下来的几个月中,可能会出现开发者交付用 JavaScript 函数包装的基于组件的 HTML5 主题的情况。这也是最近这段时间,在我的
Web 站点项目中,我所采用的方式,我会得到一个模板,然后迅速地将其封装为 JavaScript 函数。我不再使用
WordPress。基本上花同等的工夫(甚至更少),我就能实现 HTML5 和 CSS 的最佳效果。
这种方式也需要在设计师和开发人员之间建立一种新型的关系。任何人都可以编写这些 JavaScript 函数,尤其是模板的设计人员。人们不需要学习绑定方法、JSX 和 Angular 模板的语法,只掌握简单的 JavaScript 核心函数就足以让这一切运转起来。
有意思的是,从反应型流程的角度来说,这些函数可以部署在最合适的地方:在服务端或在客户端均可。
但最为重要的是,这种方式允许在 View 与 Model 之间建立最小的契约关系,让 Model 来决定如何以最好的方式将其数据传递给 View。让 Model 去处理诸如缓存、懒加载、编配以及一致性的问题。与模板和 GraphQL 不同,这种方式不需要从 View 的角度来直接发送请求。
既然我们有了一种方式将 Model 与 View 进行解耦,那么下一个问题就是:在这里该如何创建完整的应用模型呢?“控制器”该是什么样子的?为了回答这个问题,让我们重新回到 MVC 上来。
苹果公司了解 MVC 的基本情况,因为他们在上世纪 80 年代初,从 Xerox PARC“偷来了”这一模式,从那时起,他们就坚定地实现这一模式:
图3:MVC 模式
Andre Medeiros 曾经清晰地指出,这里核心的缺点在于, MVC
模式是“交互式的(interactive)”(这与反应型截然不同)。在传统的 MVC 之中,Action(Controller)将会调用
Model 上的更新方法,在成功(或出错)之时会确定如何更新
View。他指出,其实并非必须如此,这里还有另外一种有效的、反应型的处理方式,我们只需这样考虑,Action 只应该将值传递给
Model,不管输出是什么,也不必确定 Model 该如何进行更新。
那核心问题就变成了:该如何将 Action 集成到反应型流程中呢?如果你想理解 Action 的基础知识的话,那么你应该看一下
TLA+。TLA 代表的是“Action 中的逻辑时序(Temporal Logic of Actions)”,这是由 Dr. Lamport
所提出的学说,他也因此获得了图灵奖。在 TLA+ 中,Action 是纯函数:
data’ = A (data)
我真的非常喜欢 TLA+ 这个很棒的理念,因为它强制函数只转换给定的数据集。
按照这种形式,反应型 MVC 看起来可能就会如下所示:
V = f ( M.present ( A (data) ) )
这个表达式规定当 Action 触发的时候,它会根据一组输入(例如用户输入)计算一个数据集,这个数据是提交到 Model
中的,然后会确定是否需要以及如何对其自身进行更新。当更新完成后,View 会根据新的 Model 状态进行更新。反应型的环就闭合了。Model
持久化和获取其数据的方式是与反应型流程无关的,所以,它理所应当地“不应该由前端工程师来编写”。不必因此而感到歉意。
再次强调,Action 是纯函数,没有状态和其他的副作用(例如,对于 Model,不会包含计数的日志)。
反应型 MVC 模式很有意思,因为除了 Model 以外,所有的事情都是纯函数。公平来讲,Redux 实现了这种特殊的模式,但是带有
React 不必要的形式,并且在 reducer 中,Model 和 Action 之间存在一点不必要的耦合。Action
和接口之间是纯粹的消息传递。
这也就是说,反应型 MVC 并不完整,按照 Dan 喜欢的说法,它并没有扩展到现实的应用之中。让我们通过一个简单的样例来阐述这是为什么。
假设我们需要实现一个应用来控制火箭的发射:一旦我们开始倒计时,系统将会递减计数器(counter),当它到达零的时候,会将 Model 中所有未定的状态设置为规定值,火箭的发射将会进行初始化。
这个应用有一个简单的状态机:
图4:火箭发射的状态机
其中 decrement 和 launch 都是“自动”的 Action,这意味着我们每次进入(或重新进入)counting
状态时,将会保证进行转换的评估,如果计数器的值大于零的话,decrement Action 将会继续调用,如果值为零的话,将会调用
launchAction。在任何的时间点都可以触发 abort Action,这样的话,控制系统将会转换到 aborted 状态。
在 MVC 中,这种类型的逻辑将会在控制器中实现,并且可能会由 View 中的一个计时器来触发。
这一段至关重要,所以请仔细阅读。我们已经看到,在 TLA+ 中,Action 没有副作用,只是计算结果的状态,Model 处理
Action 的输出并对其自身进行更新。这是与传统状态机语义的基本区别,在传统的状态机中,Action
会指定结果状态,也就是说,结果状态是独立于 Model 的。
在 TLA+ 中,所启用的 Action 能够在状态表述(也就是 View)中进行触发,这些 Action 不会直接与触发状态转换的行为进行关联。换
句话说,状态机不应该由连接两个状态的元组(S1, A, S2)来进行指定,传统的状态机是这样做的,它们元组的形式应该是(Sk, Ak1,
Ak2,…),这指定了所有启用的 Action,并给定了一个状态 Sk,Action 应用于系统之后,将会计算出结果状态,Model
将会处理更新。
当我们引入“state”对象时,TLA+ 提供了一种更优秀的方式来对系统进行概念化,它将 Action 和 view(仅仅是一种状态的表述)进行了分离。
我们样例中的 Model 如下所示:
model = {
counter: ,
started: ,
aborted: ,
launched:
}
系统中四个(控制)状态分别对应于 Model 中如下的值:
ready = {counter: 10, started: false, aborted: false, launched: false }
counting = {counter: [0..10], started: true, aborted: false, launched: false }
launched = {counter: 0, started: true, aborted: false, launched: true}
aborted = {counter: [0..10], started: true, aborted: true, launched: false}
这个 Model 是由系统的所有属性及其可能的值所指定的,状态则指定了所启用的 Action,它会给定一组值。这种类型的业务逻辑必须要在某个地方进行实现。我们不能指望用户能够知道哪个 Action 是否可行。在这方面,没有其他的方式。不过,这种类型的业务逻辑很难编写、调试和维护,在没有语义对其进行描述时,更是如此,比如在 MVC 中就是这样。
让我们为火箭发射的样例编写一些代码。从 TLA+ 角度来讲,next-action
断言在逻辑上会跟在状态渲染之后。当前状态呈现之后,下一步就是执行 next-action 断言,如果存在的话,将会计算并执行下一个
Action,这个 Action 会将其数据交给 Model,Model 将会初始化新状态的表述,以此类推。
图5:火箭发射器的实现
需要注意的是,在客户端/服务器架构下,当自动 Action 触发之后,我们可能需要使用像 WebSocket 这样的协议(或者在 WebSocket 不可用的时候,使用轮询机制)来正确地渲染状态表述。
我曾经使用 Java 和 JavaScript 编写过一个很轻量级的开源库,它使用 TLA+
特有的语义来构造状态对象,并提供了样例,这些样例使用
WebSocket、轮询和队列实现浏览器/服务器交互。在火箭发射器的样例中可以看到,我们并非必须要使用那个库。一旦理解了如何编写,状态实现的编码
相对来讲是很容易的。
新模式——SAM 模式
对于要引入的新模式来说,我相信我们已经具备了所有的元素,这个新模式作为 MVC 的替代者,名为 SAM 模式(状态-行为-模型,State-Action-Model),它具有反应型和函数式的特性,灵感来源于 React.js 和 TLA+。
SAM 模式可以通过如下的表达式来进行描述:
V = S ( vm ( M.present ( A (data) ) ), nap (M))
它表明在应用一个 Action A 之后,View V 可以计算得出,Action 会作为 Model 的纯函数。
在 SAM 中,A(Action)、vm(视图-模型,view-model)、nap(next-action
断言)以及S(状态表述)必须都是纯函数。在 SAM 中,我们通常所说的“状态”(系统中属性的值)要完全局限于 Model
之中,改变这些值的逻辑在 Model 本身之外是不可见的。
随便提一下,next-action 断言,即 nap ()是一个回调,它会在状态表述创建完成,并渲染给用户时调用。
图7:“修改地址”的实现
模式中的元素,包括 Action 和 Model,可以进行自由地组合:
函数组合
data’ = A (B(data))
端组合(Peer)(相同的数据集可以提交给两个 Model)
M1.present (data’)
M2.present (data’)
父子组合(父 Model 控制的数据集提交给子 Model)
M1.present (data’,M2)
function present (data, child) {
// 执行更新
…
// 同步 Model
child.present (c(data))
}
发布/订阅组合
M1.on (“topic”, present )
M2.on (“topic”, present )
或
M1.on (“data”, present )
M2.on (“data”, present )
有些架构师可能会考虑到 System of Record 和 Systems of Engagement,这种模式有助于明确这两层的接口(图8),Model 会负责与 systems of record 的交互。
图8:SAM 组合模型
整个模式本身也是可以进行组合的,我们可以实现运行在浏览器中的 SAM 实例,使其支持类似于向导(wizard)的行为(如 ToDo 应用),它会与服务器端的 SAM 进行交互:
图9:SAM 实例组合
请注意,里层的 SAM 实例是作为状态表述的一部分进行传送的,这个状态表述是由外层的实例所生成的。
会话检查应该在 Action 触发之前进行(图 10)。SAM 能够启用一项很有意思的组合,在将数据提交给 Model 之前,View
可以调用一个第三方的 Action,并且要为其提供一个 token 和指向系统 Action 的回调,这个第三方 Action
会进行授权并校验该调用的合法性。
图 10:借助 SAM 实现会话管理
从 CQRS
的角度来讲,这个模式没有对查询(Query)和命令(Command)做特殊的区分,但是底层的实现需要进行这种区分。搜索或查询“Action”只是
简单地传递一组参数到 Model 中。我们可以采用某种约定(如下划线前缀)来区分查询和命令,或者我们可以在 Model 上使用两个不同的
present 方法:
{ _name : ‘/^[a]$/i’ } // 名字以A或a开头
{ _customerId: ‘123’ } // id=123 的 customer
Model 将会执行必要的操作以匹配查询,更新其内容并触发 View 的渲染。类似的约定可以用于创建、更新或删除 Model
中的元素。在将 Action 的输出传递给 Model
方面,我们可以实现多种方式(数据集、事件、Action……)。每种方式都会有其优势和不足,最终这取决于个人偏好。我更喜欢数据集的方式。
在异常方面,与 React 类似,我们预期 Model 会以属性值的形式保存异常信息(这些属性值可能是由 Action 提交的,也可能是 CRUD 操作返回的)。在渲染状态表述的时候,会用到属性值,以展现异常信息。
在缓存方面,SAM 在状态表述层提供了缓存的选项。直观上来看,缓存这些状态表述函数的结果能够实现更高的命中率,因为我们现在是在组件/状态层触发缓存,而不是在 Action/响应层。
该模式的反应型和函数式结构使得功能重放(replay)和单元测试变得非常容易。
SAM 模式完全改变了前端架构的范式,因为根据 TLA+ 的基础理念,业务逻辑可以清晰地描述为:
Action 是纯函数
CRUD 操作放在 Model 中
状态控制自动化的 Action
作为 API 的设计者,从我的角度来讲,这种模式将 API 设计的责任推到了服务器端,在 View 和 Model 之间保持了最小的契约。
Action 作为纯函数,能够跨 Model 重用,只要某个 Model 能够接受 Action 所对应的输出即可。我们可以期望 Action 库、主题(状态表述)甚至 Model 能够繁荣发展起来,因为它们现在能够独立地进行组合。
借助 SAM 模式,微服务能够非常自然地支撑 Model。像 Hivepod.io 这样的框架能够插入进来,就像它本来就在这层似得。
最为重要的是,这种模式像 React 一样,不需要任何的数据绑定或模板。
随着时间的推移,我希望能够推动浏览器永久添加虚拟 DOM 的特性,新的状态表述能够通过专有 API 直接进行处理。
我发现这个旅程将会带来一定的革新性:在过去的几十年中,面向对象似乎无处不在,但它已经一去不返了。我现在只能按照反应型和函数式来进行思考。我
借助 SAM 所构建的东西及其构建速度都是前所未有的。另外,我能够关注于 API 和服务的设计,它们不再遵循由前端决定的模式。
从横切到纵切,架构模式CQRS,提高系统进化能力
曾几何时,你是否疑惑于VO、PO、DTO、BO、POJO、Entity、MODEL的区别?
你是否有过疑问,为什么Java里有这么多的以O为名称结尾的对象?!
你是否也厌倦了编写从这个O对象到那个O对象之间的转换代码?!
你有没有想过,这一切的根源在哪里呢?有没有办法解决这个问题呢?
本文试图给你答案!
在架构风格:万金油CS与分层一文中提到,分层架构是个万金油架构,当你无法确定该使用哪种架构风格的时候,那么可以先使用分层架构。而实际上确实是这样,大部分的应用都采用了分层架构,特别是web应用。
以最简单的三层架构来说:
每一层都负责各自的任务、职责单一,开发也就相对简单。每一层相对独立,所以都能够独立进化,这是分层架构所宣称的优势!也是其「 原罪 」!
分层架构虽然将系统按层进行划分,但是层与层之间还是需要进行交互的。交互就需要有接口或协议以及传输的数据。
对于外部调用,我们可以使用TCP、HTTP、RPC、WebService等方式来进行通信;而对于内部交互来说,我们可以直接使用方法调用,使用接口来进行解耦。
但是传输的数据结构该如何定呢?
各层的独立进化,导致了交互的额外操作!这就是分层架构的「 原罪 」!也是需要这么多传输对象的其中一个原因!
而另外一个原因是 表现力差异 !
在领域设计:聚合与聚合根聊到了表现力问题,「数据设计」的表现力要弱于「对象设计」!相对应的,其实「数据展现」的表现力也是弱于「对象设计」的!
我们还是以订单来举例!假设我下单购买了多个商品,也就是说一个订单包含了多个明细。那么订单与订单明细的这层关系在「持久层」是通过主键来表现的:
订单明细包含了订单的主键,表示哪些订单明细是属于哪个订单的。
而这层关系在「逻辑层」是通过对象引用来表现的:
订单对象中持有了指向订单明细列表的引用。
而到了「展示层」,订单和订单详情之间的关系就完全靠展示方式来表现了:
如果你不了解业务,光看代码,是看不出订单与订单明细之间的关系的。上面只是纯粹的展示了订单明细在订单信息的下面。
也就是说,当我们访问页面的时候, 请求从「持久层」将扁平的数据查询到了「逻辑层」,组装成了结构化的对象,最后被传递到了「展现层」,又被拍扁了展示在我们面前 。
由于每层表现形式的不同,亦导致了需要数据传输对象。
既然横向封层不可避免的需要数据传输对象来解耦各层之间的关系,那我们是否不使用横向封层,而使用纵向切分呢?这就是CQRS架构模式!
CQRS通过对系统进行纵向切分:将「数据读」和「数据写」分离开,使得数据读写独立进化,来 解决数据显示复杂性问题 。
CQRS架构如下:
流程如下:
这又什么优势呢?
我们以订单保存和展示流程来详细的看一下CQRS的优势!
对于普通分层架构来说,在保存订单时需要一个DTO用于存储相关信息,然后转成多个对应的Model来进行持久化;而查询订单的时候,你需要查询出多个Model,然后组装成另一个DTO来存储查询的信息,因为展示的时候可能要展示更多的信息,比如买家和卖家相关信息。
同时由于数据都存储在数据库中,且表结构与Model是对应的,你能做的优化就是数据库相关的优化手段。
而在CQRS中,数据库被分成了读库和写库。那存在读库中的数据结构就可以完全按照展示逻辑来优化,比如:我可以有一张订单展示表,表中包含了买家信息和卖家信息。在展示时,直接查询这张表就可以了,不需要和用户表进行关联查询,提高了数据读性能。
而对于数据持久化来说,就不需要考虑数据展示了,只要提高持久化性能就可以了。例如不使用数据库,而使用顺序写入的文件方式。同时也不一定要存储数据本身,转而存储事件,就可以实现事件重演,这就是事件溯源。
在领域设计:Entity与VO一文中,提到了「状态」!
一般我们处理状态都是直接去修改它,像下面这样:
那么请问,这个开关刚才经历了什么?!这是典型的ABA问题,即你只知道这个开关目前的状态,但是它曾经有没有开过或关过,你就无从得知了。
我们对数据的处理也是这样,你只知道当前存在数据库中的数据是什么,而它曾经被修改过没有?被修改成过什么,你无从知晓。
因为我们存的只是「 即时状态 」,即「 快照 」!
事件溯源存储的不是数据「快照」,而是「 事件本身 」!即它记录了所有对该数据的事件。
如果你了解Redis的持久化方案,你对事件溯源就一定不会感到陌生。Redis有两种持久化方式RDB方式和AOF方式:
我们一般的持久化方式实际对应的就是Redis的RDB方式,而事件溯源就是AOF方式。
回到上图,在CQRS中,WriteDB可以通过类AOF的方式来存储命令,也就是事件溯源。当需要对ReadDB中的数据进行恢复操作时,可以通过命令重演的方式来恢复。
不过你应该发现问题了,命令重演的方式性能上有问题。所以我们可以参考Redis,使用快照+事件溯源的方式来存储。即WriteDB中存储事件,额外再定时对数据进行快照备份。恢复数据时先通过快照备份恢复,再从指定位置进行命令重演,来提高性能。
读写分离后,导致的一个问题就 是读写一致性 。在原来的分层架构中,数据写入后再读取,是可以立即读取到写入的数据的(事务保障)。
但是读写分离后,读到的数据不一定是写入的最新数据。一般情况下,这个问题并不大。因为 实际上你读的基本上都是 历史 数据 !为什么这么说呢?因为你没法保证数据在展现到你面前的过程中,没有新的写入。除非展示是基于推送机制的。
但是对于特殊情况下,可能不能容忍这样的情况。有几种解决方案:
一个mvc框架的appsettings怎么设置
MVC的辉煌过去与现存问题在每个用户界面背后,我们都在使用MVC模式,也就是模型-视图-控制器(Model-View-Controller)。MVC发明的时候,Web尚不存在,当时的软件架构充其量是胖客户端在原始网络中直接与单一数据库会话。但是,几十年之后,MVC依然在使用,持续地用于OmniChannel应用的构建。Angular2正式版即将发布,在这个时间节点重估MVC模式及各种MVC框架为应用架构带来的贡献意义重大。我第一次接触到MVC是在1990年,当时NeXT刚刚发布InterfaceBuilder(让人惊讶的是,如今这款软件依然发挥着重大的作用)。当时,我们感觉InterfaceBuilder和MVC是一个很大的进步。在90年代末期,MVC模式用到了HTTP上的任务中(还记得Struts吗?),如今,就各个方面来讲,MVC是所有应用架构的基本原则。MVC的影响十分深远,以致于React.js在介绍他们的框架时都委婉地与其划清界限:“React实现的只是MVC中视图(View)的部分”。当我去年开始使用React的时候,我感觉它在某些地方有着明显的不同:你在某个地方修改一部分数据,不需要显式地与View和Model进行交互,整个UI就能瞬间发生变化(不仅仅是域和表格中的值)。这也就是说,我很快就对React的编程模型感到了失望,在这方面,我显然并不孤独。我分享一下AndreMedeiros的观点:React在很多方面都让我感到失望,它主要是通过设计不佳的API来引导程序员[…]将多项关注点混合到一个组件之中。作为服务端的API设计者,我的结论是没有特别好的方式将API调用组织到React前端中,这恰恰是因为React只关注View,在它的编程模型中根本不存在控制器。到目前为止,Facebook一直致力于在框架层面弥合这一空白。React团队起初引入了Flux模式,不过它依然令人失望,最近DanAbramov又提倡另外一种模式,名为Redux,在一定程度上来讲,它的方向是正确的,但是在将API关联到前端方面,依然比不上我下面所介绍的方案。Google发布过GWT、AndroidSDK还有Angular,你可能认为他们的工程师熟知何为最好的前端架构,但是当你阅读Angular2设计考量的文章时,便会不以为然,即便在Google大家也达成这样的共识,他们是这样评价之前的工作成果的:Angular1并不是基于组件的理念构建的。相反,我们需要将控制器与页面上各种[元素]进行关联(attach),其中包含了我们的自定义逻辑。根据我们自定义的指令如何对其进行封装(是否包含isolatescope?),scope会进行关联或继续往下传递。基于组件的Angular2看起来能简单一点吗?其实并没有好多少。Angular2的核心包本身就包含了180个语义(Semantics),整个框架的语义已经接近500个,这是基于HTML5和CSS3的。谁有那么多时间学习和掌握这样的框架来构建Web应用呢?当Angular3出现的时候,情况又该是什么样子呢?在使用过React并了解了Angular2将会是什么样子之后,我感到有些沮丧:这些框架都系统性地强制我使用BFF“页面可替换模式(ScreenScraping)”模式,按照这种模式,每个服务端的API要匹配页面上的数据集,不管是输入的还是输出的。弃用MVC之后怎么走?此时,我决定“让这一切见鬼去吧”。我构建了一个Web应用,没有使用React、没有使用Angular也没有使用任何其他的MVC框架,通过这种方式,我看一下是否能够找到一种在View和底层API之间进行更好协作的方式。就React来讲,我最喜欢的一点在于Model和View之间的关联关系。React不是基于模板的,View本身没有法请求数据(我们只能将数据传递给View),看起来,针对这一点进行探索是一个很好的方向。如果看得足够长远的话,你会发现React唯一的目的就是将View分解为一系列(纯粹的)函数和JSX语法:它实际上与下面的格式并没有什么差别:V=f(M)例如,我当前正在从事项目的Web站点,Gliiph,就是使用这种函数构建的:图1:用于生成站点Slider组件HTML的函数这个函数需要使用Model来填充数据:图2:支撑slider的Model如果用简单的JavaScript函数就能完成任务,我们为什么还要用React呢?虚拟DOM(virtual-dom)?如果你觉得需要这样一种方案的话(我并不确定有很多的人需要这样),其实有这样的可选方案,我也期望开发出的方案。GraphQL?并不完全如此。不要因为Facebook大量使用它就对其产生误解,认为它一定是对你有好处的。GraphQL仅仅是以声明的方式来创建视图模型。强制要求Model匹配View会给你带来麻烦,而不是解决方案。React团队可能会觉得使用“客户端指定查询(Client-specifiedqueries)”是没有问题的(就像反应型团队中那样):GraphQL完全是由View以及编写它们的前端工程师的需求所驱动的。[…]另一方面,GraphQL查询会精确返回客户端请求的内容,除此之外,也就没什么了。GraphQL团队没有关注到JSX语法背后的核心思想:用函数将Model与View分离。与模板和“前端工程师所编写的查询”不同,函数不需要Model来适配View。当View是由函数创建的时候(而不是由模板或查询所创建),我们就可以按需转换Model,使其按照最合适的形式来展现View,不必在Model的形式上添加人为的限制。例如,如果View要展现一个值v,有一个图形化的指示器会标明这个值是优秀、良好还是很差,我们没有理由将指示器的值放到Model中:函数应该根据Model所提供的v值,来进行简单的计算,从而确定指示器的值。现在,把这些计算直接嵌入到View中并不是什么好主意,使View-Model成为一个纯函数也并非难事,因此当我们需要明确的View-Model时,就没有特殊的理由再使用GraphQL了:V=f(vm(M))作为深谙MDE之道的人,我相信你更善于编写代码,而不是元数据,不管它是模板还是像GraphQL这样的复杂查询语言。这个函数式的方式能够带来多项好处。首先,与React类似,它允许我们将View分解为组件。它们创建的较为自然的界面允许我们为Web应用或Web站点设置“主题”,或者使用不同的技术来渲染View(如原生的方式)。函数实现还有可能增强我们实现反应型设计的方式。在接下来的几个月中,可能会出现开发者交付用JavaScript函数包装的基于组件的HTML5主题的情况。这也是最近这段时间,在我的Web站点项目中,我所采用的方式,我会得到一个模板,然后迅速地将其封装为JavaScript函数。我不再使用WordPress。基本上花同等的工夫(甚至更少),我就能实现HTML5和CSS的最佳效果。这种方式也需要在设计师和开发人员之间建立一种新型的关系。任何人都可以编写这些JavaScript函数,尤其是模板的设计人员。人们不需要学习绑定方法、JSX和Angular模板的语法,只掌握简单的JavaScript核心函数就足以让这一切运转起来。有意思的是,从反应型流程的角度来说,这些函数可以部署在最合适的地方:在服务端或在客户端均可。但最为重要的是,这种方式允许在View与Model之间建立最小的契约关系,让Model来决定如何以最好的方式将其数据传递给View。让Model去处理诸如缓存、懒加载、编配以及一致性的问题。与模板和GraphQL不同,这种方式不需要从View的角度来直接发送请求。既然我们有了一种方式将Model与View进行解耦,那么下一个问题就是:在这里该如何创建完整的应用模型呢?“控制器”该是什么样子的?为了回答这个问题,让我们重新回到MVC上来。苹果公司了解MVC的基本情况,因为他们在上世纪80年代初,从XeroxPARC“偷来了”这一模式,从那时起,他们就坚定地实现这一模式:图3:MVC模式AndreMedeiros曾经清晰地指出,这里核心的缺点在于,MVC模式是“交互式的(interactive)”(这与反应型截然不同)。在传统的MVC之中,Action(Controller)将会调用Model上的更新方法,在成功(或出错)之时会确定如何更新View。他指出,其实并非必须如此,这里还有另外一种有效的、反应型的处理方式,我们只需这样考虑,Action只应该将值传递给Model,不管输出是什么,也不必确定Model该如何进行更新。那核心问题就变成了:该如何将Action集成到反应型流程中呢?如果你想理解Action的基础知识的话,那么你应该看一下TLA+。TLA代表的是“Action中的逻辑时序(TemporalLogicofActions)”,这是由Dr.Lamport所提出的学说,他也因此获得了图灵奖。在TLA+中,Action是纯函数:data’=A(data)我真的非常喜欢TLA+这个很棒的理念,因为它强制函数只转换给定的数据集。按照这种形式,反应型MVC看起来可能就会如下所示:V=f(M.present(A(data)))这个表达式规定当Action触发的时候,它会根据一组输入(例如用户输入)计算一个数据集,这个数据是提交到Model中的,然后会确定是否需要以及如何对其自身进行更新。当更新完成后,View会根据新的Model状态进行更新。反应型的环就闭合了。Model持久化和获取其数据的方式是与反应型流程无关的,所以,它理所应当地“不应该由前端工程师来编写”。不必因此而感到歉意。再次强调,Action是纯函数,没有状态和其他的副作用(例如,对于Model,不会包含计数的日志)。反应型MVC模式很有意思,因为除了Model以外,所有的事情都是纯函数。公平来讲,Redux实现了这种特殊的模式,但是带有React不必要的形式,并且在reducer中,Model和Action之间存在一点不必要的耦合。Action和接口之间是纯粹的消息传递。这也就是说,反应型MVC并不完整,按照Dan喜欢的说法,它并没有扩展到现实的应用之中。让我们通过一个简单的样例来阐述这是为什么。假设我们需要实现一个应用来控制火箭的发射:一旦我们开始倒计时,系统将会递减计数器(counter),当它到达零的时候,会将Model中所有未定的状态设置为规定值,火箭的发射将会进行初始化。这个应用有一个简单的状态机:图4:火箭发射的状态机其中decrement和launch都是“自动”的Action,这意味着我们每次进入(或重新进入)counting状态时,将会保证进行转换的评估,如果计数器的值大于零的话,decrementAction将会继续调用,如果值为零的话,将会调用launchAction。在任何的时间点都可以触发abortAction,这样的话,控制系统将会转换到aborted状态。在MVC中,这种类型的逻辑将会在控制器中实现,并且可能会由View中的一个计时器来触发。这一段至关重要,所以请仔细阅读。我们已经看到,在TLA+中,Action没有副作用,只是计算结果的状态,Model处理Action的输出并对其自身进行更新。这是与传统状态机语义的基本区别,在传统的状态机中,Action会指定结果状态,也就是说,结果状态是独立于Model的。在TLA+中,所启用的Action能够在状态表述(也就是View)中进行触发,这些Action不会直接与触发状态转换的行为进行关联。换句话说,状态机不应该由连接两个状态的元组(S1,A,S2)来进行指定,传统的状态机是这样做的,它们元组的形式应该是(Sk,Ak1,Ak2,…),这指定了所有启用的Action,并给定了一个状态Sk,Action应用于系统之后,将会计算出结果状态,Model将会处理更新。当我们引入“state”对象时,TLA+提供了一种更优秀的方式来对系统进行概念化,它将Action和view(仅仅是一种状态的表述)进行了分离。我们样例中的Model如下所示:model={counter:,started:,aborted:,launched:}系统中四个(控制)状态分别对应于Model中如下的值:ready={counter:10,started:false,aborted:false,launched:false}counting={counter:[0..10],started:true,aborted:false,launched:false}launched={counter:0,started:true,aborted:false,launched:true}aborted={counter:[0..10],started:true,aborted:true,launched:false}这个Model是由系统的所有属性及其可能的值所指定的,状态则指定了所启用的Action,它会给定一组值。这种类型的业务逻辑必须要在某个地方进行实现。我们不能指望用户能够知道哪个Action是否可行。在这方面,没有其他的方式。不过,这种类型的业务逻辑很难编写、调试和维护,在没有语义对其进行描述时,更是如此,比如在MVC中就是这样。让我们为火箭发射的样例编写一些代码。从TLA+角度来讲,next-action断言在逻辑上会跟在状态渲染之后。当前状态呈现之后,下一步就是执行next-action断言,如果存在的话,将会计算并执行下一个Action,这个Action会将其数据交给Model,Model将会初始化新状态的表述,以此类推。图5:火箭发射器的实现需要注意的是,在客户端/服务器架构下,当自动Action触发之后,我们可能需要使用像WebSocket这样的协议(或者在WebSocket不可用的时候,使用轮询机制)来正确地渲染状态表述。我曾经使用Java和JavaScript编写过一个很轻量级的开源库,它使用TLA+特有的语义来构造状态对象,并提供了样例,这些样例使用WebSocket、轮询和队列实现浏览器/服务器交互。在火箭发射器的样例中可以看到,我们并非必须要使用那个库。一旦理解了如何编写,状态实现的编码相对来讲是很容易的。新模式——SAM模式对于要引入的新模式来说,我相信我们已经具备了所有的元素,这个新模式作为MVC的替代者,名为SAM模式(状态-行为-模型,State-Action-Model),它具有反应型和函数式的特性,灵感来源于React.js和TLA+。SAM模式可以通过如下的表达式来进行描述:V=S(vm(M.present(A(data))),nap(M))它表明在应用一个ActionA之后,ViewV可以计算得出,Action会作为Model的纯函数。在SAM中,A(Action)、vm(视图-模型,view-model)、nap(next-action断言)以及S(状态表述)必须都是纯函数。在SAM中,我们通常所说的“状态”(系统中属性的值)要完全局限于Model之中,改变这些值的逻辑在Model本身之外是不可见的。随便提一下,next-action断言,即nap()是一个回调,它会在状态表述创建完成,并渲染给用户时调用。图7:“修改地址”的实现模式中的元素,包括Action和Model,可以进行自由地组合:函数组合data’=A(B(data))端组合(Peer)(相同的数据集可以提交给两个Model)M1.present(data’)M2.present(data’)父子组合(父Model控制的数据集提交给子Model)M1.present(data’,M2)functionpresent(data,child){//执行更新…//同步Modelchild.present(c(data))}发布/订阅组合M1.on(“topic”,present)M2.on(“topic”,present)或M1.on(“data”,present)M2.on(“data”,present)有些架构师可能会考虑到SystemofRecord和SystemsofEngagement,这种模式有助于明确这两层的接口(图8),Model会负责与systemsofrecord的交互。图8:SAM组合模型整个模式本身也是可以进行组合的,我们可以实现运行在浏览器中的SAM实例,使其支持类似于向导(wizard)的行为(如ToDo应用),它会与服务器端的SAM进行交互:图9:SAM实例组合请注意,里层的SAM实例是作为状态表述的一部分进行传送的,这个状态表述是由外层的实例所生成的。会话检查应该在Action触发之前进行(图10)。SAM能够启用一项很有意思的组合,在将数据提交给Model之前,View可以调用一个第三方的Action,并且要为其提供一个token和指向系统Action的回调,这个第三方Action会进行授权并校验该调用的合法性。图10:借助SAM实现会话管理从CQRS的角度来讲,这个模式没有对查询(Query)和命令(Command)做特殊的区分,但是底层的实现需要进行这种区分。搜索或查询“Action”只是简单地传递一组参数到Model中。我们可以采用某种约定(如下划线前缀)来区分查询和命令,或者我们可以在Model上使用两个不同的present方法:{_name:‘/^[a]$/i’}//名字以A或a开头{_customerId:‘123’}//id=123的customerModel将会执行必要的操作以匹配查询,更新其内容并触发View的渲染。类似的约定可以用于创建、更新或删除Model中的元素。在将Action的输出传递给Model方面,我们可以实现多种方式(数据集、事件、Action……)。每种方式都会有其优势和不足,最终这取决于个人偏好。我更喜欢数据集的方式。在异常方面,与React类似,我们预期Model会以属性值的形式保存异常信息(这些属性值可能是由Action提交的,也可能是CRUD操作返回的)。在渲染状态表述的时候,会用到属性值,以展现异常信息。在缓存方面,SAM在状态表述层提供了缓存的选项。直观上来看,缓存这些状态表述函数的结果能够实现更高的命中率,因为我们现在是在组件/状态层触发缓存,而不是在Action/响应层。该模式的反应型和函数式结构使得功能重放(replay)和单元测试变得非常容易。SAM模式完全改变了前端架构的范式,因为根据TLA+的基础理念,业务逻辑可以清晰地描述为:Action是纯函数CRUD操作放在Model中状态控制自动化的Action作为API的设计者,从我的角度来讲,这种模式将API设计的责任推到了服务器端,在View和Model之间保持了最小的契约。Action作为纯函数,能够跨Model重用,只要某个Model能够接受Action所对应的输出即可。我们可以期望Action库、主题(状态表述)甚至Model能够繁荣发展起来,因为它们现在能够独立地进行组合。借助SAM模式,微服务能够非常自然地支撑Model。像Hivepod.io这样的框架能够插入进来,就像它本来就在这层似得。最为重要的是,这种模式像React一样,不需要任何的数据绑定或模板。随着时间的推移,我希望能够推动浏览器永久添加虚拟DOM的特性,新的状态表述能够通过专有API直接进行处理。我发现这个旅程将会带来一定的革新性:在过去的几十年中,面向对象似乎无处不在,但它已经一去不返了。我现在只能按照反应型和函数式来进行思考。我借助SAM所构建的东西及其构建速度都是前所未有的。另外,我能够关注于API和服务的设计,它们不再遵循由前端决定的模式。
学习scala有哪些好的资源
为什么学习函数式编程
在阅读DDD巨著《Patterns, Principles, and Practices of Domain-Driven Design》的过程中,Scott在第5章提到了使用函数式编程语言配合贫血模型去实践DDD的一种思路,这激发了我的无限遐想。
在开发领域,我们已经拥有了许多的抽象方法论和大量的实现技术。但我个人认为,这一切归根结底,都是人类思维在开发领域的具体表达方式。而人类在认识和分析所要解决的业务领域问题时,思考的内容不外乎由两个部分组成:『业务流程』与『业务规则』。前者,回答了业务活动中先做什么后做什么的问题;后者,则回答了遇到什么情况时应该怎么做的问题。两者结合后,得到我们需要的业务结果,或者叫作“实现业务目标”。
再想想目前学习和掌握的面向对象的一系列方法,又是如何将上述思维结果映射到中去的呢?我认为是这样的:
对于业务流程,我们将其表达为若干对象之间的合作,比如UML里序列图的对象与消息,进而具化为具体的类及其职责,比如类及其若干业务方法。
对于业务规则,我们将其表达为若干的判断逻辑,比如UML流程图里的判断分支,进而具化为业务方法里的if-else语句,或者再复杂一点,表达为工厂、策略等设计模式的实际运用。
然后,我认为,对于复杂业务规则的梳理,可以象数学归纳法一样进行演绎:假设一个函数y=f(x),给定x的定义域,确定y的值域。特别是在排列组合等方面的一些问题,也经常采用递归的方式来解决。所以,从这个角度讲,函数式编程更贴近人类思维习惯,所以让我自然而然地把目光转向了它。
为什么选择Scala
在选择具体的函数式编程语言时,我首先想到的是它最好是同时能支持面向对象编程的。因为即便LISP作为函数式编程语言的先祖,诞生已长达半个世纪,但单纯的函数式编程语言与面向对象编程语言相比,在抽象领域概念、组合系统模块、实现信息隐蔽等方面存在一定的差距,所以一直没有成为开发的主流。
信息隐蔽原理:在西安电子科大蔡希尧与陈平老师于1993年合作出版的《面向对象技术》一书中是这样描述的:把需求和求解的方法分离;把相关信息——数据结构和算法,集中在一个模块之中,和其他模块隔离,它们不能随便访问这个模块内部的信息。
其次,由于我的语言路线是从Pascal → C → C++ → C#,所以我希望能选择一种风格近似于C、强类型的函数式编程语言。在比较了F#、R、ErLang等几种常见的函数式编程语言之后,我最终选择了Scala。
Scala有何优势
注:以下内容,节选翻译或参考自《Programming in Scala》第1章、第3章,《Programming Scala》第6章,不算完整意义上的个人心得。
函数式编程的优势
纯的函数是没有副作用的。无论何时何地,对于一个函数y=f(x),给定x必定得到y,不会因此产生二义结果。因此无论对于代码测试还是并发,由于给定输入必定得到预期输出,而不受其他因素干扰,所以能有效减少Bug产生。
在函数式编程里,大量使用immutable的值。这意味着函数运算的结果总会创建一个新的实例,避免了通常并发环境下为防止数据共享冲突而采取的保护机制。尽管这需要额外的Copy操作,但Scala针对性地提供了高效的Copy实现,以及延迟计算等弥补机制。
函数是一等公民。函数作为表达式的一部分,可以借由函数之间的嵌套、组合,实现复杂的判断逻辑。
Scala语言本身的优势
Scala是面向对象与函数式编程的混合语言,所以能有效结合二者的优点。
Scala属于Java生态圈,可以在JVM上与Java一起编译运行,所以许多Java的框架、工具都可以直接应用于Scala语言编写的项目。
Scala视一切数据类型皆为对象,且支持闭包、lambda、by-name参数等特性,语法简洁明快。
Scala使用Actor作为并发模型,与Akka框架自然契合。这是一种区别于传统的、基于数据共享、以锁为主要机制的并发模型,其特点在于以Actor为基本单位、没有数据共享、基于消息传递实现Actor之间的协作,因此可以有效避免死锁、减少竞争。
最后,如果有朝一日要转向大数据领域,有Spark这样的大型框架作为支撑。知乎:与 Hadoop 对比,如何看待 Spark 技术?
Scala对实践DDD有何意义
说了那么多,我的根本目的还是要将Scala作为实现DDD的主要武器。那么试想一下,Scala在我们实现DDD的过程中能有哪些帮助呢?我暂且胡侃乱诌如下:
表示值对象、领域事件等元素更直观。值对象、领域事件在DDD里都应该是immutable的,以往多采取POCO形式表示,现在改用Scala里的val以及case class表示,在语法层面就直观地表明是不可修改的。
在类的方法层面实现CQRS时有语法支持。用Scala里的Function(返回类型为非Unit)对应CQRS里uery,保证类的方法没有副作用;用Procedure(返回类型为Unit)对应CQRS里的Command,明确表明这一类方法会产生修改状态等副作用。这同样从语法层面就能对二者进行明确区分。
模式匹配丰富了函数操作。除了正则表达式,Scala形式多样的模式匹配语法,为提取数据、完成数据分组聚合等运算、实现逻辑判断提供了强大支持。比如定义def sum_count(ints:Seq[Int) = (ints.sum, ints.size)这样一个函数后,我们可以这样调用,以得到一个1至6的整数序列的整数值合计,及该序列的尺寸:val(sum, count) = sum_count(List(1, 2, 3, 4, 5, 6))。
为实现DSL提供有力支持。Scala自带有解析框架,加上灵活的函数语法支持,要自己实现一套DSL及其相应的语法解析器将不再困难。比如在配置文件里这样的一条配置语句,表示退休条件为年龄达到60周岁或者工龄届满30年:retire = (Age = 60) || (ServiceLength = 30)。以往的方式是自己写一个语法解析器,把这条文本转换成相应的Specification对象,然后扔给聚合去使用。现在有了Scala的帮助,就使编写语法解析器这一环节的工作量大大减少。
合理的高阶函数设计,使规则编写得到简化。比如规则、费用报销规则,以往可能需要若干层的if-else嵌套,现在则将通过高阶函数得到大幅简化。对此,我强烈刘光聪先生的Refactoring to Functions,你会在刘先生的重构过程中发现高阶函数的强大。
Actor为高效并发打下基础。Actor
内部完全自治,自带用于存储消息的mailbox,与其他Actor只能通过消息进行交互,每个Actor都是并发的一个基本单位。这些特点,非常适合于
采取Event
Sourcing方式实现的DDD。每个聚合都好比一个Actor,在聚合内部始终保持数据的强一致性,而在聚合之间交互的领域事件则好比Actor之间
的消息,聚合之间借由领域事件和Saga保证数据的最终一致性。
Trait成为AOP利器。Trait是Scala的另一大特色,它就象AOP织入一样,能动态地给某个类型注入方法或者结构。比如配合类Circuit和with后面那4个Trait的定义,val circuit = new Circuit with Adders with Multiplexers with Flipflops with MultiCoreProcessors这样就创建了一个带有加法器、乘法器、触发器和多核处理器的元件。
隐式实现为类型扩展提供支持。对应C#里的静态扩展方法,Scala通过implicit为实现数据类型的方法扩展提供了便捷,成为Trait之外的另一个功能扩展手段。
能降低常见BDD框架的学习成本。尽管这一点可能比较牵强,但我正在努力摸索如何将BDD与DDD结合,而常见的Cucumber、Spock等一些BDD框架,其语法与Scala比较相近,所以我才有如此一说。
有哪些Scala学习资料
以下是我目前主要的学习资料,并衷心欢迎各位留言补充。
书籍
Programming in Scala:由Scala语言的设计师Martin Odersky编写,循序渐进,配合了大量实例,入门必读吧。
Programming Scala:视角与上面那本有点不一样,没有Scala语言基础会感觉很困难,适合掌握了基本语法后温故而知新。
在线文档与
Scala 官方文档:Scala的,作为寻找资料的出发点是不错的。
Scala 课堂:中文版的Scala基本语法在线课堂。
Scala Synatax Primer:由Jim McBeath整理的Scala语法概要,可以当字典用。
The Neophyte’s Guide to Scala:很出名的一个Scala入门指南,以Scala中的提取器Extractor作为实例开始。
Scala 初学指南:这是上面那本指南的中译本。
Effective Scala:中文版的Scala高效编程
SBT中文入门指南:Scala Build Tool
社区
Scala 中文社区:不算活跃,原因你懂的。
Scala User:Scala入门者聚集地,没有Stack Overflow那么严格,但也需要点爬墙的身手。
SDK及IDE
Java SE:先装这个
Scala SDK:再装这个
SBT:然后装这个
IntelliJ IDEA:最后装这个,就能比较方便地开始Scala编程了
写在最后
最
近读的书很多也很杂,DDD、BDD、Scala、Cucumber以及Java基础等等都有涉及,真恨不得一口吃成个大胖子。由于时间和精力有限,所以
现在知识消化很成问题,迟迟没有进入学以致用的环节,只能先这样纸上谈兵了,好歹先把自己在学习过程中的一些思考、看到的好东西先记载下来,以备将来之
需。
Reactive Messaging Patterns with Actor Model — 1 从 Enterprise application说起
几年前我在前前一家公司任职Architect的时候,开始开启了我对大型系统架构设计的起点,当时跟我的leader 请教关於 系统之间的互动的设计概念,聊起了发展史。
通常一家持续发展中的企业,他的IT相关系统的建构面貌也几乎都跟他的企业发展面貌相近,在不同的时间点常常都是一小块一小块的被长出来,也因此常常都是彼此独立的silo并赋予它所被期待的目标。
1. 档案 — 透过指定的存取档案路径位置,让沟通的系统之间透过档案存取的锁的轮流掌握,进行资料的更新与获取。
2. 资料库 — 有了资料库以後,把资料库作为跨系统间资料状态变化的监控与获取一时间成了显学,不管三七二十一,反正我就是把存取的责任从系统卸载,不管是跨Schema 的授权存取,还是跨db之间的db-link 绑定仅读权限的控制,也都成功地度过了好一段时间。
3. 服务层的API — 陆续大家意识到了,直接暴露资料库存取层带来了很多隐忧,表结构产生变化时各个存取端都要一直跟著改动,於是产生了封装单一业务标的的念头,只是这API设计的粒度粗细一直也还没有很好的万用策略。有人主张从终端使用的角度来设计API ,有人主张从Domain Object 肩负的责任出发,更甚者是兼具两者,透过组装的方式把各个Domain Object提供的API 给串接呼叫,提供成为用户端使用的API。
4. 讯息模式 — 时间走到了ESB/SOA的年代,开始各种厂商大力倡导,透过消息队列(Message Queue)的方式为系统间的沟通进行分解,由於跨系统间的互动,除了一般业务应用标的用途,也常常伴随著资料格式的翻译解释,此外在实务上的跨平台间的消息沟通也有著通讯协定的包装与转换的处理,ESB/SOA可还真活了很长一段时间。
Reactive Messaging,强调的就是回归到真实世界的讯息互动的缩影的方式,每一个或多个参与在同一个事件的互动者(元素/系统),都会关心著他们想知道的话题事件何时被发生,以及事情发生了以後要怎麼因应,当然,若遇到了处理不了的部分可以怎麼样找到适当的协助方式。
像这样典型的事件驱动,讯息沟通模式,要回归到偶像Eric Evans在2003年发表的 Domain Driven Design,在DDD的思维模式下,主张要从Problem Domain出发,确立你要解决的问题的边界,从而找出:
快速基本理解DDD,可以看 这边 ,实际上DDD很深无法一下子看完的…
Domain model 找出来以後,大家开始思考这样的跨领域之间的讯息交互模式该是什麼样的方式,2005年另一个偶像Martin Fowler 基於当时对於DDD的理解认知,提出了CQRS 与 Event Sourcing 的设计样式作为实践DDD的参考,尤其Event Sourcing样式充分的支撑了DDD 的讯息跨domain的传递模式。
DDD强调,所有的问题本质,都该回归到以Ubiquitous Language 作为沟通的基础,把软体实践的设计重心都放在Domain Model上,对於当前你所使用的任何技术实践手段,都应该是属於偏於外围的/基底的,随时都可被抛弃与置换的,所以Eric Evans 也提出了他认为一个理想的系统设计架构原型
我记得在web 兴起的那个年代,各家大厂开始纷纷从 Client-Server 转往 WWW 的系统设计,每一家厂商都推出了符合於自有产品/设备/软体框架的最有力的推广话术,各种基於新的Web Design 的pattern 横空出世,最著名经典的 Java EE 系列的战争 → To apply EJB or not !
我印象中我以前也乖乖的K过 Core Java EE Pattern,还跟著他念了两个版本(基於不同时期的Java EE version稍有变形)。
人真的很容易被洗脑,我自己也不例外,在面对商用系统与强大的网路资讯传递过程中,决大部分的开发者的系统设计风格,都直接的与框架靠拢对齐,从而很自然的衍生出:
更甚者,你几乎可以在各大知名的框架工具的官方网站上,直接大咧咧地告诉你 Best Project structure convention
从套用这些框架的角度去看,再去回推对照比对Eric Evans 所提的Port And adapters 架构,是不是有种完全相反的感觉?
再回想一下刚开始接触程式设计以及物件导向语言的时候,我们都知道物件的组成是涵盖了属性与行为,但怎麼真的工作以後却发现好像写出来的代码都跟一开始的理解差很多,有一堆的DTO,有一堆的不知道要干嘛被工具gen出来的物件!
而且领域物件本身是有状态的,但WEB 类型的系统设计却都是无状态的
领域物件被我们设计出来之後,他的状态始终都只是被落地到资料库去(以交易型系统的设计来看,几乎都是如此) ~
至此,重新回归初心,再想想你年轻时第一次接触Object的时候的那个拟人化的学习描述方式 → 物件有动作,有责任,有资料,有代表的意义 !!
Reactive Messaging with Actor Model,正是要以此为起点,用这样基於DDD的基本思维模式来进行系统设计
javacqrs框架的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于cqrs架构实现、javacqrs框架的信息别忘了在本站进行查找喔。
发布于:2022-12-25,除非注明,否则均为
原创文章,转载请注明出处。