用DDD模拟案例分析
之前我写了几篇关于DDD的介绍和一些小例子说明,我想这对于介绍DDD还是有些模糊,甚至还不知道怎么用DDD来分析设计。昨天和园友讨论也发现没有例子很难说明,所以今天我模拟了一个案例,同时这个案例也是真实的。在写此文时我并没有给出最终的解决方案,是用来和园友交流的,我会不定时把我们讨论的结果作更新,如果你喜欢的话可能需要随时关注,但我想这个结果可能也不会很快。
好了,说下这个需求的背景吧:
本人酷爱乒乓球运动,有机会参加了一个民间组织。此组织人员众多,而且固定活动的场所会有多个,成员可以选择其中的任何一个场所活动。新进成员或余额不足的成员会一次性交纳100左右的活动费,每次活动均由管理成员对参加活动的人员进行记录,然后去更新成员的余额,管理员初期采用手工记帐,统计非常麻烦,时间久了如果各个管理员之间的信息同步不及时的话就会让统计变得非常麻烦,就会造成会员不清楚自己的余额和消费情况,所以产生了去做一个管理软件的想法。目前虽然实现了一个,但没有使用DDD,以数据库为中心,用到现在也挺稳定。 那么我就想用DDD如何去做。
场景:
1.在活动现场有成员向管理员交纳预存款时,管理员会记录成员信息及编号。线下该管理员需要在系统中创建一个收款单,记录缴费的成员及金额。
2.现场管理员会记录参与当前活动的成员信息及编号。线下该管理员需要在系统中创建一个付款单,记录活动的成员及金额,同时单据上要实际标明实际场地费用。
业务需求:
1.只有是记帐用户才能创建收付款单据(即拥有account的user)。
2.只有具有CFO角色权限的user创建收付款单据时才能选择其他account,否则他只能选自己的account。
3.同样修改单据时只CFO角色权限的user才能变更account。
4.只有具有CFO角色权限的user才能作废所有的单据,否则只能作废自己的单据。
相关知识点:
1.用户是指参加此组织的所有成员(包括场地管理员和总管理员)
2.角色是指为某些用户定义了一些具有某些操作权限的小组。
3.帐户是指为某一个用户指定了一个收付款帐号(此帐户只能关联一个用户),组织成员交的活动经费会存入到此帐户下,场地费用也会从此帐户支出。
4.目前管理中权限角色分为普通用户(Nothing),只能查看自己的余额;记帐员(Cashier),可以创建或修改收付款单据,但在录入时只能选择自己的帐号。大管理员(CFO)没有记录的限制。
5.每个收付款单据会对应一个基金单据。(由于每次活动基本固定消费为5元/人,如此次有20人参加活动,而实际租场地费用为80元,这样多出来的20元会划入球队基金用于举办一些活动这样子的)
我先简单设计了一下,不一定是合理的,仅供参考

这是另一种画图

为了方便你的思考,帖上代码,你也可以先自己去实现一下,我也很期待你的方案。
using System;
using System.Collections.Generic;
using System.Linq; namespace Domain
{
public enum RoleType
{
Nothing,
Cashier,
CFO
} public class Role
{
public Guid Id { get; private set; }
public string Name { get; private set; }
public string Grants { get; private set; } public RoleType GetRoleType()
{
return RoleType.Nothing;
}
} public class User
{
public Guid Id { get; private set; }
public string Code { get; private set; }
public string Name { get; private set; }
public Guid RoleID { get; private set; }
public Guid AccountID { get; private set; }
public decimal Balance { get; }
} public class Account
{
public Guid Id { get; private set; }
public string Name { get; private set; }
public decimal Balance { get; }
} public class DocumentType
{
public Guid Id { get; private set; }
public string Name { get; private set; }
} /// <summary>
/// 单据收付款类型
/// </summary>
public enum ReceiptType
{
PAY,
RCV
} public class Receipt
{
public Receipt(Guid id)
{
this.Id = id;
this.ReceiptItems = new List<ReceiptItem>();
} public Guid Id { get; private set; }
public string ReceiptNo { get; private set; }
public Guid DocumentTypeID { get; private set; }
public DateTime Date { get; private set; }
public ReceiptType ReceiptType { get; private set; }
public Guid AccountID { get; private set; }
public bool IsCancel { get; private set; }
public string Remark { get; private set; }
public decimal TotalAmount { get; private set; }
public decimal FactAmount { get; private set; }
/// <summary>
/// 差额
/// </summary>
public decimal Profit
{
get { return this.FactAmount - this.TotalAmount; }
}
public virtual IList<ReceiptItem> ReceiptItems { get; private set; } public void Canceled()
{
if (this.IsCancel)
throw new Exception("单据已经作废,不能再次作废。"); this.IsCancel = true;
} private void ChangeDetails(IList<ReceiptItem> receiptItems)
{
var currentItemsCount = this.ReceiptItems.Count;
var submitItemsCount = receiptItems.Count; if (currentItemsCount > submitItemsCount) {
for (int index = currentItemsCount; index > submitItemsCount; index--) {
this.ReceiptItems.RemoveAt(index - );
}
} for (int index = ; index < submitItemsCount; index++) {
var item = receiptItems[index];
if (index >= currentItemsCount) {
this.ReceiptItems.Add(new ReceiptItem());
} this.ReceiptItems[index].RowNum = index + ;
this.ReceiptItems[index].UserID = item.UserID;
this.ReceiptItems[index].Money = item.Money;
} this.Totaled();
} private void Totaled()
{
if (this.ReceiptItems != null && this.ReceiptItems.Count > ) {
this.TotalAmount = this.ReceiptItems.Sum(p => p.Money);
}
} public class ReceiptItem
{
public Guid ReceiptID { get; set; }
public int RowNum { get; set; }
public Guid UserID { get; set; }
public decimal Money { get; set; }
}
} /// <summary>
/// 基金单据
/// </summary>
public class Fund
{
public Fund(Guid id)
{
this.Id = id;
} public Guid Id { get; private set; }
public string PropertyNo { get; private set; }
public Guid DocumentTypeID { get; private set; }
public decimal Money { get; private set; }
public DateTime Date { get; private set; }
public ReceiptType ReceiptType { get; private set; }
public Guid AccountID { get; private set; }
public bool IsCancel { get; private set; }
public string Remark { get; private set; } public void Canceled()
{
if (this.IsCancel)
throw new Exception("单据已经作废,不能再次作废。"); this.IsCancel = true;
} public static Fund CreateByReceipt(Receipt receipt)
{
Fund property = new Fund(receipt.Id); property.PropertyNo = receipt.ReceiptNo;
property.Date = receipt.Date;
property.AccountID = receipt.AccountID;
property.ReceiptType = receipt.Profit >= ? ReceiptType.RCV : ReceiptType.PAY;
property.DocumentTypeID = receipt.DocumentTypeID;
property.Money = Math.Abs(receipt.Profit);
property.Remark = receipt.Remark; return property;
}
}
}
最后为了方便你的理解,我画了一个ui帮助说明,创建一个付款单需要录入哪些数据

注:现金帐户就是account的列表。
期待园友们的讨论。。。
项目已初步开发完成。源码请直通https://github.com/imyounghan/ppqclub
用DDD模拟案例分析的更多相关文章
- ENode框架Conference案例分析系列之 - 业务简介
前言 ENode是一个应用开发框架.通过ENode,我们可以方便的开发基于DDD+CQRS+EventSourcing+EDA架构的应用程序.之前我已经写了很多关于ENode的架构以及设计原理的文章, ...
- CSS3-3D制作案例分析实战
一.前言 上一节,介绍了基础的CSS3 3D动画原理实现,也举了一个小小的例子来演示,但是有朋友跟我私信说想看看一些关于CSS3 3D的实例,所以在这里为了满足一下大家的需求,同时也为了以后能够更好的 ...
- K米APP案例分析
关于 K米 -- 的案例分析 产品 K米的APP (全国KTV点歌,手机直播,互动,交友,预订)的Android客户端 第一部分 调研,评测 评测: 软件的bug,功能评测,黑箱测试 • 下载并使用, ...
- 第三次个人作业——关于K米(Andorid)的案例分析
第三次个人作业--关于K米(Andorid)的案例分析 1.K米简介 官方网址:http://www.ktvme.com/ 2.评测 2.1.上手体验 带着找bug的心态,兴致勃勃地开始体验 K米.打 ...
- 161220、使用Spring AOP实现MySQL数据库读写分离案例分析
一.前言 分布式环境下数据库的读写分离策略是解决数据库读写性能瓶颈的一个关键解决方案,更是最大限度了提高了应用中读取 (Read)数据的速度和并发量. 在进行数据库读写分离的时候,我们首先要进行数据库 ...
- [Mugeda HTML5技术教程之15]案例分析:制作移动教育课件
本文档要分析的案例是一个一氧化碳还原氧化铜的教育小课件,从中可以体会一些Mugeda API的用法和使用Mugeda动画制作移动教育课件的方法.Mugeda为移动教育领域和移动数字出版领域提供理想的教 ...
- DDD实践案例:引入事件驱动与中间件机制来实现后台管理功能
DDD实践案例:引入事件驱动与中间件机制来实现后台管理功能 一.引言 在当前的电子商务平台中,用户下完订单之后,然后店家会在后台看到客户下的订单,然后店家可以对客户的订单进行发货操作.此时客户会在自己 ...
- 一个特殊的SQL Server阻塞案例分析
上周,在SQL Server数据库下面遇到了一个有意思的SQL阻塞(SQL Blocking)案例.其实个人对SQL Server的阻塞还是颇有研究的.写过好几篇相关文章. 至于这里为什么要总结一下这 ...
- 170301、使用Spring AOP实现MySQL数据库读写分离案例分析
使用Spring AOP实现MySQL数据库读写分离案例分析 原创 2016-12-29 徐刘根 Java后端技术 一.前言 分布式环境下数据库的读写分离策略是解决数据库读写性能瓶颈的一个关键解决方案 ...
随机推荐
- thinkphp5中使用excel导出数据表格(包涵图片)
首先使用composer require phpoffice/phpexcel下载安装phpexcel包. 将包放入extend下面. 不附加图片的导出 /** * 导出excel(不带图片) * @ ...
- Java 深拷贝,浅拷贝
一直听说这两个词,确实不知道代表啥意思?也不知道究竟要用来做什么?什么时候用到他们. 下面是从一篇博文种得到的解释: 浅复制(浅克隆) :被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他 ...
- 注解@ResponseBody的作用
@ResponseBody通常是放在方法上,主要是在前端页面异步请求的时候,返回数据使用.直白点说就是加上这个注解之后,return的数据不会解析成返回跳转路径,而是会默认放在 response b ...
- Gym - 100781G-Goblin Garden Guards
题目链接:https://nanti.jisuanke.com/t/28882 解题思路:单纯的判断点是否在圆内,一一遍历圆外切正方形内的点即可,注意,该题要建个结构体数组存每个地精的位置,再bool ...
- 第一个VS2015 Xaramin Android项目(续)
上文说到已经第一个 App已经可以运行,但是并不能调试! 经过细心发现,我察觉到VS刚开始进入了调试模式,但是一闪而过.也就是说调试失败了,此时需要等待一段时间才能打开此App,如果立即打开App 会 ...
- 《Miracle_House团队》第一次作业:团队亮相
Our Team:Miracle_House part 1 团队成员组成: NO.1 汝春瑞 201571030125 (组长) Style:乐观开朗,认真踏实,责任心强,还有就是爱笑.随和 ...
- Python实例浅谈之三Python与C/C++相互调用
一.问题 Python模块和C/C++的动态库间相互调用在实际的应用中会有所涉及,在此作一总结. 二.Python调用C/C++ 1.Python调用C动态链接库 Python调用C库比较简单,不经过 ...
- VUE 动态给对象增加属性,并触发视图更新。
在开发过程中,我们时常会遇到这样一种情况:当vue的data里边声明或者已经赋值过的对象或者数组(数组里边的值是对象)时,向对象中添加新的属性,如果更新此属性的值,是不会更新视图的. 根据官方文档定义 ...
- 结构体struct、联合体union、枚举类型enum
1.c语言中的类型 1)内置类型——char,short,int,float,double: 2)用户自定义类型(UDT)——struct结构体,union联合体,enum枚举类型 2.内存对齐 2. ...
- 1-学习tecplot360
FETRIANGLE(3节点三角形), FEQUADRILATERAL(4节点四边形),FETETRAHEDRON(4节点四面体), FEBRICK(8节点六面体) 参考:https://wenku. ...