最新评论

共7页: 1 2 3 4 5 6 7 下一页 
温景良(Jason) 2012-05-24 22:08
哥,最近阅读您老人家的代码让我很头疼啊,不过你要是接着写博客的话还好!,谢谢啊!
xwy 2012-05-24 09:18
学习
安度 2012-04-12 02:10
ding 期待下wen’同时有很多疑问想请教’
水言木 2012-04-05 18:16
[quote]Leder:非常感谢,对CQRS理解又多了一些~[/quote] 互相讨论,互相学习 :)
Leder 2012-04-05 17:45
非常感谢,对CQRS理解又多了一些~
水言木 2012-04-05 16:36
[quote]Leder: Session["CurrentUserId"] = ??? 这部分是查出来的,不是通过Command返回,呵呵,所以在设计Command的时候确实是没不必要加复杂的返回值。 CQRS模式是把命令查询职责分开,命令部分只是完成增删改之类的操作,通过储存命令并通过事件模式异步处理真正的数据库。通过查询(链接真正的数据库或快照)查询处理后的结果。 如果理解错了望指正。[/quote] Command最理想状态是不返回值,像文中的例子,如果需求如此,那就不得以要让他返回一些东西,这时候就只能尽量让返回的东西越简单越好。Command返回执行结果对整个架构的主要影响是:Command将不能异步执行。
水言木 2012-04-05 16:33
[quote]Leder: Session["CurrentUserId"] = ??? 这部分是查出来的,不是通过Command返回,呵呵,所以在设计Command的时候确实是没不必要加复杂的返回值。 CQRS模式是把命令查询职责分开,命令部分只是完成增删改之类的操作,通过储存命令并通过事件模式异步处理真正的数据库。通过查询(链接真正的数据库或快照)查询处理后的结果。 如果理解错了望指正。[/quote] 命令不只是完成增删改,其实也可以认为命令是最终客户可理解的一个操作,比如修改密码,发布产品,修改价格,等等。这些名词都是客户可以直接理解的,这些可以分别对应一个Command。 你说的储存命令,不知是否笔误?在CQRS中是不保存命令的,如果使用Event Sourcing,那会保存事件(非命令)。 这里我认为要分两种情况: 1. 不使用Event Sourcing(事件溯源): 这即是本文中例子的情况,Event Sourcing并不是实现CQRS所必须的技术,而且Event Sourcing一般比较难,故这一系列文章的例子都是用最简单的CQRS,这样对未接触过CQRS的人比较好入手:)。不用Event Sourcing,则不会去保存事件,这样我们的数据库就会至少有两个,一个是存储领域模型的数据库(下面称Write DB),另一个是用于查询的数据库(Read DB),Command执行时,会触发一些事件,而这些事件的某些订阅者会负责将相关数据同步到Read DB。 2. 使用Event Sourcing: 这种情况中,将不存在1中的Write DB,取而代之的是Event Store,所以这种情况中将不会像原来那个保存Product、Order等,而是保存每个被触发的事件。而这些事件的某些订阅者,将会负责把相关数据同步到Read DB,这和1是一样的。 文中返回命令执行结果(新创建的用户的Id)并不是查询,那个Id是直接生成的,我们只是要让UI有能力去知道这个生成的ID是什么。 [b]这个ID不适合用一个独立的查询去做,因为没有办法保证查询到正确的值。[/b]这个我文中也有提到,因为在执行完命令的那一刹那,完全有可能有其它人注册了,那我们再查询Id只会查询到其它人的Id,这就直接影响到程序的正确性了。 如果说当执行完命令后界面要显示一个最新用户列表,那这时候就要按你说的利用查询去做这个事。
Leder 2012-04-05 16:08
Session["CurrentUserId"] = ??? 这部分是查出来的,不是通过Command返回,呵呵,所以在设计Command的时候确实是没不必要加复杂的返回值。 CQRS模式是把命令查询职责分开,命令部分只是完成增删改之类的操作,通过储存命令并通过事件模式异步处理真正的数据库。通过查询(链接真正的数据库或快照)查询处理后的结果。 如果理解错了望指正。
水言木 2012-03-30 11:33
[quote]Leder: @水言木 好像你的二、三个都是正确的吧。 int ret=CommandBus.Send<RegisterCommand, int>(registerCommand); string ret=CommandBus.Send<RegisterCommand, string>(registerCommand); 其实这种方式也是有约束的,约束在于你的XXXCommondExecutor实现ICommandExecutor<TCommand, TResult>的时候已经指定了返回类型。 我看了Tiny Library的Command部分只返回bool,他的持久化是由事件部分去完成,好...[/quote] 其实还有第三种方案:让RegisterCommand去实现ICommand<string>,即添加ICommand<TResult>的接口,它同样也是空接口。但我感觉这种实现比较不优雅,没有文中的第二种方案来的简洁明了。
水言木 2012-03-30 11:31
[quote]Leder: @水言木 好像你的二、三个都是正确的吧。 int ret=CommandBus.Send<RegisterCommand, int>(registerCommand); string ret=CommandBus.Send<RegisterCommand, string>(registerCommand); 其实这种方式也是有约束的,约束在于你的XXXCommondExecutor实现ICommandExecutor<TCommand, TResult>的时候已经指定了返回类型。 我看了Tiny Library的Command部分只返回bool,他的持久化是由事件部分去完成,好...[/quote] 补充一点,嘿嘿。 这个约束的问题在于,对于Controller的开发人员来讲,他并不知道应该把它约束成string,因为CommandExecutor可能在重构过程中改成返回一个对象,不仅包含了UserId,而且还包含了其它一些信息,这样Controller的代码还是不变,而且编译不会出错,但在运行时,Controller中就会有找不到CommandExecutor的异常,这类bug不好调试。而第二种方案,我们用命名和属性的方式直接指明了返回值是RegisterCommandResult,如果将来修改了这个返回值类型,那Controller就会编译错误,第二种方案将返回值类型直接绑定到了Command上,而第一种不是。第二种方案也可以说是通过更好的命名来消除开发中的歧义。
水言木 2012-03-30 11:24
[quote]Leder: @水言木 好像你的二、三个都是正确的吧。 int ret=CommandBus.Send<RegisterCommand, int>(registerCommand); string ret=CommandBus.Send<RegisterCommand, string>(registerCommand); 其实这种方式也是有约束的,约束在于你的XXXCommondExecutor实现ICommandExecutor<TCommand, TResult>的时候已经指定了返回类型。 我看了Tiny Library的Command部分只返回bool,他的持久化是由事件部分去完成,好...[/quote] 如果用第一种方案,RegisterCommandExecutor将会实现ICommandExecutor<RegisterCommand, string>接口(因为User.Id是string类型,而我们希望这个Command的返回值是UserId,所以第二个泛型参数用string),所以,对于一和二,CommandBus会找不到相应的CommandExecutor,只有用第三种调用时,才会找到CommandExecutor并执行。 CommandExecutor返回一些值来表示Command执行是成功还是失败是没有问题的,本文讨论的问题是"执行结果"(如例子中的UserId)的返回,执行结果会因Command的不同而不同。
Leder 2012-03-30 11:08
@水言木 好像你的二、三个都是正确的吧。 int ret=CommandBus.Send<RegisterCommand, int>(registerCommand); string ret=CommandBus.Send<RegisterCommand, string>(registerCommand); 其实这种方式也是有约束的,约束在于你的XXXCommondExecutor实现ICommandExecutor<TCommand, TResult>的时候已经指定了返回类型。 我看了Tiny Library的Command部分只返回bool,他的持久化是由事件部分去完成,好像是这样,还在研究中。。。
水言木 2012-03-30 09:21
[quote]Leder: 我昨天写了一下返回值的代码,使用的是第一种方案,有个不好就是不能设置返回空值类型(void),从这种CQRS实现来说(因为反射后一个CommandExecutor只能对应一个Command,所以不存在多个返回类型的可能)第二种方法也是不错的。 关注博主的事件驱动。[/quote] 嘿嘿,感谢关注。 感觉第一种方案的最大问题在于,返回什么类型是由调用者来决定的,比如文中的例子,Controller中可以写成: CommandBus.Send(registerCommand) 也可以写成 CommandBus.Send<RegisterCommand, int>(registerCommand) 也可以写成 CommandBus.Send<RegisterCommand, string>(registerCommand) 这些全都可以编译通过,但是只有第三个是正确的。一旦这样的代码多起来,代码就会变得难以理解和维护。
Leder 2012-03-30 08:22
我昨天写了一下返回值的代码,使用的是第一种方案,有个不好就是不能设置返回空值类型(void),从这种CQRS实现来说(因为反射后一个CommandExecutor只能对应一个Command,所以不存在多个返回类型的可能)第二种方法也是不错的。 关注博主的事件驱动。
我想我是青蛙 2012-03-28 23:48
@水言木 是这样的。。。本质来说。。。对于太复杂的东西俺驾驭不了,呵呵
john23.net 2012-03-28 14:01
不错的
水言木 2012-03-28 11:40
[quote]dax.net: @redhat_gg 我之前开发了一个CQRS的案例系统,地址是:[url=http://tlibcqrs.codeplex.com]http://tlibcqrs.codeplex.com[/url],有兴趣可以去了解一下。[/quote] 嘿嘿,曾经研究这个时还请教过你一些问题:D 现在我也临时写了份代码放上去了。感觉tlibcqrs对于没接触过CQRS的人来说跨度还有点大,比如使用了Event Sourcing等,本系列打算从最简单的CQRS入手实现一个基本例子,然后再考虑是否研究高级主题 :P PS: 不知dax大虾对文中“业务逻辑”那一段我的疑问有何看法?期待你的建议
水言木 2012-03-28 11:37
[quote]我想我是青蛙: 一直觉得这套架构把程序的开发搞的过于复杂。。。 可能比较适合大型网站吧。。。个人看法[/quote] 如果是小网站或小系统,就不合适使用CQRS,可能也不适合使用DDD,小型系统用快速开发的方式做有时可能还更好。要应用CQRS,最基本的得有相对复杂的业务逻辑。
水言木 2012-03-28 11:35
[quote]redhat_gg:所有代码都贴上 ,或者提供下载研究可能更好[/quote] 谢谢建议,已添加代码下载,3Q :)
dax.net 2012-03-28 10:30
@redhat_gg 我之前开发了一个CQRS的案例系统,地址是:[url=http://tlibcqrs.codeplex.com]http://tlibcqrs.codeplex.com[/url],有兴趣可以去了解一下。
共7页: 1 2 3 4 5 6 7 下一页