背景
一个类型可以充当多个角色,这个角色可以是显式的(实现了某个接口或基类),也可以是隐式的(承担的具体职责和上下文决定),本文就讨论四个角色:数据模型、领域模型、视图模型和命令模型。
四个角色
- 数据模型:面向持久化,数据的载体。
- 领域模型:面向业务,行为的载体。
- 视图模型:面向UI(向外),数据的载体。
- 命令模型:面向UI(向内),数据的载体。
这是四种角色,可以由一至四个类型来承担,具体选择几个类型需要考虑项目的上下文,但不同的选择对编程的要求是不同的,下面举几个例子。
数据模型和领域模型采用统一个类型,采用EntityFramework进行持久化。
这种设计毫无疑问对这个类型是有侵入性的,即使采用了POCO,如果需要延时加载,也只能做到编译时的POCO,运行时还是会生成代理类型,某些成员需要生命为virtual,你还需要记得默认构造方法和属性的setter都会被EntityFramework在重建对象时候调用。一个类型承担了两个角色,就要完成两份职责(持久化和封装业务逻辑),这或许违背了单一职责原则,不过某些情况下这是最好的选择了。
四个角色采用一个类型
这种设计适合业务逻辑不是很复杂的场景,系统中只有CRUD,以四色原型为例,PPT和Des适合采用这种模式。因为这个类型要承担四个角色,所以没办法采用显式的封装技术,所有成员都是Public { get; set; },考虑到很多动态语言其实是没法封装的(是按照约定),或许C#开发者可以接受这种设计,比如:虽然集合被公开了,但是我们还是可以采用封装集合模式,外部调用的时候要根据约定采用封装过的接口,TDD有利于强制这种约定,示例代码:
1 public class Order 2 { 3 public List<OrderItem> Items { get; set; } 4 5 public void AddItem(OrderItem item) {} 6 }
为视图模型引入单独的类型
你采用了WCF技术,你的UI期望的数据和领域模型非常不匹配(也是一种阻抗不匹配),这时你就会引入视图模型。如何构造视图模型呢?一般来说有两种思路:一、用AutoMapper之类的工具从领域模型映射;二、采用轻量级的框架(PetaPoco)直接从表、视图和存储过程返回。
备注
今天只是大概聊了聊这样一个想法,下一步会写一些Demo验证几种不同的组合。