博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
REST与DDD
阅读量:6587 次
发布时间:2019-06-24

本文共 2460 字,大约阅读时间需要 8 分钟。

hot3.png

之前在我曾经提出是核心,REST是壳的观点,我想在这里详细谈谈我的思路。
今天正好看看到老外一篇博文Why  is so important:,他认为,REST的核心概念应该是Representional State Transfer,中文意思是将状态转移显现出来,该文举例:
Marcus是一个农民。他有一个牧场,有4头猪,12只鸡和3头牛。
那么模拟客户端与之交谈,那么我肯定首先询问牧场的状态:“状态?”
Marcus 回答:“有4头猪,12只鸡和3头牛”。
这是最简单的将状态显现的案例,Marcus用语句“有4头猪,12只鸡和3头牛”将他的牧场状态转给了我。
那么如何以方式让Marcus加两头牛到它的牧场呢?
我们经常会范的错误是,你会说:“Marcus, 请加两头牛到你的牧场”。
请注意,我们在这里转换了状态吗?没有,我们这里表达的是动词,有面向函数风格,但是这种表述方式其实是RPC( remote procedure call 远程过程调用),这个过程就是:加两头牛到牧场。
Marcus会悲伤地回答: "400错误, Bad Request. 你是什么意思?"
那么让我们以方式请求,原来状态是:4头猪,12只鸡和3头牛,增加了两头牛的状态是:4头猪,12只鸡和3头牛。
那我就会说:“Marcus, 4头猪,12只鸡和5头牛, Please ”
Marcus: "正确!".
我: "Marcus, ...那么你现在状态是什么?". 
Marcus: " 4头猪,12只鸡和5头牛".
我: "Ahh, 很好"
这才是真正。
原文还提到,如果你希望以RPC调用,那么SOAP是一种RPC方式,但是很重量,性能差。
这里,我想补充的是,这个案例让我们明白是如何显现状态的,那么在通用需求中,我们如何表达显现状态呢?
也就是说,REST是将什么状态转移显现出来?首先,我们想到的是应该是将业务逻辑的状态显现出来,而领域驱动设计就是分析设计业务逻辑的,那么,推理结果是,REST应该是将领域层聚合根实体的状态显现出来。
首先,我们看看是如何对需求分析设计的,大致步骤如下:
1.找出需求中的上下文边界。
2.根据上下文切分成模块。
3.从每个上下文中划出聚合边界
4.确定每个聚合边界内的聚合根
其中聚合根的状态是业务逻辑状态的核心所在,REST作为一个接口壳,应该透明地将聚合根实体的状态显现给客户端,客户端通过接受用户发出的命令,透过来修改聚合根的实体状态,从而达到实现业务功能的结果。如下图:
09103346_Xjoc.png
下面以转账案例来说明+DDD的结合:
客户端向发出转账请求的应该怎么写?
1.先发帐号A的扣除命令
2.再发帐号B的增加命令
这种方式其实不是状态表达,而是函数方法调用,是动词,是一种RPC,而方式应该是针对状态进行发出命令。
那么关键问题是,在这里寻找什么状态?有一种方式:
1.查询帐号A余额状态是10元
2.发出请求帐号A余额状态是5元(扣除5元,剩余5元)
3.查询帐号B余额状态是20元
4.发出请求帐号B余额状态是25元(增加5元)
这样很符合上面的牧场的对话方式。但是问题来了:
如果客户端扣除了A帐号钱后,不申请B帐号,从第三步以后没有了,那么我们后端服务器业务逻辑就不一致了。
所以,根据的聚合根用来维护聚合边界内一致性这个原则,显然,A帐号借出和B帐号贷入应该是借贷平衡的,这是基本财务规则约束,也就是一致性要求,是逻辑要求 。
如果我们这时进行建模,会发现这里有一个聚合根实体:转账Transaction。
下面是+DDD:
1. 客户端先GET获得当前帐号余额状态
2.客户端发出转账的POST命令:
POST /transactions
注意,需要加入参数 from=1&to=2&amount=500.00
见:
3.第二步的POST命令直接递交到转账服务,transactionService.
在转账服务中,有二种实现方式:
1. +面向函数风格,直接在转账服务的方法中实现,将源账户和目标帐号看成两个角色TransferMoneySourceAccount和TransferMoneyDestinationAccount,需要通过机制。见:
2.DDD聚合体+EventSourcing方式, 转账服务委托聚合根实现,TransactionAggregate作为聚合根实体,转账是这个实体的一个行为方法,转账命令到激活这个方法,在这个方法内部将产生一个转账事件。见:
一个的转账命令POST一般对应一个上游事件,一旦上游事件进入聚合根内部,变成很多事件流分支,这些事件流分支是改变了状态后发出的,因此必须被记录下来,出错回放才能重现状态转变历史。

var tx = new Transaction();tx.Post(amount, fromAccount, toAccount);transactionRepository.Store(tx);
这是调用Transaction这个聚合根的post方法。方法内部代码:
public void Post(decimal amount, string fromAccount, string toAccount){ this.Apply(new AccountDebited(amount, fromAccount)); this.Apply(new AccountCredited(amount, toAccount));}
将转账命令(转账上游事件)分为两个事件,AccountDebited和AccountCredited,账户借款和账户贷款,这样保证借贷平衡。然后根据借贷状态切换,还有更多子状态,EventStore应该是这些和状态直接有关的事件,间接有关事件没有必要记录,这样在事件回放时才能重现状态真实改变历史。

转载于:https://my.oschina.net/u/1246036/blog/151520

你可能感兴趣的文章
js解析json
查看>>
详解性能调优命令
查看>>
使用tar或dd等完成Linux系统备份恢复
查看>>
matlab的special函数用法
查看>>
函数指针和回调函数
查看>>
信号(signal)
查看>>
dns
查看>>
想打造一款成功的移动应用?你最需要关注性能指标!
查看>>
翻译 - 元编程动态方法之public_send
查看>>
ES6中的高阶函数:如同 a => b => c 一样简单
查看>>
C语言之枚举的定义以及测试
查看>>
35.函数介绍
查看>>
node主要应用场景是在大前端
查看>>
Linux的目录ls命令
查看>>
JDBC-简单连接Oracle Database
查看>>
mysql小问题集锦
查看>>
集群tomcat+session共享
查看>>
类火墙的iptables
查看>>
Linux初级入门百篇-LVM 简介
查看>>
MySQL--事务
查看>>