.NET:关于数据模型、领域模型和视图模型的一些思考
背景
数据模型、领域模型和视图模型是“模型”的三种角色,一些架构用一种类型表示这三种角色,如:传统三层架构。也有一些架构用两种类型表示这三种角色,如:结合ORM的领域驱动架构。非常少见的场景是用三种类型表示这三种角色,我只在个别领域这么弄过,如:工作流引擎。
今天只说一个话题:是否有必要为视图模型引入独立的类型?还是用一种类型表达领域模型和视图模型这两种角色比较方便?引入一些词汇:
- A方案:用一种类型表达领域模型和视图模型这两种角色,又叫公开领域模型到视图(Open Domain To View)。
- B方案:为视图模型引入独立的类型,又叫使用数据传输对象(DTO)。
A方案
因为领域模型和视图模型是一个类型,所以领域模型会从UI进行重建,因为领域模型会从UI进行重建,而UI层是不能相信的,所以必须对领域模型进行验证(包含IsValid()方法),而且领域的很多方法都是修复领域模型的非法状态,如:重新计算订单总额、加密未加密的密码属性等等。
代码示例
internal sealed class TestGridCommandHandler : ApplicationService,
ICommandHandler<CreateTestGrid>,
ICommandHandler<UpdateTestGrid>,
ICommandHandler<DeleteTestGrid>
{
public void Handle(CreateTestGrid command)
{
var testGridService = this.Service<TestGridService>(); testGridService.Create(command.TestGridInfo);
command.Id = command.TestGridInfo.Id;
} public void Handle(UpdateTestGrid command)
{
var testGridService = this.Service<TestGridService>(); testGridService.Update(command.TestGridInfo);
} public void Handle(DeleteTestGrid command)
{
this.Service<TestGridService>().Delete(command.Id);
}
}
注意看第二个方法,这里的command.TestGridInfo就是领域模型,从客户端重建后直接调用ApplicationService进行update,update负责修复模型状态、执行验证和处理乐观并发。
B方案
因为领域模型和视图模型是一个不同的类型,所以领域模型不会从UI进行重建,因为UI进行重建的只是视图模型, 所以要从数据库加载一份领域模型,然后将视图模型合并到领域模型中,这里的合并不是指用AutoMapper这样的合并工具,而是一种合理的合并过程(不能用反射绕过领域模型封装的逻辑),在这个合并过程,领域模型始终处于合法状态(也可以不合法,很多人都这么弄,保留IsValid()方法即可)。
代码示例
internal sealed class TestOrderCommandHandler : ApplicationService,
ICommandHandler<CreateTestOrder>,
ICommandHandler<UpdateTestOrder>,
ICommandHandler<DeleteTestOrder>
{
public void Handle(CreateTestOrder command)
{
var testOrderService = this.Service<TestOrderService>(); var testOrder = command.CreateTestOrder(); testOrderService.Create(testOrder);
command.Id = testOrder.Id;
} public void Handle(UpdateTestOrder command)
{
var testOrderService = this.Service<TestOrderService>(); var testOrder = testOrderService.Repository.Load(command.Id);
testOrder.CheckOptimisticKey(command.TestOrderInfo.OptimisticKey); command.UpdateTestOrder(testOrder);
testOrderService.Update(testOrder);
} public void Handle(DeleteTestOrder command)
{
this.Service<TestOrderService>().Delete(command.Id);
}
}
注意看第二个方法,这里先用Repository从数据库返回一个领域模型,执行乐观锁检查,用视图模型修改领域模型(不是简单的反射),然后调用ApplicationService进行Update。
备注
只看代码大家可能觉得A方案比较简单,而B方案视乎有点脱裤子放屁的感觉,我之前都是用的A方案,开发效率确实高,但是应对比较复杂的逻辑就非常不爽了,具体为啥不爽我还没有想明白。
我现在非常有信心用好任何一个方案,因为一个高人告诉我:关注代码细节胜于关注这些架构上的问题。
结合四色原型,我觉得可以这样弄:PPT和DES用A方案,MI用B方案。
.NET:关于数据模型、领域模型和视图模型的一些思考的更多相关文章
- DDD:谈谈数据模型、领域模型、视图模型和命令模型
背景 一个类型可以充当多个角色,这个角色可以是显式的(实现了某个接口或基类),也可以是隐式的(承担的具体职责和上下文决定),本文就讨论四个角色:数据模型.领域模型.视图模型和命令模型. 四个角色 数据 ...
- MVC无限级分类01,分层架构,引入缓存,完成领域模型与视图模型的映射
本系列将使用zTree来创建.编辑关于品牌.车系.车型的无限级分类,使用datagrid显示,源码在github.先上最终效果: datagrid显示所有记录.分页,提供添加.修改.删除按钮,并提供简 ...
- 领域模型(DomainModel)与视图模型(ViewModel)
Model-View-Controller(模型-视图-控制器,MVC)模式将你的软件组织并分解成三个截然不同的角色: Model 封装了你的应用数据.应用流程和业务逻辑. View 从 Model ...
- KnockoutJS 3.X API 第二章 数据监控(1)视图模型与监控
数据监控 KO的三个内置核心功能: 监控(Observable)和依赖性跟踪(dependency tracking) 声明绑定(Declarative bindings) 模板(Templating ...
- Windows Phone 8初学者开发—第12部分:改进视图模型和示例数据
原文 Windows Phone 8初学者开发—第12部分:改进视图模型和示例数据 第12部分:改进视图模型和示例数据 原文地址:http://channel9.msdn.com/Series/Win ...
- Knockout v3.4.0 中文版教程-2-监控-通过监控创建视图模型(上)
2. 监控 1.通过监控创建视图模型 1. 监控 Knockout是基于以下三个核心特性: 监控和依赖跟踪 声明式绑定 模板 在本节,你将第一次了解这三个特性,在这之前,我们先来了解以下MVVM模式和 ...
- VO(视图模型) 与 DTO(数据传输对象)的区别
目录 VO(视图模型) 与 DTO(数据传输对象)的区别 1.VO与DTO概念 2.VO 视图模型的必要性与解耦 2.1 视图模型 2.2 视图模型存在的必要性 2.3 视图模型的解耦 3.DTO 存 ...
- 当类型为dynamic的视图模型遭遇匿名对象
当年在ASP.NET MVC 1.0时代我提到,在开发时最好将视图的Model定制为强类型的,这样可以充分利用静态检查功能进行排错.不过有人指出,这么做虽然易于静态检查,但是定义强类型的Model类型 ...
- [转]架构蓝图--软件架构 "4+1" 视图模型
架构蓝图--软件架构 "4+1" 视图模型 本文基于多个并发视图的使用情况来说明描述软件密集型系统架构的模型.使用多重视图允许独立地处理各"风险承担人":最终用 ...
随机推荐
- 在C中调用Matlab (转)
http://blog.163.com/rongting_chen/blog/static/164906844201252354518462/ http://www.ilovematlab.cn/th ...
- python -wordcloudan云词安装
1安装网站:https://download.lfd.uci.edu/pythonlibs/u2yrk7ps/wordcloud-1.4.1-cp36-cp36m-win_amd64.whl 2安装过 ...
- 张高兴的 Windows 10 IoT 开发笔记:使用 MAX7219 驱动数码管
This is a Windows 10 IoT Core project on the Raspberry Pi 2/3, coded by C#. GitHub:https://github.co ...
- Django 自定义模板标签和过滤器
1.创建一个模板库 使用模板过滤器的时候,直接把过滤器写在app里,例如:在app里新建一个templatetags的文件夹,这个目录应当和 models.py . views.py 等处于同一层次. ...
- 函数的类型:函数也是类型 (*)->*
函数的类型:函数也是类型 (*)->* 函数类型作为类型可以定义变量,使得函数变量具有可替代性,这个是高阶函数的编程基础. 使用函数的类型可以定义函数的变量,并用函数给这个变量赋值: 每一个函数 ...
- 什么是HOOK(钩子):消息拦截与处理
对于Windows系统,它是建立在事件驱动机制上的,说白了就是整个系统都是通过消息传递实现的.hook(钩子)是一种特殊的消息处理机制,它可以监视系统或者进程中的各种事件消息,截获发往目标窗口的消息并 ...
- 扯不清楚的virtual和abstract
定义Person类: class Person { public void Say() { Console.WriteLine("I am a person"); } } 现在,我 ...
- Win10右键添加获取管理员权限
在win10中,还是和WIN7/8一样很多文件被设置权限我们打不开,但很多时候我们还是要打开这些文件夹的,尤其是数据恢复和电脑维护优化时.于是乎我们就有必要获取管理员权限.本人隆重退出完美解决方法. ...
- 在centos7上搭建博客之小白教程~
原理 http使用方法一编译安装,php模块打入方式实现. 软件版本 在本次实验中,我们需要用到的软件版本如下: apr-1.6.2 apr-util-1.6.0 httpd-2.4.28 maria ...
- c++——静态成员变量成员函数
静态成员变量成员函数 思考:每个变量,拥有属性.有没有一些属性,归所有对象拥有? 4.1静态成员变量 1)定义静态成员变量 关键字 static 可以用于说明一个类的成员, 静态成员提供了一个同类对象 ...