ABP框架的体系结构及模块系统讲解
DDD分层
为了减少复杂性和提⾼代码的可重⽤性,采⽤分层架构是⼀种被⼴泛接受的技术。
为了实现分层的体系结构,ABP遵循DDD(领域驱动设计)的原则,将分为四个层次:
展现层(Presentation):提供⼀个⽤户界⾯,实现⽤户交互操作。
应⽤层(Application):进⾏展现层与领域层之间的协调,协调业务对象来执⾏特定的应⽤程序的任务。它不包含业务逻辑。
领域层(Domain):包括业务对象和业务规则,这是应⽤程序的核⼼层。
基础设施层(Infrastructure):提供通⽤技术来⽀持更⾼的层。例如基础设施层的仓储(Repository)可通过ORM来实现数据库交互。
根据实际需要,可能会有额外添加的层。例如:
分布式服务层(Distributed Service):⽤于公开应⽤程序接⼝供远程客户端调⽤。⽐如通过ASP.NET
Web API和WCF来实现。
这些都是常见的以领域为中⼼的分层体系结构。不同的项⽬在实现上可能会有细微的差别。
ABP的体系结构
⼀个简单的解决⽅案,⼤致包含5个项⽬:
每⼀层可以⽤⼀个或多个程序集来实现。
1.领域层(Domain)
领域层就是业务层,是⼀个项⽬的核⼼,所有业务规则都应该在领域层实现。
2.实体(Entity)
实体代表业务领域的数据和操作,在实践中,通过⽤来映射成数据库表。
3.仓储(Repository)
仓储⽤来操作数据库进⾏数据存取。仓储接⼝在领域层定义,⽽仓储的实现类应该写在基础设施层。
4.领域服务(Domain service)
当处理的业务规则跨越两个(及以上)实体时,应该写在领域服务⽅法⾥⾯。
5.领域事件(Domain Event)
在领域层某些特定情况发⽣时可以触发领域事件,并且在相应地⽅捕获并处理它们。
6.⼯作单元(Unit of Work)
⼯作单元是⼀种设计模式,⽤于维护⼀个由已经被修改(如增加、删除和更新等)的业务对象组成的列表。它负责协调这些业务对象的持久化⼯作及并发问题。
应⽤层(Application)
应⽤层提供⼀些应⽤服务(Application Services)⽅法供展现层调⽤。⼀个应⽤服务⽅法接收⼀个DTO(数据传输对象)作为输⼊参数,使⽤这个输⼊参数执⾏特定的领域层操作,并根据需要可返回另
⼀个DTO。在展现层到领域层之间,不应该接收或返回实体(Entity)对象,应该进⾏DTO映射。⼀个应⽤服务⽅法通常被认为是⼀个⼯作单元(Unit of Work)。⽤户输⼊参数的验证⼯作也应该在应⽤层实现。ABP提供了⼀个基础架构让我们很容易地实现输⼊参数有效性验证。建议使⽤⼀种像AutoMapper这样的⼯具来进⾏实体与DTO之间的映射。
基础设施层(Infrastructure)
当在领域层中为定义了仓储接⼝,应该在基础设施层中实现这些接⼝。可以使⽤ORM⼯具,例如EntityFramework或NHibernate。ABP的基类已经提供了对这两种ORM⼯具的⽀持。数据库迁移也被⽤于这⼀层。
WEB与展现层(Web & Presentation)
Web层使⽤ASP.NET MVC和Web API来实现。可分别⽤于多页⾯应⽤程序(MPA)和单页⾯应⽤程序(SPA)。
在SPA中,所有资源被⼀次加载到客户端浏览器中(或者先只加载核⼼资源,其他资源懒加载),然后通过AJAX调⽤服务端WebApi接⼝获取数据,再根据数据⽣成HTML代码。不会整个页⾯刷新。现在已经有很多SPA的JS框架,例如:AngularJs、 DurandalJs、BackboneJs、EmberJs。 ABP可以使⽤任何类似的前端框架,但是ABP提供了⼀些帮助类,让我们更⽅便地使⽤AngularJs和DurandalJs。
在经典的多页⾯应⽤(MPA)中,客户端向服务器端发出请求,服务器端代码(ASP.NET MVC控制器)从数据库获得数据,并且使⽤Razor视图⽣成HTML。这些被⽣成后的HTML页⾯被发送回客户端显⽰。每显⽰⼀个新的页⾯都会整页刷新。
SPA和MPA涉及到完全不同的体系结构,也有不同的应⽤场景。⼀个管理后台适合⽤SPA,博客就更适合⽤MPA,因为它更利于被搜索引擎抓取。
SignalR是⼀种从服务器到客户端发送推送通知的完美⼯具。它能给⽤户提供丰富的实时的体验。
已经有很多客户端的Javascript框架或库,JQuery是其中最流⾏的,并且它有成千上万免费的插件。使⽤Bootstrap可以让我们更轻松地完成写Html和CSS的⼯作。
ABP也实现了根据Web API接⼝⾃动创建 Javascript的代码函数,来简化JS对Web Api的调⽤。还有把服务器端的菜单、语⾔、设置等⽣成到JS端。(但是在我⾃⼰的项⽬中,我是把这些⾃动⽣成功能关闭的,因为必要性不是很⼤,⽽这些⼜会⽐较影响性能)。
ABP会⾃动处理服务器端返回的异常,并以友好的界⾯提⽰⽤户。
ABP模块系统
ABP框架提供了创建和组装模块的基础,⼀个模块能够依赖于另⼀个模块。在通常情况下,⼀个程序集就可以看成是⼀个模块。在ABP框架中,⼀个模块通过⼀个类来定义,⽽这个类要继承⾃AbpModule。
译者注:如果学习过Orchard的朋友,应该知道module模块的强⼤了。模块的本质就是可重⽤性,你可以在任意的地⽅去调⽤,⽽且通过实现模块,你写的模块也可以给别⼈⽤。
Assembly程序集:Assembly是⼀个包含来程序的名称,版本号,⾃我描述,⽂件关联关系和⽂件位置等信息的⼀个集合。最简单的理解就是:⼀个你⾃⼰写的类库⽣成的dll就可以看做是⼀个程序集,这个程序集可以包括很多类,类⼜包括很多⽅法等。
可以通过反射获取⼀个程序集中的类以及⽅法。
下⾯的例⼦,我们开发⼀个可以在多个不同应⽤中被调⽤MybolgApplication模块,代码如下:
public class MyBlogApplicationModule : AbpModule //定义
{
public override void Initialize() //初始化
{
IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());
//这⾏代码的写法基本上是不变的。它的作⽤是把当前程序集的特定类或接⼝注册到依赖注⼊容器中。
}
}
ABP框架会扫描所有的程序集,并且发现AbpModule类中所有已经导⼊的所有类,如果你已经创建了包含多个程序集的应⽤,对于ABP,我们的建议是为每⼀个程序集创建⼀个Module(模块)。
⽣命期事件
在⼀个应⽤中,abp框架调⽤了Module模块的⼀些指定的⽅法来进⾏启动和关闭模块的操作。我们可以重载这些⽅法来完成我们⾃⼰的任务。
ABP框架通过依赖关系的顺序来调⽤这些⽅法,假如:模块A依赖于模块B,那么模块B要在模块A之前初始化,模块启动的⽅法顺序如下:
PreInitialize-B
PreInitialize-A
Initialize-B
Initialize-A
PostInitialize-B
PostInitialize-A
下⾯是具体⽅法的说明:
1.PreInitialize
预初始化:当应⽤启动后,第⼀次会调⽤这个⽅法。在依赖注⼊注册之前,你可以在这个⽅法中指定⾃⼰的特别代码。举个例⼦吧:假如你创建了⼀个传统的登记类,那么你要先注册这个类(使⽤IocManager对登记类进⾏注册),你可以注册事件到IOC容器。等。
2.Initialize
初始化:在这个⽅法中⼀般是来进⾏依赖注⼊的注册,⼀般我们通过IocManager.RegisterAssemblyByConvention这个⽅法来实现。如果你想实现⾃定义的依赖注⼊,那么请参考依赖注⼊的相关⽂档。
3.PostInitialize
提交初始化:最后⼀个⽅法,这个⽅法⽤来解析依赖关系。
4.Shutdown
关闭:当应⽤关闭以后,这个⽅法被调⽤。
模块依赖(Module dependencies)
Abp框架会⾃动解析模块之间的依赖关系,但是我们还是建议你通过重载GetDependencies⽅法来明确的声明依赖关系。[DependsOn(typeof(MyBlogCoreModule))]//通过注解来定义依赖关系
public class MyBlogApplicationModule : AbpModule
{
public override void Initialize()
{
IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());
}
}
例如上⾯的代码,我们就声明了MyBlogApplicationModule和MyBlogCoreModule的依赖关系(通过属性
attribute),MyBlogApplicationModule这个应⽤模块依赖于MyBlogCoreModule核⼼模块,并且,MyBlogCoreModule核⼼模块会在MyBlogApplicationModule模块之前进⾏初始化。
如何⾃定义的模块⽅法
我们⾃⼰定义的模块中可能有⽅法被其他依赖于当前模块的模块调⽤,下⾯的例⼦,假设模块2依赖于模块1,并且想在预初始化的时候调⽤模块1的⽅法。
public class MyModule1 : AbpModule
{
public override void Initialize() //初始化模块
{
IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());//这⾥,进⾏依赖注⼊的注册。
}
public void MyModuleMethod1()
{
//这⾥写⾃定义的⽅法。
}
}
[DependsOn(typeof(MyModule1))]
public class MyModule2 : AbpModule
{
private readonly MyModule1 _myModule1;
public MyModule2(MyModule1 myModule1)
{
_myModule1 = myModule1;
}
public override void PreInitialize()
{
_myModule1.MyModuleMethod1(); //调⽤MyModuleMethod1的⽅法。
}
public override void Initialize()
{
IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());
}
}
jquery框架定义就这样,就把模块1注⼊到了模块2,因此,模块2就能调⽤模块1的⽅法了。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论