系统要求

C/S架构的单体桌面应用,可以满足客户个性化需求,易于升级和维护。相比于一代Winform,界面要求美观,控件丰富可定制。

解决方案

依托.Net6开发平台,采用模块化思想设计(即分而治之的策略),每个模块采用DDD分层设计。前端选用WPF + Prism 框架,后端选用ABP + EF框架,数据库选择SQL Server。

业务拆分

核心领域:包含用户管理、客户管理、表具管理、方案管理、抄表管理

通用领域:包含权限、菜单、个人中心、参数配置、审计日志、数据字典

支撑领域:包含数据查询、统计报表、消息管理、STS安全、工单、自动升级

业务建模

通过业务拆分,水务领域已经被划分为若干子领域(即模块)。每个模块可以看成是一个限界上下文,在此边界内可以进一步拆分更细粒度的单元(我们把最小的业务单元叫做聚合)。一个模块也可能只有一个聚合,如:数据字典。

先拿简单的用户管理来说,用户(User)、角色(Role)、组织(Organization)三者关系紧密、彼此协作。一个用户可以拥有多个角色,一个组织可以拥有多个用户,因此可以归属到用户界限上下文,它们既是实体又是聚合根。

再拿复杂一点的客户管理来说,客户可分为预付费和后付费两种。对于预付费客户来说,可以按量或按金额购水。对于后付费客户来说,每月会生成一笔月账单,客户需要及时充值,否则就会欠费。因此,在客户这个业务边界内,可以确定客户(Customer)、客户类型(CustomerType)、交易记录(Transactions)、月账单(MonthlyBilling)等领域对象。其中水费的计算是一个非常复杂的业务逻辑,涉及到阶梯价格、附加费、债务、当月历史售水交易等多个领域实体,所以需要借助领域服务来完成这笔交易。

系统设计

系统设计包含两部分:全局设计局部设计

全局设计是从框架角度来进行整体设计。系统框图如下:

虚线框部分为笔者设计的Lapis.Framework框架,文件组织结构如下:

其中,Laison.Lapis.XXX文件夹代表基础业务模块(Laison.Lapis.Shared除外),如:审计日志、消息管理等,每个模块就是一个独立的C#解决方案。Laison.Lapis是一个底层类库,封装各种通用基础功能,供上层调用。Laison.Lapis.Shared称之为共享模块,用于封装和业务无关,可以被业务模块共享使用的功能,如: 抽象基类、接口、虚方法等。因为是单体应用设计,所以模块之间少不了相互引用的关系。模块划分越多,引用关系也会变得越复杂。

局部设计是从业务角度来进行模块设计,每个模块按照DDD来分层:基础设施层、领域层、应用层和表现层。下面是用户管理模块(Laison.Lapis.Identity)的分层设计:

Laison.Lapis.Identity.UI:表现层,包含前端页面、控件和一些UI逻辑。
     Laison.Lapis.Identity.Application.Contracts:应用接口层,负责定义应用接口和DTO。
     Laison.Lapis.Identity.Application:应用层,用于实现应用接口,包含各种应用逻辑。
     Laison.Lapis.Identity.Domain:领域层,负责业务逻辑处理,包含实体、值对象、仓储接口等领域模型。
     Laison.Lapis.Identity.Domain.Shared:领域共享层,包含了一些枚举和常量的定义,可供其它任意层调用。
     Laison.Lapis.Identity.EntityFramework:实体框架层(EF),包含实体的映射配置和仓储的实现。
     Laison.Lapis.Identity.HttpApi.Host 和 Laison.Lapis.Identity.Shell:即宿主和壳,分别用来启动前/后端应用程序。

在ABP框架的世界里,每一分层就是一个模块单元,它是代码层面的模块,而Identity是业务层面的模块,后者包含前者,最终以DLL的形式来呈现。

系统如何运行

每个模块负责自己的业务功能,各司其职,又彼此依赖。模块划分越细,内聚性越强,代码的复用性就越高。 当系统要完整运行所有功能时,只需要像搭积木一样,把各个模块进行组装就行,做到即插即用。为了方便代码复用和管理,笔者采用Nuget包的形式将业务模块引入到项目中使用,这在开发初期似乎没有什么问题。

但随着系统不断迭代,模块数量也再随之增长。假设1个模块会生成6个DLL(采用DDD分层),那么20个模块就会有120个DLL,听上去有点吓人。每次模块发生变化,就要重新打包、发布、升级120个Nuget包,这显然不符合常理(非常耗时)。后来笔者采取了一种折中的处理方式:将通用稳定性强的模块保留在Lapis.Framework中,把易于变化的模块下沉到项目中管理,这样就能避免之前的尴尬,但却牺牲了模块代码的复用性。项目最终代码结构如下:

个性化定制

企业个性化功能定制是一个普遍而又绕不开的话题,假如能用一套代码把所有客户的需求全部覆盖,当然是最理想的状态(现实几乎不可能,除非通用标准化)。所以笔者从设计一开始就是围绕如何提高代码的复用,将差异化分散到项目中的思路来展开,这也是采用模块化设计的初衷。至于个性化如何来实施?根据笔者已知的做法有以下两种:1. 通过版本管理开分支的方式,2. 简单粗暴,每个项目都单独复制一套标准版,然后在上面做定制化修改。 除此之外,笔者似乎没有想到更好的办法。至于选择哪一种,就要根据项目实际情况来考量。

结束语

无论是模块化设计还是DDD,都是强调从业务领域角度出发,根据业务的发展,合理划分业务边界,采用分治策略,降低业务和软件开发的复杂度,持续调整现有架构,以保持架构和代码的生命力。 因此,世界上并不存在一劳永逸的架构,软件设计本身就是一个平衡取舍的过程,只有合适的才是最好的,这也许就是架构设计的魅力所在吧!如果你有好的建议或不同想法,欢迎评论区留言。

二代水务系统架构设计分享——DDD+个性化的更多相关文章

  1. 万级TPS亿级流水-中台账户系统架构设计

    万级TPS亿级流水-中台账户系统架构设计 标签:高并发 万级TPS 亿级流水 账户系统 背景 业务模型 应用层设计 数据层设计 日切对账 背景 我们需要给所有前台业务提供统一的账户系统,用来支撑所有前 ...

  2. 基于Struts2,Spring4,Hibernate4框架的系统架构设计与示例系统实现

    笔者在大学中迷迷糊糊地度过了四年的光景,心中有那么一点目标,但总感觉找不到发力的方向. 在四年间,尝试写过代码结构糟糕,没有意义的课程设计,尝试捣鼓过Android开发,尝试探索过软件工程在实际开发中 ...

  3. 新零售SaaS架构:多租户系统架构设计

    什么是多租户? 多租户是SaaS领域的特有产物,在SaaS服务中,租户是指使用SaaS系统的客户,租户不同于用户,例如,B端SaaS产品,用户可能是某个组织下的员工,但整个企业组织是SaaS系统的租户 ...

  4. PetShop的系统架构设计

    <解剖PetShop>系列 一.PetShop的系统架构设计 http://www.cnblogs.com/wayfarer/archive/2007/03/23/375382.html ...

  5. petshop4.0 具体解释之中的一个(系统架构设计)

    前言:PetShop是一个范例,微软用它来展示.Net企业系统开发的能力.业界有很多.Net与J2EE之争,很多数据是从微软的PetShop和Sun的PetStore而来.这样的争论不可避免带有浓厚的 ...

  6. NET ERP系统架构设计

    解析大型.NET ERP系统架构设计 Framework+ Application 设计模式 我对大型系统的理解,从数量上面来讲,源代码超过百万行以上,系统有超过300个以上的功能,从质量上来讲系统应 ...

  7. Unity3D手游开发日记(2) - 技能系统架构设计

    我想把技能做的比较牛逼,所以项目一开始我就在思考,是否需要一个灵活自由的技能系统架构设计,传统的技能设计,做法都是填excel表,技能需要什么,都填表里,很死板,比如有的技能只需要1个特效,有的要10 ...

  8. 图数据库 Nebula Graph 的数据模型和系统架构设计

    Nebula Graph:一个开源的分布式图数据库.作为唯一能够存储万亿个带属性的节点和边的在线图数据库,Nebula Graph 不仅能够在高并发场景下满足毫秒级的低时延查询要求,而且能够提供极高的 ...

  9. 5G 融合计费系统架构设计与实现(一)

    5G 融合计费系统架构设计与实现(一) 随着5G商用临近,5G的各个子系统也在加紧研发调试,本人有兴全程参与5G中的融合计费系统(CCS)的设计.开发.联调工作.接下来将用几篇文章介绍我们在CCS实现 ...

  10. [转]【转】大型高性能ASP.NET系统架构设计

    大型高性能ASP.NET系统架构设计 大型动态应用系统平台主要是针对于大流量.高并发网站建立的底层系统架构.大型网站的运行需要一个可靠.安全.可扩展.易维护的应用系统平台做为支撑,以保证网站应用的平稳 ...

随机推荐

  1. Protobuf: 高效数据传输的秘密武器

    当涉及到网络通信和数据存储时,数据序列化一直都是一个重要的话题:特别是现在很多公司都在推行微服务,数据序列化更是重中之重,通常会选择使用 JSON 作为数据交换格式,且 JSON 已经成为业界的主流. ...

  2. 记一次 某智能制造MES系统CPU 爆高分析

    一:背景 1. 讲故事 前段时间有位朋友找到我,说他 docker 中的web服务深夜cpu被打满,让我帮忙看一下,很多朋友问docker中怎么抓dump,我一般都推荐使用 procdump 这款自动 ...

  3. 文心一言 VS chatgpt (7)-- 算法导论2.3 3~4题

    三.使用数学归纳法证明:当n刚好是2的幂时,以下递归式的解是 T(n)=nlgn.若n=2,T(n)=2:若n=2^k,k>1,T(n)=2T(n/2)+n. 文心一言: chatgpt: 首先 ...

  4. vue全家桶进阶之路49:Vue3 环境变量

    vue3 环境变量 在Vue3中,可以通过环境变量来管理应用程序中的配置.这些配置可以包括后端API的URL.API key.跟踪ID等等.在不同的开发.测试.生产环境中使用不同的配置是非常有用的.V ...

  5. vue全家桶进阶之路24:Mock

    Mock 是一个 JavaScript 库,用于生成随机数据或模拟 HTTP 请求响应,用于前端开发中的单元测试.功能测试.集成测试等场景. Mock 可以生成各种类型的数据,包括字符串.数字.布尔值 ...

  6. 2014年蓝桥杯C/C++大学B组省赛真题(奇怪的分式)

    题目描述: 上小学的时候,小明经常自己发明新算法.一次,老师出的题目是:1/4 乘以 8/5 小明居然把分子拼接在一起,分母拼接在一起,答案是:18/45 (参见图1.png)老师刚想批评他,转念一想 ...

  7. 代码随想录算法训练营Day37 贪心算法

    代码随想录算法训练营 代码随想录算法训练营Day37 贪心算法| 738.单调递增的数字 968.监控二叉树 总结 738.单调递增的数字 题目链接:738.单调递增的数字 给定一个非负整数 N,找出 ...

  8. Not a managed type: class com.example.commonspojo.entity,公共实体类剥离,然后引入报错的问题及解决办法

    最近搞springcloud项目遇到在商品服务中调用基本服务时jvm扫描不到的问题 需要加@entityscan 学习博客: (9条消息) Not a managed type: class com. ...

  9. 【python基础】if语句-条件测试

    1.初识if语句 编程时经常需要检查一系列条件,并据此决定采取什么措施.在Python中,使用if语句能够检查程序的当前状态,并据此采取相应措施. 其语法格式之一: 假设有一个学员名单的列表,想将其中 ...

  10. R EnhancedVolcano 绘制火山图

    火山图是用于差异表达分析结果可视化的一种有效方法.今天,我们来介绍一个用于增强火山图绘制的强大 R 包:EnhancedVolcano ,该包拥有强大的绘图功能,用户可以简单的通过设置颜色.形状.大小 ...