Welcome

首页 / 软件开发 / 数据结构与算法 / DDD领域驱动设计:领域服务

DDD领域驱动设计:领域服务2016-03-21 cnblogs 刘标才什么是领域服务,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);}}}