回到目录

规 约(Specification)模式:第一次看到这东西是在microsoft NLayer项目中,它是微软对DDD的解说,就像petshop告诉了我们MVC如何使用一样,这个规约模式最重要的作用是实现了查询语句与查询条件的 分离,查询语句在底层是稳定的,不变的,而查询条件是和具体业务,具体领域有关的,是易变的,如果我们为每一个领域的每一个新需求都写一个新的方法,那就 会出现很多重复的代码,不利于程序的最终扩展!

下面我们来看一个经典例子

一个IOrderRepository的接口,定义了一个订单仓储

  1. Order_Info GetOrder_InfoById(int orderID);
  2. List<Order_Info> GetOrder_Info(DateTime from, DateTime to);
  3. List<Order_Info> GetOrder_InfoByUser(int userID);

代码本身没有任何问题,你只要去实现它就可以了,当一个新的需求到了之后,你的接口要被扩展(这是不被提倡的,一般我们会新建一个接口),然后修改

原来的实现类,去实现接口新的方法(违背了OCP原则),这种做法是大多部开发团队所经历了,我,一个普通的人,也经历了,但当我知道DDD后,当我看完

microsoft Nlayer项目之后,我知道,我一定要改变这种局面,于是,代码在规约模式的指导下,进行重构了,呵呵。

先看一下规约模式的类关系图

下面是我对原来结构的修改(由于原程序是三层架构,所以我就不改变原有架构了,只是对代码进行重构,DAL层,BLL层,WEB层)

基础设施层

IRepository仓储接口如下,怎么去实现就不放了,呵呵

  1. public interface IRepository<TEntity>
  2. where TEntity : class
  3. {
  4. /// <summary>
  5. /// 添加实体并提交到数据服务器
  6. /// </summary>
  7. /// <param name="item">Item to add to repository</param>
  8. void Add(TEntity item);
  9.  
  10. /// <summary>
  11. /// 移除实体并提交到数据服务器
  12. /// 如果表存在约束,需要先删除子表信息
  13. /// </summary>
  14. /// <param name="item">Item to delete</param>
  15. void Remove(TEntity item);
  16.  
  17. /// <summary>
  18. /// 修改实体并提交到数据服务器
  19. /// </summary>
  20. /// <param name="item"></param>
  21. void Modify(TEntity item);
  22.  
  23. /// <summary>
  24. /// 通过指定规约,得到实体对象
  25. /// </summary>
  26. /// <param name="specification"></param>
  27. /// <returns></returns>
  28. TEntity GetEntity(ISpecification<TEntity> specification);
  29.  
  30. /// <summary>
  31. /// 通用表达式树,得到实体
  32. /// </summary>
  33. /// <param name="predicate"></param>
  34. /// <returns></returns>
  35. TEntity GetEntity(Expression<Func<TEntity, bool>> predicate);
  36.  
  37. /// <summary>
  38. /// Get all elements of type {T} in repository
  39. /// </summary>
  40. /// <returns>List of selected elements</returns>
  41. IQueryable<TEntity> GetEntities();
  42.  
  43. /// <summary>
  44. /// Get all elements of type {T} that matching a
  45. /// Specification <paramref name="specification"/>
  46. /// </summary>
  47. /// <param name="specification">Specification that result meet</param>
  48. /// <returns></returns>
  49. IQueryable<TEntity> GetEntities(ISpecification<TEntity> specification);
  50.  
  51. /// <summary>
  52. /// 通用表达式树,得到集合
  53. /// </summary>
  54. /// <param name="predicate"></param>
  55. /// <returns></returns>
  56. IQueryable<TEntity> GetEntities(Expression<Func<TEntity, bool>> predicate);
  57. }

IOrderRepository接口如下

  1. public interface IOrderRepository :
  2. Domain.Core.IRepository<Order_Info>
  3. {
  4. void InsertOrder(Order_Info entity);
  5. }

DAL底层为数据持久化层,它是非常稳定的,只提供最基本的表操作,具体业务如何组成,全放在BLL层去实现

领域层

这一层中定义具体业务的规约,并组成查询方法及调用DAL层的具体方法(DAL层来接受从BLL层传过来的ISpecification参数)

  1. /// <summary>
  2. /// 根据下单日期得到订单列表
  3. /// </summary>
  4. public class OrderFromDateSpecification : Specification<Order_Info>
  5. {
  6. DateTime? _fromDate;
  7. DateTime? _toDate;
  8. public OrderFromDateSpecification(DateTime? fromDate, DateTime? toDate)
  9. {
  10. _fromDate = fromDate ?? DateTime.MinValue;
  11. _toDate = toDate ?? DateTime.MaxValue;
  12. }
  13. public override global::System.Linq.Expressions.Expression<Func<Order_Info, bool>> SatisfiedBy()
  14. {
  15. Specification<Order_Info> spec = new TrueSpecification<Order_Info>();
  16. spec &= new DirectSpecification<Order_Info>(o => o.CreateDate >= _fromDate
  17. && o.CreateDate <= _toDate);
  18. return spec.SatisfiedBy();
  19. }
  20. }
  1. /// <summary>
  2. /// 通过用户信息得到他的订单列表
  3. /// </summary>
  4. public class OrderFromUserSpecification : Specification<Order_Info>
  5. {
  6. int _userID = default(Int32);
  7. public OrderFromUserSpecification(int userID)
  8. {
  9. _userID = userID;
  10. }
  11. public override global::System.Linq.Expressions.Expression<Func<Order_Info, bool>> SatisfiedBy()
  12. {
  13. Specification<Order_Info> spec = new TrueSpecification<Order_Info>();
  14. spec &= new DirectSpecification<Order_Info>(o => o.UserID == _userID);
  15. return spec.SatisfiedBy();
  16. }
  17. }

业务层真实的查询主体,只要在一个方法里写就OK了,然后它非常稳定,如果以后还有其它查询业务出来,直接添加一个查询规约即可

  1. /// <summary>
  2. /// 根据WEB层传来及组件好的规约,返回集体
  3. /// </summary>
  4. /// <param name="spec"></param>
  5. /// <returns></returns>
  6. public List<Order_Info> GetOrder_InfoBySpec(ISpecification<Order_Info> spec)
  7. {
  8. return _iOrderRepository.GetEntities(spec).ToList();
  9. }

WEB层

Web层建立一个指定的规约,并为规约组件所需要的数据即可

  1. public ActionResult List(int? userID)
  2. {
  3. ISpecification<Order_Info> spec = new OrderFromUserSpecification(userID ?? );
  4. var model = orderService.GetOrder_InfoBySpec(spec);
  5. return View(model);
  6. }

如果这时来了个新需要,使用用户名进行查询,你可以直接建立一个OrderFromUserNameSpecification的规约即可,而不需要修改OrderService,呵呵!

回到目录

DDD~领域服务的规约模式的更多相关文章

  1. 如何运用DDD - 领域服务

    目录 如何运用DDD - 领域服务 概述 什么是领域服务 从实际场景下手 更贴近现实 领域服务VS应用服务 扩展上面的需求 最常见的认证授权是领域服务吗 使用领域服务 不要过多的使用领域服务 不要将过 ...

  2. DDD领域驱动设计架构模式:防腐层(Anti-corruption layer)

    在微服务(Microservices)架构实践中,架构设计借用了DDD中的一些概念和技术,比如一个微服务对应DDD中的一个限界上下文(Bounded Context):在微服务设计中应该首先识别出DD ...

  3. [.NET领域驱动设计实战系列]专题五:网上书店规约模式、工作单元模式的引入以及购物车的实现

    一.前言 在前面2篇博文中,我分别介绍了规约模式和工作单元模式,有了前面2篇博文的铺垫之后,下面就具体看看如何把这两种模式引入到之前的网上书店案例里. 二.规约模式的引入 在第三专题我们已经详细介绍了 ...

  4. 基于DDD的.NET开发框架 - ABP领域服务

    返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...

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

    1.DDD领域驱动设计实践篇之如何提取模型 2.DDD领域驱动设计之聚合.实体.值对象 3.DDD领域驱动设计之领域基础设施层 什么是领域服务,DDD书中是说,有些类或者方法,放实体A也不好,放实体B ...

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

    前言:之前一直在搭建项目架构的代码,有点偏离我们的主题(DDD)了,这篇我们继续来聊聊DDD里面另一个比较重要的知识点:领域服务.关于领域服务的使用,书中也介绍得比较晦涩,在此就根据博主自己的理解谈谈 ...

  7. 生产环境下实践DDD中的规约模式

    最近的开发工作涉及到两个模块“任务”和“日周报”.关系是日周报消费任务,因为用户在写日周报的时候,需要按一定的规则筛选当前用户的任务,作为日周报的一部分提交.整个项目采用类似于Orchard那种平台加 ...

  8. [.NET领域驱动设计实战系列]专题三:前期准备之规约模式(Specification Pattern)

    一.前言 在专题二中已经应用DDD和SOA的思想简单构建了一个网上书店的网站,接下来的专题中将会对该网站补充更多的DDD的内容.本专题作为一个准备专题,因为在后面一个专题中将会网上书店中的仓储实现引入 ...

  9. DDD理论学习系列(8)-- 应用服务&领域服务

    DDD理论学习系列--案例及目录 1. 引言 单从字面理解,不管是领域服务还是应用服务,都是服务.而什么是服务?从SOA到微服务,它们所描述的服务都是一个宽泛的概念,我们可以理解为服务是行为的抽象.从 ...

随机推荐

  1. Android中的桌面快捷方式

    一.判断是否已有快捷方式 private String getAuthorityFromPermission(Context context, String permission){ if (perm ...

  2. angular之自定义过滤器的使用

    自定义过滤器需要使用filter函数,格式如下: filter("filterName',function(){ return function(target,args){ .... } } ...

  3. java设计模式之-----桥接模式

    一.概念定义 将抽象部分和实现部分分离,使他们都可以独立的变化.(这句话有点抽象啊..) 在软件系统中,某种类型由于自身逻辑,具有多个维度的变化,如何利用面向对象的技术来使得该类型沿着多方向变化,而又 ...

  4. what is a ear

    http://docs.oracle.com/javaee/6/tutorial/doc/bnaby.html An EAR file (see Figure 1-6) contains Java E ...

  5. [spark案例学习] 单词计数

    数据准备 数据下载:<莎士比亚全集> 我们先来看看原始数据:首先将数据加载到RDD,然后显示数据框的前15行. shakespeareDF = sqlContext.read.text(f ...

  6. Linux内核分析之扒开系统调用的三层皮(下)

    一.实验内容 1. 通过内核的方式使用系统调用 需要使用的命令 rm menu -rf //强制删除当前menugit clone http://github.com/mengning/menu.gi ...

  7. javascript 中的事件机制

    1.javascript中的事件. 事件流 javascript中的事件是以一种流的形式存在的. 一个事件会也有多个元素同时响应. 有时候这不是我们想要的效果, 我们只是需要某个特定的元素相应我们的绑 ...

  8. Hyper-V初涉_Hyper-V虚拟机文件交换

    使用虚拟机时,文件交互就显得十分重要.如果能实现物理机与虚拟机之间的文件交互,将会节省大量的时间.比较可惜的是,Hyper-V虚拟机并不支持USB存储设备,所以在文件交换上略显麻烦. 与Hyper-V ...

  9. CPU与内存的关系

    至今才对cpu和内存有一定了解了,下面有几个比喻的理解,很形象呦~ 1# 例如你要吃东西时,硬盘是锅,内存是碗,CPU是你,浅显点就是这样子了~ 2# 例如电脑是企业,内存是车间,cpu是生产线,硬盘 ...

  10. 开启个人blog~

    希望自己能坚持写blog,不断的总结,不断的沉淀.