结构划分迷雾
Asp.Net Boilerplate中默认项目结构是如下所示(项目代号Gravel),每层都是一个单独的类库或是应用程序。
- Gravel.sln
- Gravel.Web
- Gravel.Application
- Gravel.Core
- Gravel.EFCore
在这种固有分层方式下,ABP没有指明要采用什么形式去划分限界上下文,是采用文件夹隔离形式还是采用类库隔离形式,并没有严格说明。
注意:具体是在应用层领域层基础设施层中需要隔离限界上下文。
文件夹隔离
如采用文件夹隔离形式,则解决方案结构如下,在每一层中建立文件夹形式以此来划分限界上下文的边界。
- Gravel.sln
- Gravel.Web
- Gravel.Application
- OrderCenter
- ProductCenter
- CustomerCenter
- Gravel.Core
- OrderCenter
- ProductCenter
- CustomerCenter
- Gravel.EFCore
- OrderCenter
- ProductCenter
- CustomerCenter
类库隔离
如采用类库隔离形式(即依照限界上下文拆分类库),则解决方案结构如下,Gravel作为承载体,其余三个作为上下文独立出来,按照上下文的边界高于分层的边界(这种划分也更加合理)。
- Gravel.sln
- Gravel
- Gravel.Web
- Gravel.Application
- Gravel.Core
- Gravel.EFCore
- OrderCenter
- Application
- Core
- EFCore
- ProductCenter
- Application
- Core
- EFCore
- CustomerCenter
- Application
- Core
- EFCore
对于Asp.Net Boilerplate,见到的几个Demo及博客园内一些作者,都是采用的第一种风格形式,即分层边界高于上下文的边界。
而对于第二种形式,在ABP VNext的MicroserviceDemo中采用的是这种形式。并且在ABP VNext中,这种形式的变体是可以从单体切换到微服务的。
注意:这里的解决方案结构并不是固定如此,因ABP中模块化设计,有些是可以删除的。
回归本质
这样拆分的目的是什么?为了划分限界上下文,为了达到清晰边界,从而控制住上下文内部。
架构没有清晰的层次,职责缺乏合理的分配,代码变得不可阅读和维护,最终形成一种无序设计。
从分层形式角度出发,分层形式有物理分层和逻辑分层,DDD中提到的分层是属于逻辑分层,逻辑上划分了应用层领域层基础设施层,并没有规定物理分层的形式,即没有规定是整个项目划分成一个类库还是多个类库,也没有规定整个项目是划分成多个上下文,每个上下文是一个类库还是多个类库。
如果不考虑内部类库的数量,本身其实关注的就是如何组织多个限界上下文,是在一个项目中组织还是一个项目中拆分成多个限界上下文组织。
代码模型组织策略
按层次划分
采用文件夹形式划分本质上是一种按层次划分,同一层中,依照限界上下文,划分不同的边界,这是一种近乎常规化的划分方式。
按特性划分
依照垂直划分方式,将限界上下文垂直物理拆分,每个限界上下文内都是一个独立的团体。该团体内部仍然可以是分层架构的。
按组件划分
在特性划分基础上再升华,除去UI部分,组成组件,依照限界上下文形式组合,这也更贴近考虑DDD时不要纳入UI层。如此划分后,其他的Controller可以通过组件形式访问某个限界上下文中的数据。
结构划分迷雾消散
给定的AspNet Boilerplate或是AspNet Zero中都是采用的按层次划分的形式,但并没有说按照组件划分不可行,毕竟在一个gitee仓库中看到过按照组件划分的形式,而且是可用的。而对于ABP VNext更推荐按组件划分的形式,主要参考是MicroserviceDemo中的格式。
所以最终迷雾也就解除了,是ABP没有规定必须要按照哪种格式去组织代码,因为对于AspNet Boilerplate来讲,模块化的设计,本身可以允许按照层次划分与按照组件划分,至于选用哪种方式,更多的是,使用者自身去选择,也从另一方面看出ApNet Boilerplate本身作为基础框架,提供了使用者更多的选择性。
AspNet Boilperlate按层次划分形式组织代码
AspNet Boilerplate按组件划分形式组织代码
总结
对于实际使用中来讲,如果按照层次划分,使用ABP时总有点说不出来的感觉,不知道如何去管理代码,写的爽时,直接把其他上下文中的应用服务,领域服务或是仓储直接搬到当前的限界上下文中用,这也就导致后期代码看起来很凌乱,耦合也严重。
想要通过按层次划分引入防腐层来避免直接依赖,却要遵循约束,必须强行遵守纪律,通过防腐层去访问其他限界上下文。
从这一角度考虑,也逐渐理解了为什么要限界上下文的边界要高于分层的边界,按照组件划分的形式,直接断绝了限界上下文间的直接调用,而是采用防腐层或是其他形式去做中间隔离,类似代理形式解决直接耦合问题,同时也方便往微服务方向拆分时不会拖泥带水。
2020-11-22,望技术有成后能回来看见自己的脚步