zoukankan      html  css  js  c++  java
  • DDD领域驱动设计之领域服务

    1、DDD领域驱动设计实践篇之如何提取模型

    2、DDD领域驱动设计之聚合、实体、值对象

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

    什么是领域服务,DDD书中是说,有些类或者方法,放实体A也不好,放实体B也不好,因为很可能会涉及多个实体或者聚合的交互(也可能是多个相同类型的实体),此时就应该吧这些代码放到领域服务中,领域服务其实就跟传统三层的BLL很相似,只有方法没有属性,也就没有状态,而且最好是用动词命名,service为后缀,但是真正到了实践的时候,很多时候是很难区分是领域实体本身实现还是用领域服务区实现的,除了那些需要操作(一般是参数了)多个实体的方法外,有些单个实体的操作是很难严格区分的,实际上放实体和领域服务都可以,只是会有技术上的实现问题,比如实体里面怎么注入仓促的问题,如果放领域服务中了,就很容易注入了;还有一点就是实体或者聚合最好是不要去调用领域服务的,真是没有必要,如果要也会存在注入问题,所以比较合适的实践是,一些方法,如果有涉及系统性判断,如用户名唯一这种查找表的,那么就放到领域服务中,让运用层来调用,领域服务在去调用仓储。

    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.Arrange
    {
        public interface IPlanArrangeRepository : IRepository<PlanArrange>
        {
            /// <summary>
            /// 项目名称是否存在
            /// </summary>
            /// <param name="xmmc"></param>
            /// <returns></returns>
            bool ExistsXMMC(string xmmc);
            /// <summary>
            /// 是否已下发
            /// </summary>
            /// <param name="appc"></param>
            /// <param name="nd"></param>
            /// <param name="XZQDM"></param>
            /// <returns></returns>
            bool IsSent(int appc, int nd, string XZQDM);
    
            /// <summary>
            /// 统计计划安排表中,已经存储的指标数据
            /// </summary>
            /// <param name="year"></param>
            /// <param name="xzqdm"></param>
            /// <returns></returns>
            IndicatorArea TotalSendToIndicator(int year, string xzqdm);
        }
    }
    
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using DDD.Infrastructure.Domain;
    
    namespace DDD.Domain.Indicator
    {
        public interface IPlanIndicatorRepository : IRepository<PlanIndicator>
        {
            /// <summary>
            /// 获取预留指标
            /// </summary>
            /// <param name="year"></param>
            /// <param name="xzqdm"></param>
            /// <returns></returns>
            IndicatorArea TotalReserveIndicator(int year, string xzqdm);
            /// <summary>
            /// 获取指定行政区下发的指标(计划指标)
            /// </summary>
            /// <param name="year"></param>
            /// <param name="xzqdm"></param>
            /// <returns></returns>
            IndicatorArea TotalReceiveIndicator(int year, string xzqdm);
            /// <summary>
            /// 获取下发到指定行政区的指标
            /// </summary>
            /// <param name="year"></param>
            /// <param name="xzqdm"></param>
            /// <returns></returns>
            IndicatorArea TotalSendToIndicator(int year, string xzqdm);
    
            /// <summary>
            /// 是否已下发
            /// </summary>
            /// <param name="appc"></param>
            /// <param name="nd"></param>
            /// <param name="XZQDM"></param>
            /// <returns></returns>
            bool IsSent(int appc, int nd, string XZQDM);
            /// <summary>
            /// 是否存在已下发项目
            /// </summary>
            /// <param name="appc"></param>
            /// <param name="nd"></param>
            /// <param name="XZQDM"></param>
            /// <param name="XFXZQDM"></param>
            /// <returns></returns>
            bool Exists(int appc, int nd, string XZQDM, string XFXZQDM);
        }
    }
    

      

    2、领域服务

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using DDD.Domain.Indicator;
    using DDD.Infrastructure;
    
    namespace DDD.Domain.Arrange
    {
        /// <summary>
        /// 计划安排服务
        /// </summary>
        public class ArrangeService
        {
            private readonly IPlanArrangeRepository repository;
            /// <summary>
            /// service可以用多个不同的Repository接口吗
            /// </summary>
            private readonly IPlanIndicatorRepository indicatorRepository;
    
            public ArrangeService(IPlanArrangeRepository repository, IPlanIndicatorRepository indicatorRepository)
            {
                this.repository = repository;
                this.indicatorRepository = indicatorRepository;
            }
    
            /// <summary>
            /// 登记指标项目,如果是国家级别,那么projects可以不传
            /// </summary>
            /// <param name="planArrange"></param>
            /// <param name="planArranges"></param>
            public void Register(PlanArrange planArrange, IList<PlanArrange> planArranges)
            {
                CheckAndThrow(planArrange, false);
                planArrange.Register();
                if (planArranges != null)
                {
                    foreach (var item in planArranges)
                    {
                        item.APPC = planArrange.APPC;
                        item.ND = planArrange.ND;
                        item.XZQDM = planArrange.XZQDM;
                        item.Register();
                    }
                }
            }
    
            /// <summary>
            /// 这个方法是修改的时候调用判断的
            /// </summary>
            /// <param name="planArrange"></param>
            public void CheckUpdate(PlanArrange planArrange)
            {
                CheckAndThrow(planArrange, true);
            }
    
            private void CheckAndThrow(PlanArrange planArrange, bool isUpdate)
            {
                if (repository.IsSent(planArrange.APPC, planArrange.ND, planArrange.XZQDM))
                {
                    throw new DomainException("批次已下发,不允许登记或修改");
                }
                if (isUpdate)
                {
                    var original = repository.Find(planArrange.Id);
                    if (original.XMMC != planArrange.XMMC && repository.ExistsXMMC(planArrange.XMMC))
                    {
                        throw new DomainException("项目名称已存在");
                    }
                }
                else if(repository.ExistsXMMC(planArrange.XMMC)) 
                {
                    throw new DomainException("项目名称已存在");
                }
                CheckOverPlus(planArrange, isUpdate);
            }
    
            /// <summary>
            /// 判断剩余指标是否足够
            /// <p>总指标等于指标分解中预留部分,如果是县级,那么等于市级下发给县级的</p>
            /// <p>剩余指标等于总指标-下发的指标(包含项目已下发和未下发的项目)</p>
            /// </summary>
            /// <param name="planArrange"></param>
            private void CheckOverPlus(PlanArrange planArrange, bool isUpdate)
            {
                //总指标数,这里是不是应该用领域事件呢
                IndicatorArea totalIndicator = null;
                if (planArrange.ZBJB == IndicatorGrade.Province || planArrange.ZBJB == IndicatorGrade.City)
                {
                    totalIndicator = indicatorRepository.TotalReserveIndicator(planArrange.ND, planArrange.XZQDM);
                }
                else if (planArrange.ZBJB == IndicatorGrade.County)
                {
                    totalIndicator = indicatorRepository.TotalReceiveIndicator(planArrange.ND, planArrange.XZQDM);
                }
                if (totalIndicator != null)
                {
                    //计划单位是亩
                    var xfIndicator = repository.TotalSendToIndicator(planArrange.ND, planArrange.XZQDM);
                    xfIndicator += planArrange.JHSY;
                    if (isUpdate)
                    {
                        var original = repository.Find(planArrange.Id);
                        xfIndicator -= original.JHSY;
                    }
                    if (GreaterThan(xfIndicator.GD, totalIndicator.GD))
                    {
                        throw new DomainException("耕地剩余指标不足");
                    }
                    if (GreaterThan(xfIndicator.NYD, totalIndicator.NYD))
                    {
                        throw new DomainException("农用地剩余指标不足");
                    }
                    if (GreaterThan(xfIndicator.WLYD, totalIndicator.WLYD))
                    {
                        throw new DomainException("未利用地剩余指标不足");
                    }
                }
            }
    
            /// <summary>
            /// 
            /// </summary>
            /// <param name="mu"></param>
            /// <param name="hectare"></param>
            /// <returns></returns>
            private bool GreaterThan(decimal mu, decimal hectare)
            {
                decimal left = 0;
                decimal right = 0;
                if (mu > 0 && mu % 15 == 0)
                {
                    left = mu / 15;
                    right = hectare;
                }
                else
                {
                    left = mu * 666.6666667M;
                    right = hectare * 10000;
                }
                return left > right;
            }
        }
    }
    
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using DDD.Infrastructure;
    
    namespace DDD.Domain.Indicator
    {
        /// <summary>
        /// 计划指标登记服务
        /// </summary>
        public class IndicatorService
        {
            private readonly IPlanIndicatorRepository repository;
    
            public IndicatorService(IPlanIndicatorRepository repository)
            {
                this.repository = repository;
            }
    
            /// <summary>
            /// 登记指标项目,如果是国家级别,那么projects为空
            /// </summary>
            /// <param name="planIndicator"></param>
            /// <param name="planIndicators"></param>
            public void Register(PlanIndicator planIndicator, IList<PlanIndicator> planIndicators)
            {
                if (planIndicator.ZBJB != IndicatorGrade.Country)
                {
                    var totalArea = planIndicator.IndicatorArea +
                                    IndicatorArea.Sum(planIndicators.Select(t => t.IndicatorArea));
                    CheckAndThrow(planIndicator, totalArea, false);
                    //保证聚合完整性
                    foreach (var item in planIndicators)
                    {
                        item.APPC = planIndicator.APPC;
                        item.ND = planIndicator.ND;
                        item.XZQDM = planIndicator.XZQDM;
                        item.Register();
                    }
                }
                planIndicator.Register();
            }
    
            /// <summary>
            /// 这个方法是修改的时候调用判断的
            /// </summary>
            /// <param name="planIndicator"></param>
            public void CheckUpdate(PlanIndicator planIndicator)
            {
                CheckAndThrow(planIndicator, planIndicator.IndicatorArea, true);
            }
    
            /// <summary>
            /// 这个方法是修改的时候调用判断的
            /// </summary>
            /// <param name="planIndicator"></param>
            private void CheckAndThrow(PlanIndicator planIndicator,IndicatorArea area, bool isUpdate)
            {
                var original = isUpdate ? repository.Find(planIndicator.Id) : null;
                if (repository.IsSent(planIndicator.APPC, planIndicator.ND, planIndicator.XZQDM))
                {
                    throw new DomainException("批次已下发,不允许登记或修改");
                }
                //下发的时候判断,一个行政区只能一个文号
                if (planIndicator.XFXZQDM != planIndicator.XZQDM)
                {
                    if (isUpdate)
                    {
                        if(original.XFXZQDM != planIndicator.XFXZQDM && Exists(planIndicator))
                        {
                            throw new DomainException("同一批次中,不允许对同一个行政区下发多次");
                        }
                    }
                    else if(Exists(planIndicator))
                    {
                        throw new DomainException("同一批次中,不允许对同一个行政区下发多次");
                    }
                }
                var overIndicator = TotalOverPlusIndicator(planIndicator.ND, planIndicator.XZQDM);
                if (isUpdate)
                {
                    overIndicator += original.IndicatorArea;
                }
                if (area.NYD > overIndicator.NYD)
                {
                    throw new DomainException("农用地剩余指标不足");
                }
                if (area.GD > overIndicator.GD)
                {
                    throw new DomainException("耕地剩余指标不足");
                }
                if (area.WLYD > overIndicator.WLYD)
                {
                    throw new DomainException("未利用地剩余指标不足");
                }
            }
    
            /// <summary>
            /// 获取剩余指标
            /// </summary>
            /// <param name="year"></param>
            /// <param name="xzqdm"></param>
            /// <returns></returns>
            private IndicatorArea TotalOverPlusIndicator(int year, string xzqdm)
            {
                return repository.TotalReceiveIndicator(year, xzqdm) - repository.TotalReserveIndicator(year, xzqdm) - repository.TotalSendToIndicator(year, xzqdm);
            }
    
            private bool Exists(PlanIndicator planIndicator)
            {
                return repository.Exists(planIndicator.APPC, planIndicator.ND, planIndicator.XZQDM, planIndicator.XFXZQDM);
            }
        }
    }
    

      

  • 相关阅读:
    数据结构-队列的链式存储(链队列)
    数据结构-栈的实现之运算式求值
    数据结构-栈的实现之迷宫解密
    Ninject之旅之七:Ninject依赖注入
    Ninject之旅之六:Ninject约定
    Ninject之旅之五:Ninject XML配置
    Ninject之旅之四:Ninject模块
    Ninject之旅之三:Ninject对象生命周期
    Ninject之旅之二:开始使用Ninject(附程序下载)
    Ninject之旅之一:理解DI
  • 原文地址:https://www.cnblogs.com/liubiaocai/p/3938259.html
Copyright © 2011-2022 走看看