前言

很多场景【单体+模块化】比微服务更合适,开发难度低、代码可复用性强、可扩展性强。模块化开发有些难点,模块启动与卸载、模块之间的依赖和通讯。asp.net core abp为我们提供了模块化开发能力及其它基础功能。基于abp(一代6.3)结合DDD已基本开发好一个【工单管理模块】,本篇做个基本介绍并说明如何集成此模块,后续会详细说明思路。

资源

线上demo:http://web1.cqyuzuji.com:9000/ 账号:admin 密码:123qwe

后端源码:https://gitee.com/bxjg1987/abp

前端源码:https://gitee.com/bxjg1987/front

必备知识

熟悉asp.net core和abp(注意是老版本,非vNext,但也很容易迁移到vNext上)

术语

下文会提供到一些概念,理解这个黑重要。

abp模块:这个不解释了,是abp基础,请参考官方文档

通用模块:这个是使用abp模块开发方式做的一些通用的,与具体业务无关的模块,比如:数据字典模块

业务模块:工单管理、广告管理、电商模块等为了实现具体业务的模块。

业务场景

客户是做复印机出租的,它希望做一套系统管理整个业务,其中工单是一个比较重要的模块,大致流程如下:

  1. 客户通过小程序上报工单,说明什么设备出了什么问题
  2. 系统后台管理员查看下大致问题后审核
  3. 后台管理员将已审核的工单分配给指定维修人员,或维修人员通过app自己领取已审核的工单
  4. 当维修人员到达客户处,通过app将工单设置为已执行状态
  5. 当维修人员处理完任务后通过app将工单设置为已完成状态,同时可能需要录入完成情况说明

以上是主体流程,还有些边角的以后文章会详细说明,比如:从审核状态跳跃到已完成状态;从已完成状态回退到待审核状态;状态变化时的事件等。

工单类型不同:有些工单可能并不是客户提交的,比如当采购的二手设备入库时要做检修,也会产生工单,这种情况工单不会与客户关联,而时与入库单关联;再比如让某员工开车去托快递回来这种情况工单会与物流信息关联

工单创建方式不同:客户通过小程序提交、后台管理员手动建立、当发生某些事件时自动创建(比如采购入库时自动创建)

其实工单管理模块是个通用的业务,在很多系统可能都需要,因此考虑做成独立的业务模块,方便复用。

目标

可复用

工单模块以nuget包发布,你可以安装后简单配置后就可以使用。

易升级

上面说了,以nuget包形式发布的,将来模块更新后发布新版本的nuget包,各系统更新下,引用新版本包就ok啦

独立性

工单模块只依赖些通用的、非业务型的模块。工单模块需要用到“员工”概念,在系统中往往体现为一个用户,工单模块本身不提供“员工管理”的功能,因为你的系统可能有自己的“员工管理”功能;或你直接拿abp原始的 AbpUser作为员工也行。试想如果“工单模块”本身提供了员工管理模块,你引用过去,发现自己系统中已有实现了的员工管理,是不是很麻烦?

所以你的项目引用工单模块时需要做个适配,为工单模块提供需要用到的员工相关功能,主要是几个查询。

说明:

abp vNext使用契约层来实现模块独立化,个人认为不完整,比如你的项目中有个”员工管理“模块,你在定义契约接口和DTO时只能定义通用的,为了尽量通用,接口中的方法会尽量多,或分开多定义几个接口,DTO中的属性也会尽量多,因为你不知道将来哪各模块引用你,所以你无法定义准确的、刚好够用的接口和DTO。

现在有各”工资管理“模块,引用你的”员工管理模块“,它会拿到DTO中很多不必要的属性,也会在引入接口时拿到很多不需要的方法。

再比如我的”工单模块“如果直接引用你的契约,将来我发布工单模块,其它系统引用后,它必须去实现”员工契约“中的接口,它会很迷茫,我要实现这个契约中所有的接口吗?DTO所有的属性我都需要赋值吗?其实某些契约中的接口方法工单模块可能根本不需要,同理契约中的DTO也不一定都需要赋值。

还有更多问题,这些问题不影响使用,但挺别扭。出现这样的原因,是独立的业务模块应该在契约中定义自己能向外提供什么数据之外,还应该定义自己需要什么,而不是让别的模块的契约来指定。

我们在开发工单模块时,会从这两个方向来定义契约,即:工单模块需要什么数据?工单模块能向外提供什么数据?

可扩展

abp本身提供了很强的扩展能力,你可以

  1. 通过“动态属性系统”来扩展实体类
  2. 通过工单CRUD、工单状态变化等事件来添加自己的业务逻辑
  3. 通过集成并替换工单模块提供领域服务、应用服务来重写现有业务逻辑
  4. 默认的UI只是结合我自己的项目用easyui实现的,你可以实现自己的UI
  5. 通过集成抽象工单实体、抽象工单的领域服务、抽象的工单应用服务来实现更多的工单类型

使用DDD开发方式

实践下DDD

核心业务逻辑在工单实体类中,它定义了相应的业务方法,内部会改变工单实体自身的一些状态,必要时触发相应事件,以此来确保工单始终能处于正确的状态,比如:某个已完成的工单无关联的员工或没有开始和完成时间;再比如某个已拒绝的工单,没有拒绝说明。如果实体的属性都是public get; set; 很容易出现这种问题,因为协作开发时别人很可能胡乱调用你的实体,随意设置值。

领域服务有少量代码,也触发相应的领域事件。

应用服务来接收前端调用,协调领域实体和服务来实现业务逻辑。

关于DDD下篇详细说明设计思路时再细说

集成

可扩展性中提到工单是抽象化的,但默认提供了一个”普通工单“的实现,因此安装并配置模块后此功能立即可用。另外也可用提供几个子类实现一个自定义类型的工单。

线上demo:http://web1.cqyuzuji.com:9000/  账号:admin 密码:123qwe

先在abp官方下载一个干净的abp项目,写此文章时用的abp6.3 .net 5。或者你可用在你目前的项目引入并测试。按以下步骤进行配置。

安装nuget包

相关nuget包都是以:BXJG.WorkOrder为前缀的。

先确保:

在解决方案上右键 > 管理解决方案的包 > 更新 -> Castle.Windsor.MsDependencyInjection 升级到3.4.0

在解决方案上右键 > 管理解决方案的包 > 更新 -> Microsoft.EntityFrameworkCore 更新到5.0.4

XXX.Core层中

Install-Package BXJG.WorkOrder.EFCore -Version 1.0.0-rc

XXX.EntityFrameworkCore层中

Install-Package BXJG.WorkOrder.EFCore -Version 1.0.0-rc

XXX.Application层中

Install-Package BXJG.WorkOrder.Application -Version 1.0.0-rc
Install-Package BXJG.WorkOrder.EmployeeApplication -Version 1.0.0-rc

工单模块中,后台管理工单和员工端对工单的操作是分开两个应用层项目定义的,根据你的情况决定是否分开,若分开则上面的包需要分开安装。

配置

在DbContext中注册相关实体

由于工单模块没有使用独立DbContext的方式,因此需要在你的主程序的DbContext中注册并配置”普通工单“和“工单分类”的实体。在XXX.EntityFrameworkCore层中找到你的DbContext,做如下配置:

注册权限和菜单

普通工单后台管理和员工端相关权限已定义为扩展方法,可以直接在主程序中调用,将其注册到主程序的权限树中。在XXX.Core/Authorization/XXXAuthorizationProvider中注册【普通工单】和【工单分类】的权限,为了演示将权限注册在了租户权限下面。

同理在BXJG.Web.Mvc/Startup/XXXNavigationProvider中注册【普通工单】和【工单分类】的菜单

context.Manager.MainMenu.AddBXJGWorkOrderNavigation();

注册动态api

由于开发模块时不确定你会如何使用工单模块的应用层,因此默认并未自动注册为动态web api,如果需要你可以自己配置,目前是手动,将来可能提供扩展方法一次性注册。在XXX.Web.Core/XXXWebCoreModule的PreInitialize()中配置启用工单模块中普通工单和工单分类的相关动态web api

添加模块依赖

虽然已添加了模块相关包引用,但此时这些包对于主程序来说仅仅是普通的dll,必须按abp的方式,让主程序的模块依赖工单模块,这样工单模块中的dll才会以Abp模块方式启动。由于工单模块只提供到应用程序级别,因此在主程序的Application层中的Module类中添加依赖是最合适的。在XXX.Application/XXXApplicationModule中添加模块依赖

添加模块适配代码

如前所述,工单模块不提供员工管理功能,但依赖员工信息,因此你需要提供一个适配,这也是完全独立的工单模块的关键。在XXXApplication中新增WorkOrder文件夹,然后定义如代码

当然还需要在XXXApplication中的Model文件的Initialize()中配置依赖注入

IocManager.Register<IEmployeeAppService, WorkOrder.EmployeeAppService>(DependencyLifeStyle.Transient);
IocManager.Register<IEmployeeSession, EmployeeSession>(DependencyLifeStyle.Transient);

数据库迁移

这个是按abp的套路,这不再详述。注意abp默认下载来的项目连接字符串是连接到localhost的,而vs2019的localdb稍有不同,我是改成如下形式的,你看着办

"Default": "Server=(localDB)\\mssqllocaldb; Database=BXJGDB; Trusted_Connection=True;"

运行

不出意外的话接口就可以访问了

  

  • WorkOrder:后台管理员对工单进行管理的接口,其中ChangeStatus是将工单跳跃或回退到指定状态,这个操作不是一步到位的,比如从”待审核“状态 跳跃到 ”已完成“中间会经历:确认、分配、执行、完成等步骤,操作员必须有这些步骤的权限,切工单状态必须正确(打个比方,分配时会判断工单是否已关联的处理人,只是假设,目前没做这个限制),这部分逻辑大多在工单实体中。
  • WorkOrderCategory:后台管理员对工单分类进行维护的接口
  • WorkOrderCommon:员工端或后台管理端都可以调用的接口,用来获取工单状态列表、紧急程度列表等
  • WorkOrderEmployee:员工端对工单进行操作的接口,获取待分配的工单、执行、完成工单等。

后续

  • 目前已实现真正的独立的业务模块
  • 配置还需要进一步简化
  • 目前只是基本能跑通工单流程,未作详细测试。
  • 上面说明了模块的基本集成,以及模块内默认实现的“普通工单”的功能,如何扩展后续会说明,比如:实现自定义类型的工单、如何通过继承、事件等方式来扩展工单模块等。
  • 目前模块只提供到应用层级别,也就是只提供后端接口,前端我使用的easyui,虽然可以使用abp的虚拟文件系统来实现UI模块化,但目前没有这样做,你可以使用自己喜欢的框架来完成UI

abp加DDD开发:低耦合、可复用、可扩展的【工单】业务模块-简介和集成的更多相关文章

  1. 面系那个对象开发原则.高内聚.低耦合+Python安装详细教程+print输出带颜色的方法

    面系那个对象开发原则.高内聚.低耦合 软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准.划分摸块的一个准则就是高内聚低耦合. 这是软件工程中的概念,是判断设计好坏的标准,主要是面向OO的设计, ...

  2. 针对接口编程能帮助达到面向对象开发和设计中"低耦合"的要求. 某公司...打印机...(笔试中遇到的题目)

    针对接口编程能帮助达到面向对象开发和设计中"低耦合"的要求.         举个例子:某公司有一台特殊打印机,还可以使用一年,一年后可能换为另一种打印机,这两种打印机都特殊而贵. ...

  3. 如何使用ABP进行软件开发(2) 领域驱动设计和三层架构的对比

    简述 上一篇简述了ABP框架中的一些基础理论,包括ABP前后端项目的分层结构,以及后端项目中涉及到的知识点,例如DTO,应用服务层,整洁架构,领域对象(如实体,聚合,值对象)等. 笔者也曾经提到,AB ...

  4. 模块内高内聚?模块间低耦合?MVC+EF演示给你看!

    前言 在软件项目开发过程中,我们总能听见“高内聚,低耦合”,即使这种思想在我们学习编程的过程中就已经耳濡目染.可一旦当我们上项目,赶进度的时候我们就会“偷懒”,能省时间就省.管他什么设计模式,什么软件 ...

  5. 如何使用ABP进行软件开发之基础概览

    ABP框架简述 1)简介 在.NET众多的技术框架中,ABP框架(本系列中指aspnetboilerplate项目)以其独特的魅力吸引了一群优秀开发者广泛的使用. 在该框架的赋能之下,开发者可根据需求 ...

  6. Java学习之浅析高内聚低耦合

    •前言 如果你涉及软件开发,可能会经常听到 "高内聚,低耦合" 这种概念型词语. 可是,何为 "高内聚,低耦合" 呢? •概念 "高内聚,低耦合&qu ...

  7. 网站迁移纪实:从Web Form 到 Asp.Net Core (Abp vNext 自定义开发)

    问题和需求 从2004年上线,ZLDNN.COM运行已经超过16年了,一直使用DotNetNuke平台(现在叫DNN Platform),从最初的DotNetNuke 2.1到现在使用的7.4.先是在 ...

  8. ABP框架个人开发实战(1)_环境搭建

    前言 之前关注ABP框架有一阵子了,一直没有潜下心来实际研究一下.最近想自己建站,以后有自己的功能开发项目,可以在自己的站点上开发,并一步步的完善,所以找个比较好用的框架迫在眉睫,选来选去,决定用AB ...

  9. 用abp vNext快速开发Quartz.NET定时任务管理界面

    今天这篇文章我将通过实例代码带着大家一步一步通过abp vNext这个asp.net core的快速开发框架来进行Quartz.net定时任务调度的管理界面的开发.大伙最好跟着一起敲一下代码,当然源码 ...

随机推荐

  1. webpack 5

    webpack 5 webpack 5 requires at least Node.js 10.13.0 (LTS). https://webpack.js.org/migrate/5/ https ...

  2. after upgrade macOS Catalina bugs

    after upgrade macOS Catalina bugs 升级了macOS catalina后,碰到的 bugs? macOS 10.15.5 https://www.apple.com/m ...

  3. how to tell a function arguments length in js

    how to tell a function arguments length in js JavaScript函数不对参数值(参数)执行任何检查 https://www.w3schools.com/ ...

  4. js currying function All In One

    js currying function All In One js 实现 (5).add(3).minus(2) 功能 例: 5 + 3 - 2,结果为 6 https://stackoverflo ...

  5. 1. VUE介绍

    今天开始系统学习vue前端框架. 我是有前端基础的, 刚工作那会, 哪里分那么清楚啊, 前后端我都得做, 所以, css, js, jquery, bootstrap都会点, 还系统学过ext, 哈哈 ...

  6. java的单例模式小知识点

    单例模式 目的 为了让一个类有且仅有一个实例 优点 只允许一个,节省空间 不用频繁创建删除,提高性能 缺点 不容易扩展 长期不使用会被系统当作垃圾回收,造成系统状态的丢失 实现 要点 防止外界随意的创 ...

  7. C++算法代码——级数求和[NOIP2002 普及组]

    题目来自:http://218.5.5.242:9018/JudgeOnline/problem.php?id=1078 或者:https://www.luogu.com.cn/problem/P10 ...

  8. idea 如何在新窗口中打开项目

    参考链接:https://blog.csdn.net/dreamfly88/article/details/52240188 以前一直再用eclipse,可以同时打开好几个项目,idea中同样支持打开 ...

  9. docker+tomcat+jenkin实现立即构建Springboot项目

    一.创建一个Springboot项目 1.编写pom.xml <groupId>com.zwhxpp</groupId> <artifactId>springboo ...

  10. 死磕Spring之IoC篇 - 解析自定义标签(XML 文件)

    该系列文章是本人在学习 Spring 的过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring 源码分析 GitHub 地址 进行阅读 Spring 版本:5.1. ...