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,
    }
}

  

  

时间: 2024-10-09 14:37:17

DDD领域驱动设计之聚合、实体、值对象的相关文章

DDD领域驱动设计之领域服务

1.DDD领域驱动设计实践篇之如何提取模型 2.DDD领域驱动设计之聚合.实体.值对象 3.DDD领域驱动设计之领域基础设施层 什么是领域服务,DDD书中是说,有些类或者方法,放实体A也不好,放实体B也不好,因为很可能会涉及多个实体或者聚合的交互(也可能是多个相同类型的实体),此时就应该吧这些代码放到领域服务中,领域服务其实就跟传统三层的BLL很相似,只有方法没有属性,也就没有状态,而且最好是用动词命名,service为后缀,但是真正到了实践的时候,很多时候是很难区分是领域实体本身实现还是用领域

DDD领域驱动设计之领域基础设施层

1.DDD领域驱动设计实践篇之如何提取模型 2.DDD领域驱动设计之聚合.实体.值对象 其实这里说的基础设施层只是领域层的一些接口和基类而已,没有其他的如日子工具等代码,仅仅是为了说明领域层的一些基础问题 1.领域事件简单实现代码,都是来至ASP.NET设计模式书中的代码 namespace DDD.Infrastructure.Domain.Events { public interface IDomainEvent { } } namespace DDD.Infrastructure.Dom

DDD领域驱动设计初探(一):聚合

前言:又有差不多半个月没写点什么了,感觉这样很对不起自己似的.今天看到一篇博文里面写道:越是忙人越有时间写博客.呵呵,似乎有点道理,博主为了证明自己也是忙人,这不就来学习下DDD这么一个听上去高大上的东西.前面介绍了下MEF和AOP的相关知识,后面打算分享Automapper.仓储模式.WCF等东西的,可是每次准备动手写点什么的时候,就被要写的Demo难住了,比如仓储模式,使用过它的朋友应该知道,如果你的项目不是按照DDD的架构而引入仓储的设计,那么会让它变得很“鸡肋”,用不好就会十分痛苦,之前

(转载)浅谈我对DDD领域驱动设计的理解

原文地址:http://www.cnblogs.com/netfocus/p/5548025.html 从遇到问题开始 当人们要做一个软件系统时,一般总是因为遇到了什么问题,然后希望通过一个软件系统来解决. 比如,我是一家企业,然后我觉得我现在线下销售自己的产品还不够,我希望能够在线上也能销售自己的产品.所以,自然而然就想到要做一个普通电商系统,用于实现在线销售自己企业产品的目的. 再比如,我是一家互联网公司,公司有很多系统对外提供服务,面向很多客户端设备.但是最近由于各种原因,导致服务经常出故

浅谈我对DDD领域驱动设计的理解

从遇到问题开始 当人们要做一个软件系统时,一般总是因为遇到了什么问题,然后希望通过一个软件系统来解决. 比如,我是一家企业,然后我觉得我现在线下销售自己的产品还不够,我希望能够在线上也能销售自己的产品.所以,自然而然就想到要做一个普通电商系统,用于实现在线销售自己企业产品的目的. 再比如,我是一家互联网公司,公司有很多系统对外提供服务,面向很多客户端设备.但是最近由于各种原因,导致服务经常出故障.所以,我们希望通过各种措施提高服务的质量和稳定性.其中的一个措施就是希望能做一个灰度发布的平台,这个

C#进阶系列——DDD领域驱动设计初探(一)

前言:又有差不多半个月没写点什么了,感觉这样很对不起自己似的.今天看到一篇博文里面写道:越是忙人越有时间写博客.呵呵,似乎有点道理,博主为了证明自己也是忙人,这不就来学习下DDD这么一个听上去高大上的东西.前面介绍了下MEF和AOP的相关知识,后面打算分享Automapper.仓储模式.WCF等东西的,可是每次准备动手写点什么的时候,就被要写的Demo难住了,比如仓储模式,使用过它的朋友应该知道,如果你的项目不是按照DDD的架构而引入仓储的设计,那么会让它变得很“鸡肋”,用不好就会十分痛苦,之前

C#进阶系列——DDD领域驱动设计初探(六):领域服务

前言:之前一直在搭建项目架构的代码,有点偏离我们的主题(DDD)了,这篇我们继续来聊聊DDD里面另一个比较重要的知识点:领域服务.关于领域服务的使用,书中也介绍得比较晦涩,在此就根据博主自己的理解谈谈这个知识点的使用. DDD领域驱动设计初探系列文章: C#进阶系列——DDD领域驱动设计初探(一):聚合 C#进阶系列——DDD领域驱动设计初探(二):仓储Repository(上) C#进阶系列——DDD领域驱动设计初探(三):仓储Repository(下) C#进阶系列——DDD领域驱动设计初探

DDD领域驱动设计仓储Repository

DDD领域驱动设计初探(二):仓储Repository(上) 前言:上篇介绍了DDD设计Demo里面的聚合划分以及实体和聚合根的设计,这章继续来说说DDD里面最具争议的话题之一的仓储Repository,为什么Repository会有这么大的争议,博主认为主要原因无非以下两点:一是Repository的真实意图没有理解清楚,导致设计的紊乱,随着项目的横向和纵向扩展,到最后越来越难维护:二是赶时髦的为了“模式”而“模式”,仓储并非适用于所有项目,这就像没有任何一种架构能解决所有的设计难题一样.本篇

C#进阶系列——DDD领域驱动设计初探(二):仓储Repository(上)

前言:上篇介绍了DDD设计Demo里面的聚合划分以及实体和聚合根的设计,这章继续来说说DDD里面最具争议的话题之一的仓储Repository,为什么Repository会有这么大的争议,博主认为主要原因无非以下两点:一是Repository的真实意图没有理解清楚,导致设计的紊乱,随着项目的横向和纵向扩展,到最后越来越难维护:二是赶时髦的为了“模式”而“模式”,仓储并非适用于所有项目,这就像没有任何一种架构能解决所有的设计难题一样.本篇通过这个设计的Demo来谈谈博主对仓储的理解,有不对的地方还望