转载Mvc的多层架构

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>();
        }

    }
复制代码

其他基础类库我们会结合具体需求进行定制,上面例子多有不妥之处只起演示之用。

综上所述,本篇只起抛砖引玉之用,还请大牛拍砖指导。

 

从搭建电商系统引申出来的知识拓扑图

 

起因

由于最近一直在研究和储备做云计算服务方向的基础知识,突然发现自己的知识并没有形成体系。同时也不知道该系统的去学习些什么,总之就是有一些琐碎的知识但没有主干。楼主一直认为知识只有使用才会有价值,有意义,否则也只能是实验室里的东西。所以不管是在分析业务,思考职责分离或者技术选型时,总会强调“使用场景”。因为只有明确了“使用场景”,才能帮助我们更好的思考合适的解决方案,于是想从搭建一个电商系统开始,思考围绕整个系统会涉及的知识,从而扩展出新的知识,完成系统学习的目的。最终不会搭建出来一个完整的系统,更多的是学习完成一个类似系统会有的知识与脉络。

为什么选择电商系统

因为楼主认为在系统层面来讲电商系统应该是比较全面的系统了,涉及到常见一些内部系统,如:仓储,物流,财务,商品管理,订单管理等等,而且有时会针对于不同的级别客户,不同商品,不同供应商等都会存在不同的对应策略,甚至可以夸张的说,唯一不变的就是变化。

当然也不会将一个系统搞的太京东太淘宝,我们还是关注于解决问题以及实现业务。

下面是一个常见的电商系统功能拓扑:

前端常见的样子,提供商品展示、搜索引擎、商品推荐、网站促销、购买支付、账户中心、资讯公告等模块。

会有对应的后台管理系统,提供商品管理、采购管理、销售管理、退换货管理、财务管理、库存管理、客户管理、内容管理、数据分析等模块。

辅助功能:

有些系统只是简单的搭建起商品展示,订单及商品管理,就可以了,仓储会对接现有的ERP系统。但是有些电商系统需要一套有效的数据作为支撑,这样的系统会区别于小型而简单的系统,在系统之外会有N多的引擎为整个电商系统服务。

B2C电子商务平台辅助系统

– 支付接口系统             提供支持货到付款、在线支付、订单POS机等收款方式。支付接口丰富,支持支付宝、财付通、银联在线、一网通等第三方支付系统

– 比价系统             商品自动比价,实时抓取竞争对手价格信息,提供调价决策建议

– 营销数据接口系统             跟踪网盟、搜索、媒体、弹窗、CPS等外部广告效果,辅助营销决策

– 短信、邮件网关系统             支持短信、邮件通知、促销

– 第三方商品推广平台接口系统             支持丰富的营销推广平台,包括E淘、百度商品搜索、腾讯QQ钱包等

– 淘宝、天猫第三方网店接口系统                         连通淘宝、天猫,支持淘宝商品、订单接口

– O2O商户管理系统 提供商户入驻、商品管理、销售管理、退换货管理等功能模块

– 财务及ERP接口系统             提供和SAP/R3、用友、金蝶等财务系统凭证接口,极大提高财务自动化程度,减少出错

– 数据挖掘及决策支持系统             基于云计算的海量数据挖掘,并提供报表、分析、辅助决策等系统

当然这一套下来价格也是不菲的,但是如果将这些外包给其他公司开发很难好用。

首先外包公司不太会针对你的系统做更加深入合理化的调查。

其次,想达到什么样的目的,需求应该是客户自己更清楚。

所以如果一个公司有志做电商还是在后端引擎开发商组建一个自己的团队吧。

针对于客户的卖点:

其实在商品出来之前,用户很难说出自己想要什么,但是作为开发商还是需要一些噱头去满足客户的胃口的,常见的会这样说:

此商务系统采用基于微软公司的.Net系统平台开发全套解决方案,保证了系统的先进性、安全性、扩展性、稳定性、伸缩性,支持分布式缓存系统,最大支持日访问量500万人次,10万订单,满足超大型客户需求

  • 基于Web2.0的大型电子商务解决方案,采用大量Ajax技术提高用户体验;
  • 基于云计算的系统架构,符合SaaS(软件即服务)设计理念;
  • 基于中文分词和产品型号分词的高速产品搜索引擎,支持10万个并发搜索;
  • 海量数据挖掘功能,根据客户网站访问行为为客户提供强大的购物推荐功能;
  • 强大的商品展示、搜索、推荐系统;
  • 全面的商品促销活动形式(团购、秒杀、限时抢购、特卖、拍卖、满赠、抽奖、优惠券、积分等);
  • 集成进销存ERP、仓储物流WMS,支持多分公司、多仓库等大型电子商务应用 ;
  • 商品销售支持实库、虚库、赊销、代销,经营模式灵活多样 ;
  • 支持新浪微博、QQ、支付宝等第三方帐号登录;
  • 全面的SEO优化功能 ;
  • 超高安全性,采用各种加密安全技术,保证交易安全,和国家一级信息安全资质服务企业长期紧密合作,保证系统安全可靠。

好的UI:

Ui是系统展现给客户的第一印象,其实在小的外包公司,这点更加重要。甚至有些客户只是因为Ui问题就选择其他家。但是忽视Ui的重要性却是很多后端程序员常见的问题,关于审美,大家照顾漂亮的妹子就Ok了,她会改变你的。

以后有时间我们就围绕这么多的需求和业务有的放矢的做分析和开发了,东西很多,不清楚的也很多,加油。

 
 
 
原文地址:https://www.cnblogs.com/Leo_wl/p/3642160.html