应用程序框架实战三十八:项目示例VS解决方案的创建(一)

  进行项目开发的第一步,是创建出适合自己团队习惯的VS解决方案,虽然我已经提供了项目示例,但毕竟是我创建的,你直接使用可能并不合适,另外你如果尝试模仿重新创建该示例,中间可能碰到各种障碍,特别是项目间的依赖关系。

  本文的目的是帮助.Net架构初学者能顺利搭建起适合自己的VS解决方案,我会在本文演示曾经用过的几种不同风格的目录结构,你可以根据自己的习惯选择一种并自行修改。

  本系列假定你已经熟悉如何创建.NET类库等基础知识,并具有.Net开发经验,我不会详细到每一个细节。如果你是.Net初学者,尚未开发过任何实际项目,对你的建议就是关闭本页面,去买本书打好基础,需要提升时再来。

影响VS解决方案结构的因素

分层

  VS解决方案的创建,与你采用的分层方式密切相关,我在之前的文章已经简单介绍过单层架构和三层架构,本文采用DDD分层架构进行演示,说明项目示例Applications.Managements是怎样创建出来的,并且可以怎样简化。

  我们平时所说的分层,属于逻辑分层,它按照一定的逻辑层次分离关注点,以便更好的组织代码结构,方便维护。

  物理分层,是将不同的代码部署到不同的服务器,以提供更高性能和安全性,如果对逻辑分层和物理分层的关系感兴趣,请参考《C#企业应用开发艺术-CSLA.NET框架开发实战》一书。

  对于逻辑分层,只要将代码分离到不同区域即可,可以按照不同的抽象粒度来组织代码,比如类级别、目录级别、程序集级别。

  目前普遍采用的是程序集级别进行分层,即不同层的代码放到不同的程序集中。

  如果期望减少程序集,可以仅创建一个类库,采用目录级别的划分方式,在一个类库中创建不同层的文件夹,各目录放置各层的代码。

  类级别粒度将不同层的对象合并,通过方法的形式分离各层代码。

  通过程序集方式分层有诸多好处,比如清晰度、复用性等,我将采用程序集方式进行演示,建议你在项目上也尽量采用这种方式。

功能模块

  另一个影响VS解决方案创建的因素是功能模块的划分。

  对于一个业务系统,我们不仅要分层,而且还要划分功能模块,以便对较大的业务问题进行分解,从而降低复杂度,同时在解决某个特定模块的问题时,减少其它模块的干扰。

  对于领域层,我目前的方式是为每一个功能模块创建一个类库项目,这样做的好处是更方便复用,另外当我需要将这个功能模块分离出去也会更容易。

  当然,采用文件夹来分隔也同样可行,这是两种不同的风格,我会向你演示如何设计可以更方便的切换。

可复用能力

  对于一个稍大的项目,把特定业务模块、通用业务模块、通用技术类库放在同一个VS解决方案中,VS解决方案上的项目可能会达到上百个,编译速度将非常缓慢,导致很多不必要的麻烦。

  我之前已经多次提到,你可以把通用技术类和通用业务类从具体项目中分离出来,这样可以供多个项目使用,每个项目拥有自己的VS解决方案,共享相同的应用框架,这样维护会更容易,编译速度也更快。

  对于Applications.Managements项目示例,通用技术类库已经抽取到Util解决方案中,Applications.Managements不包含通用性的纯技术代码。

  我不会为你演示通用业务类的分离,因为代码示例太简单。

其它因素

  不同的应用程序类型也会影响VS解决方案的创建,比如创建WPF智能客户端应用程序,需要将应用层创建为WCF类库,这样可以将应用服务直接开放出来供WPF调用。对于WPF+WCF的架构,我后续也会进行介绍,如何通过IOC切换本地调用和远程调用,通过这个架构可以在开发时期采用本地方式调用,提高开发效率,部署时切换到WCF远程代理。

  如果你采用了诸如SOA架构,需要切割子系统部署成服务,可能需要创建更多类型的项目和多个VS解决方案,这些架构也对VS解决方案的创建具有相当影响。由于本人在SOA方面经验尚浅,待后续有更多经验再进行分享。

VS解决方案创建实战

  下面的步骤演示了我创建VS解决方案的习惯,你不一定要按我的步骤,只要最终达到你的要求即可。

创建表现层MVC项目

  打开VS解决方案,我使用的是VS 2013,自带了MVC4。

  创建MVC4项目,项目名称为Applications.Presentation.Managements,解决方案名称为Applications.Managements,MVC项目模板选择“基本”。

  先来讨论顶级空间的命名,即这里的Applications,要点是尽量抽象。顶级空间名称尽量不使用特定于项目或客户的名称,因为你做下一个项目时,需要大量更改命名空间。我采用Applications,代表抽象的应用程序,任何项目都是应用程序。你也可以采用公司的名称,比如你们公司简称为abc,那么可以这样Abc.Managements。

  Managements代表这是一个管理系统。Applications.Presentation.Managements说明它是管理系统的表现层,但为什么不用Applications.Managements.Presentation,后面介绍到领域层时我会说明这样做的原因。

  对于表现层来讲,没有其它层会调用它的API,所以我想保持表现层命名空间的简短,我耍了一个小技巧,将表现层的命名空间改成了Presentation。

  右键单击Applications.Presentation.Managements项目,选择“属性”,修改“默认命名空间”为Presetation,如下图所示。

  MVC创建完成后,会在根目录留下一个packages文件夹,MVC项目也会添加packages.config配置,这玩意很有用,Nuget会帮你管理依赖的程序集及版本,当别人运行你的解决方案时缺少程序集,会自动下载还原。但我更喜欢自己手工管理,在Applications.Managements根目录创建Libraries文件夹,我会把packages中必须的几个dll复制进去,其它全部删掉。

  当然,修改表现层默认命名空间和删除packages属于我的个人习惯,介绍这个是方便你看我的代码,对于你自己的项目,完全没必要这么麻烦。

  编译通过,继续下面的步骤,后面再返回到表现层。

创建表现层扩展项目

  在Applications.Managements解决方案上右键,“添加”->“新建项目”,创建一个类库项目,名称为Applications.Presentation.Managements.Extensions。

  这个项目用来为表现层提供一些支持,比如业务组件的封装。

  将它的默认命名空间改成Presentation,方便表现层的调用。

  下面来考虑类库输出的DLL目标位置,默认是在bin\Debug\,表示DLL将输出到类库项目的bin目录下Debug文件夹中。由于我们后续会创建更多的程序集,如果对输出位置不进行统一,将导致每个输出目录都产生大量重复的DLL。

  右键单击扩展类库,选择属性,找到“生成”选项卡的“输出路径“,修改为..\Release\,如下图所示。

  下面为Applications.Presentation.Managements项目添加Applications.Presentation.Managements.Extensions扩展项目的引用。

编译通过,继续下面的步骤。

创建应用层项目

  在Applications.Managements解决方案上创建类库项目,名称为Applications.Services,修改类库输出路径为..\Release\。

  应用层是为表现层提供服务的,所以表现层Applications.Presentation.Managements需要添加应用层Applications.Services的引用。

  编译通过,继续下面的步骤。

创建基础设施层项目

  DDD分层架构中没有了数据访问层的概念,被包含到基础设施层中。

  基础设施层是为其它各层服务的,按道理说,其它各层应该包含对基础设施层的引用。

  但是,为了让领域层变得更纯净,一般会应用依赖倒置原则,让基础设施层反过来依赖领域层。

  一些初学者会产生疑惑,这样不是造成循环引用的死循环了吗?下面谈下我的看法,不一定正确,仅供参考。

  在《领域驱动设计》一书中,对基础设施层的描述为:基础设施层为各层提供通用的技术能力

  可见基础设施层是一个抽象概念,诸如层超类型、公共操作类的封装、数据库的持久化,发邮件,发短信等操作,都是基础设施层的范畴。

  在我的代码中,大量通用技术,都被封装到Util相关的程序集中,如果需要合并VS解决方案,那么所有的Util项目都是基础设施层的一个组成部分。

  对于领域层与基础设施层的引用关系,与基础设施层中项目耦合性强弱有关,一般规律是:

  1. 领域层只引用基础设施层中低耦合的项目,比如领域层可以引用Util.Core、Util.Domains,这些项目只包含对.NET本身类库的引用,没有外部依赖,也不需要任何配置,领域层依赖这些项目不会降低可复用性和可测试性等指标。
  2. 与领域层密切相关的高耦合基础设施层项目,应用依赖倒置原则,先在领域层定义操作接口,由基础设施层项目引用相关领域层项目,并实现该接口。比如仓储代表聚合的集合,仓储的实现依赖于特定数据访问技术,甚至包含SQL语句,所以具有很高的耦合,且与领域层特定模块密切相关。如果领域层直接引用数据访问实现的程序集,将严重降低领域层的可复用性,且更难测试。
  3. 与领域层关系不大的高耦合基础设施层项目,应用分离接口模式,将操作接口与具体实现分离到基础设施层的不同项目中。比如工作单元的接口IApplicationUnitOfWork,它与领域层基本没啥关系,如果你的领域层只包含一个类库项目,把IApplicationUnitOfWork放到领域层项目中也没什么问题,我之前一直是这样干的,但如果你的领域层包含多个项目,你应该放到哪一个项目中?单独创建一个基础设施层项目来放置这些接口会好一些。

(未完待续…

代码更新

  1. 更新了js中的一些Bug
  2. 由于项目上需要使用MYSQL,最近几天摸索了EF操作MYSQL,遇到的一些障碍已解决,特将解决方案放出,供使用MYSQL的同学参考。

    a) MYSQL的乐观离线锁与SQL SERVER显著不同,主要参考王刚的这篇:EF6 Code First 系列 (三):在MySql中使用和SqlServer一致的RowVersion并发控制。不过对于Sql Server,我还是使用IsRowVersion,毕竟这是大部分项目的主打。

    b) 通过一个简单的Ioc配置,即可将Sql Server切换到MySql。

  c) MySql不支持Sql Server的schema,我使用.分隔MySql表名模拟schema,以保持与Sql Server的一致性风格,这需要一点EF映射技巧。

  3. codesmith代码生成模板已更新,调整了命名空间,并增加了对mysql特定配置的生成。

  4. 数据库备份中提供了一个MYSQL建库脚本。

QQ群

  应用程序框架交流QQ群1:386092459(已满)

  应用程序框架交流QQ群2:376124781

  EasyUi交流QQ群:157809322

源码下载:(下载时顺手推荐)

框架源码

项目示例源码

数据库备份

Codesmith生成器模板

备注

  本来准备只用一篇来完成本文,不过太忙了,只有分几次来写,希望各位见谅,下次更新的时间会比较长。

时间: 2024-10-05 20:27:44

应用程序框架实战三十八:项目示例VS解决方案的创建(一)的相关文章

应用程序框架实战三十六:CRUD实战演练介绍

从本篇开始,本系列将进入实战演练阶段. 前面主要介绍了一些应用程序框架的概念和基类,本来想把所有概念介绍完,再把框架内部实现都讲完了,再进入实战,这样可以让初学者基础牢靠.不过我的精力很有限,文章进度越来越慢,所以准备切换一下介绍顺序,把实战演练提前,以方便你阅读代码. 实战演练介绍 本系列实战演练共分两个部分. 实战演练第一部分介绍如何快速解决CRUD机械操作,这一部分我将手把手带领各位同学从搭建VS环境开始,创建程序集及各程序集间的依赖关系,以及引入依赖的外部DLL,并手工完成代码示例中Ap

应用程序框架实战三十:表现层及ASP.NET MVC介绍(一)

本文将介绍表现层及ASP.NET MVC的一些要点,特别是ASP.NET MVC的一些抽象和封装技巧,如果你对MVC还不了解,可以参考<ASP.NET MVC4 高级编程>,作者Jon Galloway等,这本书由ASP.NET MVC团队成员编写,相当不错. 表现层的职责 表现层的职责是展示和收集数据,将领域层的数据和逻辑展示出来,并收集用户输入的相关信息. 搞清楚表现层的职责以后,你就应该清楚,表现层不是你应该编写业务逻辑的地方,这也是分层架构的核心. 如果要展示一个计算值,不应该在表现层

应用程序框架实战三十五:服务概述

上一篇介绍了我对几种实体的认识,本篇将介绍几种服务的用法. 预告一下本系列后续计划,本篇之后,准备进入实战演练阶段,先介绍如何快速解决CRUD操作,从如何使用PD数据建模到使用CodeSmith生成代码,先带你感受一下,再回过来介绍框架内部元素,以免你在阅读时昏昏欲睡. 应用服务介绍 对于一个新的设计元素,可以先假定不需要它,等到确实认识到它的作用再引入.那么,应用服务为我们带来了哪些好处呢? 应用服务帮助简化表现层操作 以MVC为例,如果没有应用服务,那么控制器将直接调用仓储,设置查询条件,转

应用程序框架实战三十四:数据传输对象(DTO)介绍及各类型实体比较(转)

本文将介绍DDD分层架构中广泛使用的数据传输对象Dto,并且与领域实体Entity,查询实体QueryObject,视图实体ViewModel等几种实体进行比较. 领域实体为何不能一统江湖? 当你阅读我或其它博主提供的示例代码时,会发现几种类型的实体,这几种实体初步看上去区别不大,只是名称不同,特别在这些示例非常简单的情况下更是如此.你可能会疑惑为何要搞得这么复杂,采用一种实体不是更好? 在最理想的情况下,我们只想采用领域实体Entity进行所有的操作. 领域实体是领域层的核心,是业务逻辑的主要

微信小程序把玩(三十八)获取设备信息 API

原文:微信小程序把玩(三十八)获取设备信息 API 获取设备信息这里分为四种, 主要属性: 网络信息wx.getNetWorkType, 系统信息wx.getSystemInfo, 重力感应数据wx.onAccelerometerChange, 罗盘数据wx.onCompassChange wxml <button type="primary" bindtap="getNetWorkType">获取网络类型</button> <butt

应用程序框架实战二十六:查询对象

信息系统的查询需求千变万化,在仓储中为每个查询需求创建一个特殊方法,将导致大量乏味而臃肿的接口. 一种更加可行的办法是,在应用层服务中描述查询需求,并通过仓储执行查询. 为了能够更好的描述查询需求,可以将查询功能从仓储中抽取出来,专门创建一个查询对象. 查询最复杂的部分是条件过滤,这也是查询对象的主要职责.查询对象可以认为是规约模式的一个变种,允许查询对象动态创建查询条件. 在Util.Domains项目Repositories目录中,创建查询对象基接口IQueryBase,代码如下. usin

应用程序框架实战二十五:查询条件(规约模式应用)

前面已经做了一些准备工作,本篇将介绍查询条件的封装,它是规约模式的一个应用. 规约使用一个对象来封装谓词,我之前已经介绍过它在验证方面的应用,本篇是规约模式在查询方面的应用. 规约的强大之处在于,能够将一堆杂乱无章的条件判断或查询条件封装起来,以一个清晰的概念来表达,并使得这些谓词具备了可复用的能力. 首先在Util.Domains项目的Repositories目录中创建ICriteria接口,这个接口表示一个查询条件,代码如下. using System; using System.Linq.

应用程序框架实战二十四:基础查询扩展 - 分页与排序

上一篇介绍了IQueryable的Where方法存在的问题,并扩展了一个名为Filter的过滤方法,它是Where方法的增强版.本篇将介绍查询的另一个重要主题——分页与排序. 对于任何一个信息系统,查询都需要分页,因为不可能直接返回表中的所有数据. 如果直接使用原始的Ado.Net,我们可以编写一个通用分页存储过程来进行分页查询,然后通过一个DataTable返回给业务层.不过进入Entity Framework时代,分页变得异常简单,通过Skip和Take两个方法配合就可以完成任务. 为了让分

应用程序框架实战二十:映射层超类型

上一篇介绍了工作单元层超类型的封装演化过程,本文将介绍对Entity Framework映射层超类型的封装. 使用Entity Framework一般需要映射三种类型的对象,即实体.聚合.值对象. 聚合与实体映射的主要区别是:聚合映射单属性标识Id,并需要映射乐观离线锁Version,而实体的标识往往需要映射成复合属性,这样方便物理删除聚合中的实体.Entity Framework通过EntityTypeConfiguration进行实体映射. 值对象以嵌入值模式映射,这需要使用ComplexT