DDD领域驱动设计之聚合、实体、值对象
关于具体需求,请看前面的博文:DDD领域驱动设计实践篇之如何提取模型,下面是具体的实体、聚合、值对象的代码,不想多说什么是实体、聚合等概念,相信理论的东西大家已经知晓了。本人对DDD表示好奇,没有在真正项目实践过,甚至也没有看过真正的DDD实践的项目源码,处于极度纠结状态,甚至无法自拔,所以告诫DDD爱好者们,如果要在项目里面实践DDD,除非你对实体建模和领域职责非常了解(很多时候会纠结一些逻辑放哪里好,属于设计问题)以及你的团队水平都比较高认同DDD,否则请慎重。。。勿喷!
代码在后,请先看DEMO结果图
1、聚合的基类,注意,几乎属性都是拼音首字母命名,勿喷哈,不要跑题!
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using DDD.Infrastructure; using DDD.Infrastructure.Domain; namespace DDD.Domain { /// <summary> /// 项目基类 /// </summary> public abstract class ProjectBase : EntityBase, IAggregateRoot { protected ProjectBase() { this.ND = DateTime.Now.Year; this.CJSJ = DateTime.Now; this.WH = new DocumentNumber(); } /// <summary> /// 安排批次 /// </summary> public int APPC { get; set; } /// <summary> /// 项目名称 /// </summary> public string XMMC { get; set; } /// <summary> /// 项目编号 /// </summary> public string XMBH { get; internal set; } /// <summary> /// 年度 /// </summary> public int ND { get; set; } /// <summary> /// 文号 /// </summary> public DocumentNumber WH { get; set; } /// <summary> /// 创建时间 /// </summary> public DateTime CJSJ { get; set; } /// <summary> /// 下发行政区名称 /// </summary> public string XFXZQMC { get; set; } /// <summary> /// 下发行政区代码 /// </summary> public string XFXZQDM { get; set; } /// <summary> /// 行政区名称 /// </summary> public string XZQMC { get; set; } /// <summary> /// 行政区代码 /// </summary> public string XZQDM { get; set; } /// <summary> /// 备注 /// </summary> public string BZ { get; set; } /// <summary> /// 指标级别 /// </summary> public IndicatorGrade ZBJB { get; set; } /// <summary> /// 附件id /// </summary> public decimal ATTACHID { get; set; } /// <summary> /// 项目状态 /// </summary> public ProjectStauts Status { get; set; } /// <summary> /// 业务代码 /// </summary> protected abstract string BussinessCode { get; } /// <summary> /// 登记 /// </summary> /// <param name="seq"></param> public virtual void Register() { this.XMBH = this.BussinessCode + SeqGeneratr.Generate(); } /// <summary> /// 是否可以更新或者删除 /// </summary> /// <returns></returns> public virtual bool CanUpdate() { return this.ZBJB == IndicatorGrade.Country || this.XFXZQDM == this.XZQDM || this.Status == ProjectStauts.Default; } public void Send() { this.Status = ProjectStauts.Sent; } } }
2、聚合1
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using DDD.Infrastructure; using DDD.Infrastructure.Domain; namespace DDD.Domain.Indicator { /// <summary> /// 计划指标 /// </summary> public class PlanIndicator : ProjectBase { public PlanIndicator() { IndicatorArea = new IndicatorArea(); } protected override string BussinessCode { get { return "103101"; } } /// <summary> /// 指标面积 /// </summary> public IndicatorArea IndicatorArea { get; set; } public override IEnumerable<BusinessRule> Validate() { if (this.IndicatorArea.GD > this.IndicatorArea.NYD) { yield return new BusinessRule("IndicatorArea.GD", "耕地面积不能大于农用地面积"); } } public override void Register() { if (this.ZBJB == IndicatorGrade.Country) { this.Send(); } base.Register(); } } }
3、聚合2
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using DDD.Infrastructure; using DDD.Infrastructure.Domain; namespace DDD.Domain.Arrange { /// <summary> /// 计划安排 /// </summary> public class PlanArrange : ProjectBase { public PlanArrange() { JHSY = new IndicatorArea(); SJSY = new IndicatorArea(); } protected override string BussinessCode { get { return "103102"; } } /// <summary> /// 计划使用面积 /// </summary> public IndicatorArea JHSY { get; set; } /// <summary> /// 实际使用面积 /// </summary> public IndicatorArea SJSY { get; set; } /// <summary> /// 剩余面积 /// </summary> public IndicatorArea SY { get { return JHSY - SJSY; } } /// <summary> /// 用地类别 /// </summary> public string XMYDLB { get; set; } public override IEnumerable<BusinessRule> Validate() { if (this.JHSY.GD > this.JHSY.NYD) { yield return new BusinessRule("JHSY.GD", "计划使用中,耕地面积不能大于农用地面积"); } if (this.SJSY.GD > this.SJSY.NYD) { yield return new BusinessRule("SJSY.GD", "实际使用中,耕地面积不能大于农用地面积"); } if (string.IsNullOrEmpty(this.XMYDLB)) { yield return new BusinessRule("XMYDLB", "项目用地类别不允许为空"); } } } }
4、值对象1
using System; using DDD.Infrastructure.Domain; namespace DDD.Domain { /// <summary> /// 文号 /// </summary> public class DocumentNumber : ValueObject<DocumentNumber>, ICloneable { public static readonly string LeftYearChar = "〔"; public static readonly string RightYearChar = "〕"; public DocumentNumber() { } public DocumentNumber(string wh) { try { this.Code = wh.Substring(0, wh.IndexOf(LeftYearChar)); this.Year = wh.Substring(wh.IndexOf(LeftYearChar), wh.IndexOf(RightYearChar) - this.Code.Length + 1); this.Order = wh.Replace(this.Code + this.Year, ""); this.Year = this.Year.Replace(LeftYearChar, "").Replace(RightYearChar, ""); } catch(Exception ex) { throw new InvalidCastException("文号格式不正确", ex); } } /// <summary> /// 发文机关代字 /// </summary> public string Code { get; set; } /// <summary> /// 年份 /// </summary> public string Year { get; set; } private string order; /// <summary> /// 顺序号 /// </summary> public string Order { get { if (!string.IsNullOrEmpty(order) && !order.Contains("号")) { order += "号"; } return order; } set { order = value; } } public override string ToString() { if (string.IsNullOrEmpty(Code) && string.IsNullOrEmpty(Year) && string.IsNullOrEmpty(order)) { return string.Empty; } return this.Code + LeftYearChar + Year + RightYearChar + Order; } public static implicit operator DocumentNumber(string wh) { return new DocumentNumber(wh); } public object Clone() { return this.MemberwiseClone(); } } }
5、值对象2
using DDD.Infrastructure; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using DDD.Infrastructure.Domain; namespace DDD.Domain { /// <summary> /// 指标面积 /// </summary> public class IndicatorArea : ValueObject<IndicatorArea> { /// <summary> /// 新增建设用地 /// </summary> public decimal XZJSYD { get { return NYD + WLYD; } } /// <summary> /// 农用地 /// </summary> public decimal NYD { get; set; } /// <summary> /// 耕地 /// </summary> public decimal GD { get; set; } /// <summary> /// 未利用地 /// </summary> public decimal WLYD { get; set; } /// <summary> /// 将公顷转换成亩 /// </summary> /// <returns></returns> public IndicatorArea HectareToMu() { return new IndicatorArea { GD = this.GD * 15, NYD = this.NYD * 15, WLYD = this.WLYD * 15, }; } /// <summary> /// 重载加法运算符 /// </summary> /// <param name="a"></param> /// <param name="b"></param> /// <returns></returns> public static IndicatorArea operator +(IndicatorArea a, IndicatorArea b) { return new IndicatorArea { GD = a.GD + b.GD, NYD = a.NYD + b.NYD, WLYD = a.WLYD + b.WLYD, }; } /// <summary> /// 重载减法运算符 /// </summary> /// <param name="a"></param> /// <param name="b"></param> /// <returns></returns> public static IndicatorArea operator -(IndicatorArea a, IndicatorArea b) { return new IndicatorArea { GD = a.GD - b.GD, NYD = a.NYD - b.NYD, WLYD = a.WLYD - b.WLYD, }; } public static IndicatorArea Sum(IEnumerable<IndicatorArea> query) { return new IndicatorArea { GD = query.Sum(t => t.GD), NYD = query.Sum(t => t.NYD), WLYD = query.Sum(t => t.WLYD), }; } } }
6、枚举
using System.ComponentModel; namespace DDD.Domain { /// <summary> /// 指标级别 /// </summary> public enum IndicatorGrade { /// <summary> /// 国家 /// </summary> [Description("国家")] Country, /// <summary> /// 省级 /// </summary> [Description("省级")] Province, /// <summary> /// 市级 /// </summary> [Description("市级")] City, /// <summary> /// 县级 /// </summary> [Description("县级")] County, } }
using System.ComponentModel; namespace DDD.Domain { /// <summary> /// 项目状态 /// </summary> public enum ProjectStauts { /// <summary> /// 默认状态,已登记 /// </summary> Default, /// <summary> /// 已下发 /// </summary> Sent, } }