Mvc的多层架构
分享一个Mvc的多层架构,欢迎大家拍砖斧正
多层架构是什么?
多层架构是开发人员在开发过程当中面对复杂且易变的需求采取的一种以隔离控制为主的应对策略,关于多层架构的标准,我认为有一句话是比较有代表性的“每一层都可以单独部署”,最传统,最简单的就是从三层开始的:
将整个项目自下而上的分为:数据持久(数据访问)层,逻辑(业务)层,UI(展现)层。
数据访问层:负责将数据持久化响应的数据存储设备上,如DataBase,Txt,Excel等。
业务逻辑层:负责处理为满足软件需求而订制的一系列的逻辑与业务,如用户在前端下订单之后,整个业务流可能涉及到,获取用户信息,获取商品信息,获取购物车信息,验证商品可购买数量是否满足本次购买,针对用户身份产生不同的优惠策略,同时会验证Cookie,Session等端产生数据的有效性,最终才会产生订单,而订单产生之后会涉及到仓储物流等一系列的Erp系统业务,所有的这一套都属于“下订单”这一需求的业务逻辑。
展示层:负责与用户交互的界面,良好的用户体验多是使用在这里。
学习过Petshop的话,对于三层都不会陌生:
但是随着业务的复杂每一层都会有自己的进化,最终有了无数附加在三层之上的框架与开发思想。
Mvc与MVP:
首先我一直认为这两种事属于展现层的,“展现层MCV”,“展现层MVP”。
然后我们站在展现层的角度思考一下“Mvc”与“MVP”。
Mvc:分为model,Controller,View,相信大家对于他已经很熟悉了,在此不再累述。
MVP:MVP有Model-Presenter-View三个层次
其实在楼主最开始接触Mvc的时候,就在想如果直接通过Controller与Model交互是不是显得有一些“不干净”,因为在楼主眼里“展现层的Controller”,做得最多的应该就是对于请求路由的不同响应与调用,但是很多的例子会将一些数据验证,去糟的操作过程放在Controller中,显得不伦不类。当MVP出现的时候,一切满足了楼主的幻想,P的过程就是满足了这一需求,P起到中介的作用,负责接收视图请求,再把结果映射到view上,P可以不对View做强引用,可通过IView适配多个view。当然我也会在这里做一些针对于终端数据的验证与过滤。
业务逻辑:
从描述上可以看的很清楚,整个自上而下的结构,最复杂,最可能失控的就是业务逻辑层,因为其中包含着许多的不可控因素,每个行业领域的需求都有可能包含自身的领域知识。于是在之后的多层架构发展构成当中,更多的变化与智慧是体现在这里。
领域驱动:限于本人才学不能在这里分享太多,以防误导大家,想了解更多可参考园子里的其他大牛,其实没有3,5年相关经验是很难理解的,个人感觉如果你不理解的话也不会对你有什么影响,因为领域驱动是建立在良好的面相对象分析,边界划分基础之上的,在学习的过程当中已经能帮助你去学习到足够多的知识了,最终到不到山巅其实已经无所谓了。
简单的说,这个思想最重要的是以业务领域为核心进行发散,期望在变更程序的其他部分,不会影响到领域模型,也就是那句话为了“复杂的系统应用程序中业务规则行为方式(就是“领域逻辑”)是会经常变化的,我们要去拥抱这种变化”。结构图:
CQRS:是指命令查询职责的分离,是一个小的模式形态,该模式的关键在于:“一个方法要么是用来改变某个对象的状态的,要么就是返回一个结果,这两者不会同时并存”。将整个系统分拆为两个部分:
- Commands(命令) - 改变某一个对象或整个系统的状态(有时也叫做modifiers或者mutators)。
- Queries(查询) - 返回值并且不会改变对象的状态。
架构图:
不管DDD也好,CQRS也好,其实这两种都不会100%适合所有的项目架构的,这就需要架构师结合项目本身特点及需求有所选择,但是其中的思想我们可以运用在项目的任何地方。
基于消息的分布式:
其实不管使用怎样的架构,加入怎样的架构思想(soa),核心或者是开发者最想达到的就是层次,系统之间的解耦,复杂的东西没人会喜欢。
随着系统的发展,我们的程序会涉及到多台服务器,多种终端,同时为了解耦我们引入了基于消息的分布式架构。
首先,所以系统的通信基于消息,逻辑联系不会涉及到具体的业务实现,同时消息的传递更加的廉价可适配多种终端。
其次,由于所用逻辑只是基于消息实现,迭代的成本也会相对于其他耦合项目更快更方便。
展示层:
随之Web2.0的到来单一页面展示的信息也更加的丰富,Ajax,js的流行也使得Ui端的操作也愈加变重,于是大家有期望以一种工程的思想去拥抱这种变化,于是MVVM,js的Mvc框架陆续出现。同时随着移动互联网的兴起,不同终端对于系统的对接也非常重要,于是我们考虑在Ui与Logic之间引入Application或Service层应对不同终端配置。
如:我们在Client Presenter Layer 上加入WCF适配多种终端提交的订单,都是建立在消息基础之上的,楼主之前做电商系统是针对于来自淘宝,天猫,亚马逊订单时,为避免出现对库中订单并发,产生“超买”情况,采用了在上层Ui与logic层之间引入了OrderChannel层,将不同终端订单进行排队的解决方案。
以上是架设一个能够适配不同需求的架构过程,但是真正的真理是需要大家在实践中,错误中汲取的。
下面是楼主简单的小分层架构,不妥,不足之处希望大家指导斧正。
层次划分:
为了实现单独部署,层次解耦所以层次之间是基于接口实现的。
DataAccess层引入仓储实现统一DTO操作,实现基于Ef:
IRepository:
public interface IRepository<T> where T:class
{
IEnumerable<T> FindAll(Expression<Func<T,bool>> exp);
void Add(T entity);
void Delete(T entity);
void Submit();
}
引入RepositoryBase实现接口定义:
public class RepositoryBase<T>:IRepository<T> where T:class
{
DbContext context;
public RepositoryBase(DbContext _context)
{
context = _context;
} public RepositoryBase() {
this.context = new TestDBEntities();
} public IEnumerable<T> FindAll(Expression<Func<T, bool>> exp)
{
return context.Set<T>().Where(exp);
} public void Add(T entity)
{
context.Set<T>().Add(entity);
} public void Delete(T entity)
{
context.Set<T>().Remove(entity);
} public void Submit()
{
context.SaveChanges();
}
}
这对于单一的某个仓储我们单独引入其自身的仓储接口:
public interface IUserRepository:IRepository<UserTest>
{
IList<UserTest> GetAllById(int id); bool CheckUserExist(UserTest u);
}
特定仓储实现:
public class UserRepository : RepositoryBase<UserTest>,IUserRepository
{
public IList<UserTest> GetAllById(int id)
{
using (TestDBEntities entities=new TestDBEntities())
{
var users = from u in entities.UserTests
where u.ID == id
select u;
return users.ToList();
}
} public bool CheckUserExist(UserTest u)
{
using (TestDBEntities entities = new TestDBEntities())
{
List<UserTest> users = entities.UserTests.Where(ut => ut.UserName == u.UserName && ut.UserPassword==u.UserPassword).ToList<UserTest>();
return users.Count==0 ? false : true;
}
}
}
在Service层同样建立相关接口适配特种服务:
IUserCore:
public interface IUserCore
{
CommandStatueEnum UserLogin(IModel model);
CommandStatueEnum UserRegister(IModel model); List<UserTest> GetUsers(Expression<Func<UserTest, bool>> expr);
}
UserCore:
public class UserCore : IUserCore
{
#region Structure
IUserRepository _repository;
public UserCore(IUserRepository repository) {
this._repository = repository;
}
#endregion public CommandStatueEnum UserLogin(IModel model)
{
try
{
UserLogin u = model as UserLogin;
UserTest uTest = new UserTest();
uTest.UserName = u.UserName;
uTest.UserPassword = u.Password; if (_repository.CheckUserExist(uTest))
{
return CommandStatueEnum.Succeed;
}
else
{
return CommandStatueEnum.Fail;
}
}
catch (Exception ex) {
throw ex;
}
} public CommandStatueEnum UserRegister(IModel model)
{
try
{
UserLogin u = model as UserLogin;
UserTest uTest = new UserTest() { UserName=u.UserName, UserPassword=u.Password};
_repository.Add(uTest);
_repository.Submit();
return CommandStatueEnum.Succeed;
}
catch (Exception ex)
{
throw ex;
}
} public List<UserTest> GetUsers(System.Linq.Expressions.Expression<Func<UserTest, bool>> expr=null)
{
return _repository.FindAll(expr).ToList<UserTest>();
}
}
Controller:
public class AccountController : Controller
{
IUserCore userCore;
public AccountController(IUserCore _userCore)
{
this.userCore = _userCore;
} //
// GET: /Account/ #region view
public ActionResult Home()
{
ViewBag.Users = userCore.GetUsers(u=>u.IsUse==1);
return View();
} public ActionResult Login()
{
return View();
} public ActionResult Register()
{
return View();
}
#endregion #region Post
[HttpPost]
public ActionResult Login(UserLogin account)
{
try
{
if (userCore.UserLogin(account) == CommandStatueEnum.Succeed)
{
return RedirectToAction("Home");
}
else
{
return View();
}
}
catch (Exception ex)
{
ExceptionModel.IsExcept = true;
ExceptionModel.Exception = ex.ToString();
ExceptionModel.CreateTime = DateTime.Now;
return View();
}
} [HttpPost]
public ActionResult Register(UserLogin account)
{
try
{
if (userCore.UserRegister(account) == CommandStatueEnum.Succeed)
{
return RedirectToAction("Home");
}
else
{
return View();
}
}
catch (Exception ex)
{
ExceptionModel.IsExcept = true;
ExceptionModel.Exception = ex.ToString();
ExceptionModel.CreateTime = DateTime.Now;
return View();
}
}
#endregion
}
对于接口之间我们通过引入IOC工具解耦:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas(); WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
AuthConfig.RegisterAuth(); #region IOC
var builder = new ContainerBuilder();
SetupResolveRules(builder);
builder.RegisterControllers(Assembly.GetExecutingAssembly());
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
#endregion
} private void SetupResolveRules(ContainerBuilder builder)
{
//Components are wired to services using the As() methods on ContainerBuilder
builder.RegisterType<UserCore>().As<IUserCore>();
builder.RegisterType<UserRepository>().As<IUserRepository>();
} }
其他基础类库我们会结合具体需求进行定制,上面例子多有不妥之处只起演示之用。
综上所述,本篇只起抛砖引玉之用,还请大牛拍砖指导。
Mvc的多层架构的更多相关文章
- 转载Mvc的多层架构
Mvc的多层架构 分享一个Mvc的多层架构,欢迎大家拍砖斧正 多层架构是什么? 多层架构是开发人员在开发过程当中面对复杂且易变的需求采取的一种以隔离控制为主的应对策略,关于多层架构的标准,我认为有 ...
- 分享一个MVC的多层架构,欢迎大家拍砖斧正
如果你对项目管理.系统架构有兴趣,请加微信订阅号"softjg",加入这个PM.架构师的大家庭 多层架构是开发人员在开发过程当中面对复杂且易变的需求采取的一种以隔离控制为主的应对策 ...
- 如何让MVC和多层架构和谐并存(一)
MVC的架构和多层架构,在ORM框架上是不兼容的.MVC的数据库操作需要通过实体框架Entity Framework,多层的数据库操作需要通过DAL层.我们最近刚完成的项目,实现了MVC和多层的并存, ...
- 如何让MVC和多层架构和谐并存(二)
上一节说了一些笼统的东西,这节说一些实际的操作. 1.取列表.这是一个新闻列表: 对应MVC的model是: public class NewsListModel { /// < ...
- 多层架构+MVC+EF+AUTOFAC+AUTOMAPPER
最近使用ligerui搭建了一个简单的教务管理demo,将重要的地方记录,也希望能帮到有这方面需要园友. 一.目录 1.多层架构+MVC+EF+AUTOFAC+AUTOMAPPER: 2.MVC中验证 ...
- MVC 5 Scaffolding多层架构代码生成向导开源项目
asp.net MVC 5 Scaffolding多层架构代码生成向导开源项目(邀请你的参与) Visual Studio.net 2013 asp.net MVC 5 Scaffolding代码 ...
- 【转】浅谈MVC与三层架构
首先给大家引入下MVC的概念: MVC(Model View Controller)模型.视图以及控制器,它是一种较为广泛应用的结构设计模式. 模型:就是在MVC设计模式中需要被显示的数据.在通常情况 ...
- .NET跨平台之mac 下vs code 多层架构编程
合肥程序员群:49313181. 合肥实名程序员群:128131462 (不愿透露姓名和信息者勿加入,申请备注填写姓名+技术+工作年限) Q Q:408365330 E-Mail:eg ...
- MVC与三层架构的区别
我们平时总是将三层架构与MVC混为一谈,殊不知它俩并不是一个概念.下面我来为大家揭晓我所知道的一些真相. 首先,它俩根本不是一个概念. 三层架构是一个分层式的软件体系架构设计,它可适用于任何一个项目. ...
随机推荐
- [jQuery1.9]Cannot read property ‘msie’ of undefined错误的解决方法
原文:[jQuery1.9]Cannot read property 'msie' of undefined错误的解决方法 $.browser在jQuery1.9里被删除了,所以项目的js代码里用到$ ...
- C++使用简单的函数指针
函数指针: 被调用函数指针必须包括函数的存储器地址,为了正常工作,指针还必须包括其它信息,这一参数列表指针的参数类型和返回类型的函数. 因此,当你声明一个函数指针,数的參数类型和返回类型.以及指针名. ...
- RSA JS 加密解密DEMO
<script src="Scripts/jquery-1.4.1.js" type="text/javascript"></script&g ...
- Ansible@一个有效的配置管理工具--Ansible configure management--翻译(四)
不要未经书面许可转载 第三章是长,因为,我会分几个部分来翻译. Advanced Playbooks So far the playbooks that we have looked at are s ...
- DynamicReports 导出Excel 例子
import java.awt.Color; import java.io.FileOutputStream; import java.util.ArrayList; import java.util ...
- [LeetCode] 033. Search in Rotated Sorted Array (Hard) (C++)
指数:[LeetCode] Leetcode 解决问题的指数 (C++/Java/Python/Sql) Github: https://github.com/illuz/leetcode 033. ...
- 【百度地图API】批量地址解析与批量反地址解析(带商圈数据)
原文:[百度地图API]批量地址解析与批量反地址解析(带商圈数据) 摘要:因为地址解析的webserives方式还没有开通,所以先用JS版本的地址解析接口来批量获取地址解析数据吧,同时还能得到商圈的数 ...
- Windows系统下Redis的安装
Redis是一个用的比较广泛的Key/Value的内存数据库,新浪微博.Github.StackOverflow 等大型应用中都用其作为缓存,Redis的官网为http://redis.io/. 最近 ...
- 深入struts2(三)---工作机制和运行流程图
1 工作原理 1.1 体系架构 图2.1 struts2.0体系架构图 1.2 工作机制 针对上节体系架构图,以下分步说明运行流程 Ø client初始化一个指向Servle ...
- AngularJS模块的详解
AngularJS模块的详解 在讲angularjs的模块之前,我们先介绍一下angular的一些知识点: AngularJS是纯客户端技术,完全用Javascript编写的.它使用的是网页开发的常规 ...