How we (should) do MVC
How we (should) do MVC
1. 框架图

2. 分层结构,使用IOC解耦各层,使各层仅接口依赖。
3. 考虑在EF与服务层中间加入Repository Pattern/Unit of Work Pattern并同时使用IOC
4. 从问题域或面向对象的视角划分层内的对象,比如Service层,不要把所有的服务接口定义及实现放入唯一类或文件中,而是需要划分。
5. Domain Model
5.1. 要Domain Model 根据需求分析从数据库角度出发使用面向对象思维采用EF Code First 方式建立POCO 类,尽量使用ID做主键,属性命名尽量符合EF惯用法,关联的需要延迟加载的类型对象使用virtual属性,集合使用ICollection.
5.2. 考虑 适当给Domain Model添加辅助方法以简化业务实现。
5.3. 考虑 适当给 Domain Model添加NotMapped属性以简化业务实现。
5.4. 考虑 适当给 Domain Model 添加外键属性以适合某些场景的应用。
5.5. 要 将数据约束建立在数据库表设计中,不要将UI检查约束属性放到DomainModel中,而是放到ViewModel中。
5.6. 要 类使用名词单数形式,集合属性使用名词复数形式。
6. EF
6.1. 要 使用EF4.1 Code First
6.2. 要 注意单复数形式
6.3. 要 遵循EF惯用规则,只在惯用规则不满足需求下设置映射关系。
7. Repository Pattern/Unit of Work Pattern
7.1. 要 在服务层和EF层加入Repository Pattern/Unit of Work Pattern
7.2. 要 在Repository Pattern/Unit of Work Pattern中使用接口与实现分离原则
7.3. 要 在服务层和本层接口间使用IOC/DI解耦紧依赖
8. 服务层
8.1. 要 在服务层使用接口与实现分离模式
8.2. 要 在服务层根据域模型面向对象地划分成多个服务,不允许全放在一个里。
8.3. 服务层调用Repository Pattern/Unit of Work Pattern层,并且使用IOC只能接口依赖,不能直接调用EF
8.4. 要 服务层接口函数中的多参数使用DTO模式传递,不要使用一长串参数的那种方式。DTO,数据传输对象,可以理解为ViewModel。
8.5. 要Repository Pattern接口中使用DomainModel做参数,服务调用Repository Pattern时通过AutoMapper在DTO和DomainModel间做转换。
8.6. 考虑 服务接口层返回ViewModel,并通过AutoMapper转换调用Repository得到的DomainModel再返回ViewModel。
9. Controller
9.1. 要 从资源的角度去划分Controller。既从域模型对象的视角去划分Controller,或者认为一个Controller对应一个表。
9.2. 要 使用Restful风格定义Action。因为从资源角度去划分Controller,那么一个Controller应该是对资源的CRUD操作的GET/Post,所以一个Controller不应该超过8个Action,理想情况的Controller命名应该是“DomainModel+Controller”,包括的Action有:
public ViewResult Index() 表示Get资源集合的View
public ViewResult Details(int id)表示Get指定id的资源对象的View
public ActionResult Create()表示Get资源对象的View
[HttpPost]
public ActionResult Create(ViewModel类型 model)表示Post创建资源对象
public ActionResult Edit(int id)表示Get修改资源对象的View
[HttpPost]
public ActionResult Edit(ViewModel类型 model)表示Post修改资源对象
public ActionResult Delete(int id)表示Get删除指定对象的View
[HttpPost]
public ActionResult Delete(int id)表示Post删除指定对象
9.3. 要 如果一个Controller的名字不是一个域模型对象的名字或者这个Controller的Action的数量超过上面描述的8个或者它的Action名字跟上面描述的大不同,说明没有从资源的角度去划分Controller,需要重新规划Controller。
9.4. 要 正确的明确的指定Action的Http method,特别是对于Post的
9.5. 不要 在Controller中定义任何私有方法,不要有任何除Get/Post之外的同名Action的重载
9.6. 要 Action的参数个数需要在0-2个之间。对于Action的长串多参数应该定义相应的ViewModel类并将参数作为该ViewMoel的属性,然后使用ViewModel作为参数。
9.7. 要 使用ViewModel作为Action的参数后,对于页面到Action的Viewmodel的参数数据绑定,首先要充分利用DefaultModelBinder的绑定规则绑定,如果不能满足,需要使用自定义ModelBinder绑定参数,不要在Controller内部在对字符串参数逗号处理或列表处理之类的参数绑定方法。
9.8. 要 Action的实现要简单,只做调度工作,既只是调用相应的服务接口并转交ViewModel给相应的View或者只检查ModelState.IsValid并调用相应的服务接口。一个Action函数里的代码应该在5行以内。
9.9. 不要 Action不要掺杂业务实现,也不应该知道view的东西,Action作为View和Service的中间人,它唯一的一个桥梁应该只有一个参数,那就是ViewModel
9.10. 要 Controller与服务层的依赖要使用IOC接偶,仅仅是接口依赖。
10. ViewModel
10.1. 要 要从View的数据视角去定义ViewModel。注意ViewModel和DomainModel是从不同的视角来定义的,DomainModel是从域模型(数据库)的角度定义的,而ViewModel是从页面数据的视角定义的,简单的说一个页面View展示或者提交的数据只有一个强类型的对象,那就是ViewModel,即一个View有且仅有一个对应的ViewModel。
10.2. 要 View是强类型的,即一个View的所有要展示或提交的数据只对应唯一一个定义的ViewModel类型,简单的理解就是Action及View只通过一个ViewModel绑定或提交,不要使用任何ViewBag,ViewData等动态类型或字典来传递数据。
10.3. 要 View的强类型说明你应该常用这样类型的代码,Get的Action中是return View(model), 相应的view中是@model ViewModel类型,而Post的Action只有一个参数,也是ViewModel类型。
10.4. 要 ViewModel中只包含View需要的数据,并且包含View所有需要的数据。
10.5. 要 在ViewModel中包含校验规则属性
10.6. 考虑 在ViewModel中定义辅助方法封装显示逻辑,以避免在View中实现逻辑导致难以测试。
10.7. 要 ViewModel的属性命名要参照相应的DomainModel,如果ViewModel中的属性有对应的DomainModel中相应概念的属性,则属性名一定要和DomainModel相同。
10.8. 要 对于页面Post的ViewModel在调用服务接口前需要对该ViewModel检查ModelState.IsValid, 并且前提是要在ViewModel中定义检查规则。
11. View
11.1. 要 View要是强类型的,一个View对应一个ViewModel,不使用ViewBag等
11.2. 不要 View不要包含业务及复杂的逻辑,如果在view中出现if/循环等逻辑处理,应该考虑封装HtmlHelper来封装相应业务,否则容易出错且不易测试。
11.3. 要 要综合使用layout/partial view等减少重复代码及处理框架
11.4. 要 一个view的数据模型应该是一个域模型或相关范围内,如果一个view包含太多不太相关的数据模型,则要考虑把这个view根据模型范围拆分成多个view,再通过partial组合,否则一个太大的view对应的viewmodel难以处理。

浙公网安备 33010602011771号