array(2) { ["docs"]=> array(10) { [0]=> array(10) { ["id"]=> string(3) "428" ["text"]=> string(77) "Visual Studio 2017 单独启动MSDN帮助(Microsoft Help Viewer)的方法" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(8) "DonetRen" ["tagsname"]=> string(55) "Visual Studio 2017|MSDN帮助|C#程序|.NET|Help Viewer" ["tagsid"]=> string(23) "[401,402,403,"300",404]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400964" ["_id"]=> string(3) "428" } [1]=> array(10) { ["id"]=> string(3) "427" ["text"]=> string(42) "npm -v;报错 cannot find module "wrapp"" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(4) "zzty" ["tagsname"]=> string(50) "node.js|npm|cannot find module "wrapp“|node" ["tagsid"]=> string(19) "[398,"239",399,400]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400760" ["_id"]=> string(3) "427" } [2]=> array(10) { ["id"]=> string(3) "426" ["text"]=> string(54) "说说css中pt、px、em、rem都扮演了什么角色" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(12) "zhengqiaoyin" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400640" ["_id"]=> string(3) "426" } [3]=> array(10) { ["id"]=> string(3) "425" ["text"]=> string(83) "深入学习JS执行--创建执行上下文(变量对象,作用域链,this)" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "Ry-yuan" ["tagsname"]=> string(33) "Javascript|Javascript执行过程" ["tagsid"]=> string(13) "["169","191"]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511399901" ["_id"]=> string(3) "425" } [4]=> array(10) { ["id"]=> string(3) "424" ["text"]=> string(30) "C# 排序技术研究与对比" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(9) "vveiliang" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(8) ".Net Dev" ["catesid"]=> string(5) "[199]" ["createtime"]=> string(10) "1511399150" ["_id"]=> string(3) "424" } [5]=> array(10) { ["id"]=> string(3) "423" ["text"]=> string(72) "【算法】小白的算法笔记:快速排序算法的编码和优化" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(9) "penghuwan" ["tagsname"]=> string(6) "算法" ["tagsid"]=> string(7) "["344"]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511398109" ["_id"]=> string(3) "423" } [6]=> array(10) { ["id"]=> string(3) "422" ["text"]=> string(64) "JavaScript数据可视化编程学习(二)Flotr2,雷达图" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "chengxs" ["tagsname"]=> string(28) "数据可视化|前端学习" ["tagsid"]=> string(9) "[396,397]" ["catesname"]=> string(18) "前端基本知识" ["catesid"]=> string(5) "[198]" ["createtime"]=> string(10) "1511397800" ["_id"]=> string(3) "422" } [7]=> array(10) { ["id"]=> string(3) "421" ["text"]=> string(36) "C#表达式目录树(Expression)" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(4) "wwym" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(4) ".NET" ["catesid"]=> string(7) "["119"]" ["createtime"]=> string(10) "1511397474" ["_id"]=> string(3) "421" } [8]=> array(10) { ["id"]=> string(3) "420" ["text"]=> string(47) "数据结构 队列_队列实例:事件处理" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "idreamo" ["tagsname"]=> string(40) "C语言|数据结构|队列|事件处理" ["tagsid"]=> string(23) "["246","247","248",395]" ["catesname"]=> string(12) "数据结构" ["catesid"]=> string(7) "["133"]" ["createtime"]=> string(10) "1511397279" ["_id"]=> string(3) "420" } [9]=> array(10) { ["id"]=> string(3) "419" ["text"]=> string(47) "久等了,博客园官方Android客户端发布" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(3) "cmt" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511396549" ["_id"]=> string(3) "419" } } ["count"]=> int(200) } 222 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);
        }
    }
}

  

相关文章: