0x00 起因
有段时间没写博客了,其实最近本来是根据梳理的MSDN上的资料(UWP开发目录整理)有条不紊的进行UWP学习的。学习中有了心得体会或遇到了问题就写一篇博客记录一下,方便后面查询。不过前几天在园子里逛看了几篇领域驱动的文章,突然发现领域驱动设计的有些地方对我有了很大的提示。在之前用WPF做桌面开发时,使用MVVM可以把View和Model很好的解耦,但在处理数据持久化的时候并没有找到一种特别好的方式。我之前的做法是把ADO封装了一层SQLHelper用于处理数据库操作,解耦了数据库操作和具体数据库类型的依赖,当然了前提是数据库操作是基于ADO.NET实现的。对于返回的DataTable,DataSet等在Model的方法中转换为Model的实例。例如有一个对象是User,要从数据库中获取所有用户我一般是在User类中写一个静态方法,User.GetAll(),这个方法会使用SQLHelper执行SQL语句,获取DataTable,然后把DataTable转换成IEnumerable<User>并返回,这算是人肉ORM吧。为此我还写了个代码生成工具,把类中需要的属性和属性对应的数据库表字段名称、类型等信息设置好,可以直接生成代码实现映射,省去了手动输入的麻烦,不过能生成的数据库操作类型十分有限。但这样做存在的一个很大的问题就是Model变得十分臃肿。虽然领域驱动提倡用充血模型,但我那种Model应该算是过度充血了吧。而且以现在的观点来看,这种方式把基础层(数据库操作)、ORM(ADO对象映射为User对象)、Model紧紧的粘合在了一起,如果突然有一天跟我说数据操作必须用WebAPI,我能做的大概就是把之前的代码Ctrl+K+C,然后重新从WebAPI获取数据,然后从JSON映射到Model吧。如果说需要数据库和WebAPI混用,我的Model将会变得更加臃肿。
领域驱动设计通过Repository实现了业务领域和基础层的数据库操作之间低耦合,结合之前MVVM模式带来的View和Model的低耦合,又恰巧最近在学习UWP开发,所以有了把MVVM和DDD在UWP开发中实践的想法,于是也有了这篇文章。在这里需要说明的是我也是刚开始接触领域驱动,其中的很多概念还没有接触到,看的比较多的就是Repository,毕竟数据持久化是我之前的痛点。后面我也会把数据驱动相关书设置为我的床头书,打算认真看一下,可能随着学习和对领域驱动的了解有些想法会发生改变,有了新的体会后面也会写文章,这篇文章主要记录了这个时期我对领域驱动的粗浅理解,有不对的地方也希望多多指教。
0x01领域驱动和MVVM模式我看到的关于领域驱动的文章很多都是讨论实践于ASP.NET MVC的,而且几乎都把ORM当作了必选项。而我大多数时间是做桌面开发的,所以看到领域驱动的第一反应是与MVVM模式的结合。那么MVVM和DDD有没有可能结合起来呢。
MVVM核心的三个部分是Model、View、ViewModel,重点解决的是通过ViewModel实现Model和View的低耦合(MVVM模式解析和在WPF中的实现),但有一个问题是没有解决的,那就是业务逻辑和数据持久化要如何处理。有人认为业务逻辑应该放在ViewModel中,Model应该是POCO对象,用于数据显示,业务逻辑可以通过ViewModel和Service实现。在这种观点中Model更偏向于DTO的存在。还有一种认为业务应当聚合到Model之下,这样更OO一点,难以聚合到Model下的就写成Service,我就是属于这种观点的,甚至于Model相关的所有业务逻辑和数据操作也全都聚合到了Model下。
而领域驱动注重的是业务领域这一层的分离,简直就是对MVVM的一种最好的补充。现在要再有人问MVVM中业务逻辑该放在哪里,我会毫不犹豫地告诉他业务逻辑要放在领域层。
从分层的思想来看,领域驱动设计为四层架构,分别是表示层、应用层、领域层和基础层。
接下来要讨论的领域驱动和MVVM的结合也是以领域驱动的这四层架构为基础的,把MVVM的三个核心概念融合到这四层架构中。下面我就结合MVVM谈一下自己对这几层的一些认识。
0x02 表示层和应用层首先表示层这个是最明确的,就是用户看到的那一层。对于桌面应用来说就是窗口,对于Web应用来说就是HTML页面。对应于MVVM模式中的View。
然后说下应用层,领域驱动设计认为应用层是非常薄的一层,应用层调用下面的领域层和基础层来实现功能。这个对于主张把业务逻辑聚合到Model下的我来说没什么违和感,在MVVM中,应用层对应的就是ViewModel。首先必须说明的是这里说的ViewModel和MVC中的ViewModel并不完全是一个概念,这两个ViewModel都是对View的抽象,但在MVC中引入ViewModel主要是为了与View对应,减少View的逻辑操作。这个ViewModel是对View中数据的抽象。在MVVM中ViewModel除了对View的数据抽象外还包含了对用户交互和功能等行为的抽象。例如MVVM中的ViewModel还需要处理用户的点击,输入等操作,MVVM中的ViewModel完全可以不包含业务细节,实现为很薄的一层。例如把大象放进冰箱大致分三步,ViewModel中的操作就是:
Fridge.Open();
Fridge.PutIn(elephant);
Fridge.Close();
至于冰箱空间不够放不下大象了需要抛出异常,能放下的话把大象放在哪里更合理,放入大象后是不是需要摆放空间优化等等都聚合到了Fridge这个Model中,ViewModel只是调用这些功能。这也符合领域驱动中提倡的充血模型。
在领域驱动设计的四层架构中,我们可以看到表示层是可以调用下面三层的,也就是说表示层可能对下面三层都会产生依赖。这样当表示层发生变化时下面三层中有的地方可能也需要做出改变,同样业务逻辑的一些变化可能也会影响到表示层。而在领域驱动中使用了MVVM模式后,表示层(View)依赖于ViewModel,对于ViewModel以下都不直接产生依赖。
0x03 领域层和基础层View和ViewModel都找到了对应的层了,那Model对应哪一层呢。这个问题就不好回答了,这个得看对Model的理解了。如果把ViewModel看作很薄的一层应用层,Model使用贫血的POCO对象,那么这时候MVVM中的Model更偏向于DTO的存在,这时候在领域层需要有另外的聚合了数据和逻辑的模型。如果把MVVM中的Model看作数据和业务聚合的充血模型,那么这个Model可以当作业务领域中的Model,这时候如果需要的话可以考虑加入DTO。不过感觉这么考虑有点太教条了。我觉得可把领域层模型看作MVVM中的Model。