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

本文将会全面的介绍我们团队在模块化设计方面取得的经验。之所以加了“全面”二字,是因为本文的内容将会涉及到:数据库、路由、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,当然,这个数据库继承自原有系统的数据库。这样,新增加的模块就可以完全访问原有数据库了。请看模块数据库代码:

 namespace Lin.Plugins.Donating.Data
{
public class DonatingDataContext : LinDataContext
{
public DbSet<DonateComment> DonateComments { get; set; }
public DbSet<DonatePost> DonatePosts { get; set; }
public DbSet<DonateWant> DonateWants { get; set; } public DonatingDataContext()
{
Database.SetInitializer<DonatingDataContext>(null);
} protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
var mappings = typeof(DonatingDataContext).Assembly.GetInheritedTypes(typeof(EntityTypeConfiguration<>));
foreach (var mapping in mappings)
{
dynamic instance = Activator.CreateInstance(mapping);
modelBuilder.Configurations.Add(instance);
}
}
}
}

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

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

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

public Guid CreatorUserId { get; set; }

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

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

 public DonatePost GetPost(Guid postId)
{
using (var db = new DonatingDataContext())
{
return db.DonatePosts.Get(postId);
}
}

而在发布到产品服务器的时候,只需要在产品数据库上执行新模块的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模块化的介绍就完了,不知道你是否看的云里雾里的。没有关系,当你决定开始尝试一下模块化设计的时候,相信本文中的一些实践还是能给你一些启发的。

最后

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

全面解析ASP.NET MVC模块化架构方案的更多相关文章

  1. MVC模块化架构

    全面解析ASP.NET MVC模块化架构方案 什么叫架构?揭开架构神秘的面纱,无非就是:分层+模块化.任意复杂的架构,你也会发现架构师也就做了这两件事. 本文将会全面的介绍我们团队在模块化设计方面取得 ...

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

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

  3. Asp.net mvc项目架构分享系列之架构概览

    Asp.net mvc项目架构分享系列之架构概览 Contents 系列一[架构概览] 0.项目简介 1.项目解决方案分层方案 2.所用到的技术 3.项目引用关系 系列二[架构搭建初步] 4.项目架构 ...

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

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

  5. ASP.NETCORE MVC模块化

    ASP.NETCORE MVC模块化编程 前言 记得上一篇博客中跟大家分享的是基于ASP.NETMVC5,实际也就是基于NETFRAMEWORK平台实现的这么一个轻量级插件式框架.那么今天我主要分享的 ...

  6. ASP.NET MVC 多语言方案

    前言: 好多年没写文章了,工作很忙,天天加班, 每天都相信不用多久,就会升职加薪,当上总经理,出任CEO,迎娶白富美,走上人生巅峰,想想还有点小激动~~~~ 直到后来发生了邮箱事件,我竟然忘了给邮箱密 ...

  7. 解析ASP.NET Mvc开发之EF延迟加载

    目录: 1)从明源动力到创新工场这一路走来 2)解析ASP.NET WebForm和Mvc开发的区别 3)解析ASP.NET Mvc开发之查询数据实例 ------------------------ ...

  8. Asp.net Mvc模块化开发之分区扩展框架

    对于一个企业级项目开发,模块化是非常重要的. 默认Mvc框架的AreaRegistration对模块化开发真的支持很好吗?真的有很多复杂系统在使用默认的分区开发的吗?我相信大部分asp.net的技术团 ...

  9. Asp.net Mvc模块化开发之“开启模块开发、调试的简单愉快之旅”

    整个世界林林种种,把所有的事情都划分为对立的两个面. 每个人都渴望的财富划分为富有和贫穷,身高被划分为高和矮,身材被划分为胖和瘦,等等. 我们总是感叹,有钱人的生活我不懂;有钱人又何尝能懂我们每天起早 ...

随机推荐

  1. In-Memory:在内存中创建临时表和表变量

    在Disk-Base数据库中,由于临时表和表变量的数据存储在tempdb中,如果系统频繁地创建和更新临时表和表变量,大量的IO操作集中在tempdb中,tempdb很可能成为系统性能的瓶颈.在SQL ...

  2. Javascript 的执行环境(execution context)和作用域(scope)及垃圾回收

    执行环境有全局执行环境和函数执行环境之分,每次进入一个新执行环境,都会创建一个搜索变量和函数的作用域链.函数的局部环境不仅有权访问函数作用于中的变量,而且可以访问其外部环境,直到全局环境.全局执行环境 ...

  3. transtion:过渡动画

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 17.0px Monaco; color: #4f5d66 } p.p2 { margin: 0.0px 0 ...

  4. div实现自适应高度的textarea,实现angular双向绑定

    相信不少同学模拟过腾讯的QQ做一个聊天应用,至少我是其中一个. 过程中我遇到的一个问题就是QQ输入框,自适应高度,最高高度为3row. 如果你也像我一样打算使用textarea,那么很抱歉,你一开始就 ...

  5. SQL Server-聚焦在视图和UDF中使用SCHEMABINDING(二十六)

    前言 上一节我们讨论了视图中的一些限制以及建议等,这节我们讲讲关于在UDF和视图中使用SCHEMABINDING的问题,简短的内容,深入的理解,Always to review the basics. ...

  6. Lambda

    Lambda Lambda 表达式是一种可用于创建委托或表达式目录树类型的匿名函数. 通过使用 lambda 表达式,可作为参数传递或作为函数调用值返回的本地函数. Lambda 表达式对于编写 LI ...

  7. RSA非对称加密,使用OpenSSL生成证书,iOS加密,java解密

    最近换了一份工作,工作了大概一个多月了吧.差不多得有两个月没有更新博客了吧.在新公司自己写了一个iOS的比较通用的可以架构一个中型应用的不算是框架的一个结构,并已经投入使用.哈哈 说说文章标题的相关的 ...

  8. 【初码干货】使用阿里云对Web开发中的资源文件进行CDN加速的深入研究和实践

    提示:阅读本文需提前了解的相关知识 1.阿里云(https://www.aliyun.com) 2.阿里云CDN(https://www.aliyun.com/product/cdn) 3.阿里云OS ...

  9. 从零自学Hadoop(22):HBase协处理器

    阅读目录 序 介绍 Observer操作 示例下载 系列索引 本文版权归mephisto和博客园共有,欢迎转载,但须保留此段声明,并给出原文链接,谢谢合作. 文章是哥(mephisto)写的,Sour ...

  10. 了解 ARDUINO 101* 平台

    原文链接 简介 作为一名物联网 (IoT) 开发人员,您需要根据项目的不同需求,选择最适合的平台来构建应用. 了解不同平台的功能至关重要. 本文第一部分比较了 Arduino 101 平台和 Ardu ...