全面解析ASP.NET MVC模块化架构方案

什么叫架构?揭开架构神秘的面纱,无非就是:分层+模块化。任意复杂的架构,你也会发现架构师也就做了这两件事。

本文将会全面的介绍我们团队在模块化设计方面取得的经验。之所以加了“全面”二字,是因为本文的内容将会涉及到:数据库、路由、C#、JavaScript、CSS、HTML等一个完整模块所需要的内容。

在阅读本文之前后,你也可以转到我们的开源项目:https://github.com/leotsai/mvcsolution。这个开源项目完整的总结了我们团队在ASP.NET MVC领域的分层架构思想,同时也定义了很多标准。

本文的目的则主要是介绍架构思想之模块化。

当前项目架构以及模块化架构图

一个模块的运行通常要依托于一个完整的系统。在本文的例子中,模块化依托于我们的开源项目MvcSolution这个架构。下面先看看这个架构的工程引用关系图:

上面这个架构是系统原本的架构,而在这个原本的架构之上,我们怎么添加模块的呢?请看下图:

从上图可以看出,我们增加的模块是引用了系统原来的多个工程的,这虽然会增加模块与当前系统的耦合度,但是却让模块的开发变得简单很多。而对于我们团队来讲,因为所有项目都是基于MvcSolution这个架构,所以每一个模块都可以很快的融入每一个系统。

下面在看看添加了模块之后的项目文件结构:

由于我们这个模块不算大,所以就把所有C#文件都放到一个工程里面了,分层只在心中。这也有利于我们将这个模块搬运到其他项目中。

1. 模块的入口

我们模块的入口非常简单,没有反射,也没有动态初始化,就是一个再普通不过的System.Web.MvcAreaRegistration。 在Global.ascx.cs文件中,通过AreaRegistration.RegisterAllAreas();把所有引用的Area全部注册。

在这个AreaRegistration类中,主要完成了路由的注册以及IOC的注册,如下图:

这样,这个模块的公开URL地址就已经生成了,如:http://www.example.com/donating。你可以在程序其他地方添加这个链接,那么用户就可以访问这个模块了。

2. 模块化之数据访问

模块内的数据访问如何跟现有系统的数据访问共存,这个问题是所有模块化架构师最难处理的问题,相比其他文件(C#、JS、CSS、HTML),数据访问最难处理好,因为下面这个目标很难达到:

目标:模块内可以很轻易的访问现有数据库的所有内容,但是却最好不对现有数据库进行任何更改,只有这样,当该模块移除之后,才不会留下任何无用的代码;

在我们的实践当中,基本上可以达到这个目标。下面我就详细介绍我们是如何进行数据访问设计的,基于EF CodeFirst。

原有系统使用着一个非常复杂且庞大的数据库,我们希望新增加的模块不做任何更改。原有数据库大概长这个样子的:

我们增加的模块化工程的文件结构如下图:

我们是增加了一个新的数据库DonatingDataContext,当然,这个数据库继承自原有系统的数据库。这样,新增加的模块就可以完全访问原有数据库了。请看模块数据库代码:

 

上方代码请注意 base.OnModelCreating(modelBuilder); 这句话非常重要,干什么的就不用解释了。

在增加新模块才用到的表的时候,难免需要跟原有系统的表建立外键关系。正常的做法,我们是通过EntityTypeConfiguration<T>来添加,如下图:

但是在模块化设计中,由于不能修改原有系统中表的定义,所以用EntityTypeConfiguration<T>是无法完成外键关系定义的。这时,只能用DataAnnotations的方式:

public Guid CreatorUserId { get; set; }

[ForeignKey("CreatorUserId")]
public virtual User CreatorUser { get; set; }

这样,在新数据库中,就可以随意的访问原有数据库了。如下所示:

1 public DonatePost GetPost(Guid postId)
2 {
3 using (var db = new DonatingDataContext())
4 {
5 return db.DonatePosts.Get(postId);
6 }
7 }

而在发布到产品服务器的时候,只需要在产品数据库上执行新模块的SQL脚本即可(可以通过EF生成新库,再用ManagementStudio生成脚本)。

3. 模块化之静态文件(JS、CSS、HTML)

从本文最开始的文件结构图中可以看到,我们把几乎所有静态文件都放到了一个叫“Plugins/Donating”的文件夹中了,这将非常有利于我们搬运这个模块到其他项目中。

请注意上面段落中的“几乎”二字,这说明还有小部分文件不在这里。这部分特殊的文件都是JS,有两部分,一部分在“_js-plugins/donating”,另一部分在“_grunt/gruntfile.js”中。用过grunt的都知道,这是JS未压缩为混淆的源文件了,你也可以参考我们的开源项目MvcSolution 了解更多这两部分JS的设计思想。简单的将,这两部分JS在发布网站的时候,是要被删除的,不需要拷贝到服务器的。

下面,请看静态文件结构图:

上图说明了很多东西,除了js文件夹是由grunt生成的话,其他的都一目了然,不再赘述。

对于CSHTML文件,在我们的例子中,是引用的原有项目的一个Layout,包括一些基本的CSS和JS,都有引用原有项目。这固然增加了耦合度,但我们的项目都是基于同一个架构,各个层级的代码都在标准之内,所以还是可以很容易的搬运模块的。

好了,本文关于ASP.NET MVC模块化的介绍就完了,不知道你是否看的云里雾里的。没有关系,当你决定开始尝试一下模块化设计的时候,相信本文中的一些实践还是能给你一些启发的。

最后

希望大家针对本文中的观点和实践畅所欲言,说说你的看法。

MVC模块化架构的更多相关文章

  1. 全面解析ASP.NET MVC模块化架构方案

    什么叫架构?揭开架构神秘的面纱,无非就是:分层+模块化.任意复杂的架构,你也会发现架构师也就做了这两件事. 本文将会全面的介绍我们团队在模块化设计方面取得的经验.之所以加了“全面”二字,是因为本文的内 ...

  2. Asp.net Mvc模块化开发之“部分版本部分模块更新(上线)”

    项目开发从来就不是一个简单的问题.更难的问题是维护其他人开发的项目,并且要修改bug.如果原系统有重大问题还需要重构. 怎么重构系统不是本文探讨的问题,但是重构后如何上线部署和本文关系密切.这个大家可 ...

  3. Asp.net Mvc模块化开发系列(目录)

    模块化开发是非常重要的,模块化开发是个系统性问题,为此我觉得有必须要写一个系列的文章才能基本说的清楚 那又为什么要写一个目录呢? 其一.是对我昨天承诺写一个系列新的文章的回应 其二.是先写出一个大纲, ...

  4. net Mvc模块化开发

    Asp.net Mvc模块化开发之“部分版本部分模块更新(上线)” 项目开发从来就不是一个简单的问题.更难的问题是维护其他人开发的项目,并且要修改bug.如果原系统有重大问题还需要重构. 怎么重构系统 ...

  5. Angular JS从入门基础 mvc三层架构 常用指令

    Angular JS从入门基础  mvc模型 常用指令 ★ 最近一直在复习AngularJS,它是一款优秀的前端JS框架,已经被用于Google的多款产品当中.AngularJS有着诸多特性,最为核心 ...

  6. ABP vnext模块化架构的最佳实践的实现

    在上一篇文章<手把手教你用Abp vnext构建API接口服务>中,我们用ABP vnext实现了WebAPI接口服务,但是并非ABP模块化架构的最佳实践.我本身也在学习ABP,我认为AB ...

  7. MVC实用架构设计(三)——EF-Code First(5):二级缓存

    前言 今天我们来谈谈EF的缓存问题. 缓存对于一个系统来说至关重要,但是是EF到版本6了仍然没有见到有支持查询结果缓存机制的迹象.EF4开始会把查询语句编译成存储过程缓存在Sql Server中,据说 ...

  8. MVC实用架构设计(三)——EF-Code First(4):数据查询

    前言 首先对大家表示抱歉,这个系列已经将近一个月没有更新了,相信大家等本篇更新都等得快失望了.实在没办法,由于本人水平有限,写篇博客基本上要大半天的时间,最近实在是抽不出这么长段的空闲时间来写.另外也 ...

  9. MVC实用架构设计(三)——EF-Code First(3):使用T4模板生成相似代码

    前言 经过前面EF的<第一篇>与<第二篇>,我们的数据层功能已经较为完善了,但有不少代码相似度较高,比如负责实体映射的 EntityConfiguration,负责仓储操作的I ...

随机推荐

  1. Android菜鸟的成长笔记(8)——Intent与Intent Filter(上)

    原文:[置顶] Android菜鸟的成长笔记(8)——Intent与Intent Filter(上) Intent代表了Android应用的启动“意图”,Android应用将会根据Intent来启动指 ...

  2. Android菜鸟的成长笔记(4)——你真的理解了吗?

    原文:Android菜鸟的成长笔记(4)--你真的理解了吗? 在上一篇中我们查看了QQ的apk源文件中的布局结构,并仿照QQ完成了我们第一个应用的界面,详细请看<Android菜鸟的成长笔记&g ...

  3. ubuntu10.10 tftp安装,配置,测试

    ubuntu10.10 tftp安装,配置,测试 成于坚持,败于止步 虽然ubuntu/centos/redhat都是linux,但是内核其中存在一定的修改,所以对于tftp服务器的安装存在不同的命令 ...

  4. 【Error】JavaWeb: 严重: Failed to initialize end point associated with ProtocolHandler [&quot;http-bio-8080&quot;]

    在MyEclipse中启动Tomcat时出现错误,错误信息例如以下: 严重: Failed to initialize end point associated with ProtocolHandle ...

  5. 纯JS实现的3D标签云,不依赖不论什么第三方库,支持移动页面

    <span style="font-family: Arial, Helvetica, sans-serif;"><!DOCTYPE html PUBLIC &q ...

  6. Handler和HandlerThread

    1.什么是Handler? SDK中关于Handler的说明例如以下: A Handler allows you to sendand process Messageand Runnable obje ...

  7. Swift - 自定义单元格实现微信聊天界面

    1,下面是一个放微信聊天界面的消息展示列表,实现的功能有: (1)消息可以是文本消息也可以是图片消息 (2)消息背景为气泡状图片,同时消息气泡可根据内容自适应大小 (3)每条消息旁边有头像,在左边表示 ...

  8. 11 款最好 CSS 框架 让你的网站独领风骚

    网页设计和发展领域已经成为竞争激烈的虚拟世界.想要在网络的虚拟世界中生存,仅有一堆静止的在线网络应用是远远不够的,网页必须要有很多功能,配以让人无法抗拒的设计.网页编码一定要合适.精确,才能保证不发生 ...

  9. HDU 1394 Minimum Inversion Number (线段树 单点更新 求逆序数)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1394 题意:给你一个n个数的序列,当中组成的数仅仅有0-n,我们能够进行这么一种操作:把第一个数移到最 ...

  10. 树莓派玩耍笔记4 -- 树莓派ssh党必备的配置

    1. 关闭桌面显示 对于ssh 党.当然不须要系统花费资源在显示上. 所以我们先在 "raspi-conifg" 下选择默认启动为Text 启动(这好像也是Raspbian 的默认 ...