回到目录

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

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

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

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

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

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

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

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

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

基础设施层

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

public interface IRepository<TEntity>
where TEntity : class
{
/// <summary>
/// 添加实体并提交到数据服务器
/// </summary>
/// <param name="item">Item to add to repository</param>
void Add(TEntity item); /// <summary>
/// 移除实体并提交到数据服务器
/// 如果表存在约束,需要先删除子表信息
/// </summary>
/// <param name="item">Item to delete</param>
void Remove(TEntity item); /// <summary>
/// 修改实体并提交到数据服务器
/// </summary>
/// <param name="item"></param>
void Modify(TEntity item); /// <summary>
/// 通过指定规约,得到实体对象
/// </summary>
/// <param name="specification"></param>
/// <returns></returns>
TEntity GetEntity(ISpecification<TEntity> specification); /// <summary>
/// 通用表达式树,得到实体
/// </summary>
/// <param name="predicate"></param>
/// <returns></returns>
TEntity GetEntity(Expression<Func<TEntity, bool>> predicate); /// <summary>
/// Get all elements of type {T} in repository
/// </summary>
/// <returns>List of selected elements</returns>
IQueryable<TEntity> GetEntities(); /// <summary>
/// Get all elements of type {T} that matching a
/// Specification <paramref name="specification"/>
/// </summary>
/// <param name="specification">Specification that result meet</param>
/// <returns></returns>
IQueryable<TEntity> GetEntities(ISpecification<TEntity> specification); /// <summary>
/// 通用表达式树,得到集合
/// </summary>
/// <param name="predicate"></param>
/// <returns></returns>
IQueryable<TEntity> GetEntities(Expression<Func<TEntity, bool>> predicate);
}

IOrderRepository接口如下

 public interface IOrderRepository :
Domain.Core.IRepository<Order_Info>
{
void InsertOrder(Order_Info entity);
}

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

领域层

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

    /// <summary>
/// 根据下单日期得到订单列表
/// </summary>
public class OrderFromDateSpecification : Specification<Order_Info>
{
DateTime? _fromDate;
DateTime? _toDate;
public OrderFromDateSpecification(DateTime? fromDate, DateTime? toDate)
{
_fromDate = fromDate ?? DateTime.MinValue;
_toDate = toDate ?? DateTime.MaxValue;
}
public override global::System.Linq.Expressions.Expression<Func<Order_Info, bool>> SatisfiedBy()
{
Specification<Order_Info> spec = new TrueSpecification<Order_Info>();
spec &= new DirectSpecification<Order_Info>(o => o.CreateDate >= _fromDate
&& o.CreateDate <= _toDate);
return spec.SatisfiedBy();
}
}
    /// <summary>
/// 通过用户信息得到他的订单列表
/// </summary>
public class OrderFromUserSpecification : Specification<Order_Info>
{
int _userID = default(Int32);
public OrderFromUserSpecification(int userID)
{
_userID = userID;
}
public override global::System.Linq.Expressions.Expression<Func<Order_Info, bool>> SatisfiedBy()
{
Specification<Order_Info> spec = new TrueSpecification<Order_Info>();
spec &= new DirectSpecification<Order_Info>(o => o.UserID == _userID);
return spec.SatisfiedBy();
}
}

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

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

WEB层

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

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

如果这时来了个新需要,使用用户名进行查询,你可以直接建立一个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. C# v3微信 access token 过期处理的问题

    //记录access token 申请时的时间 private static DateTime GetAccessToken_Time; /// <summary> /// 过期时间为72 ...

  2. [转]了解SQL Server锁争用:NOLOCK 和 ROWLOCK 的秘密_Mr_Indigo的空间

    了解SQL Server锁争用:NOLOCK 和 ROWLOCK 的秘密 关系型数据库,如SQL Server,使用锁来避免多用户修改数据时的并发冲突.当一组数据被某个用户锁定时,除非第一个用户结束修 ...

  3. Linux三剑客之awk

    awk awk是linux下的一个命令,他对其他命令的输出,对文件的处理都十分强大,其实他更像一门编程语言,他可以自定义变量,有条件语句,有循环,有数组,有正则,有函数等.他读取输出,或者文件的方式是 ...

  4. DLX (poj 3074)

    题目:Sudoku 匪夷所思的方法,匪夷所思的速度!!! https://github.com/ttlast/ACM/blob/master/Dancing%20Link%20DLX/poj%2030 ...

  5. Redis多机常用架构-sentinel

    哨兵经典架构 sentinel结构 哨兵工作原理 Sentinel 通过配置文件发现Master,如下: Sentinel 通过向Master发送 INFO 命令来自动获得所有Slave的地址 跟Ma ...

  6. ReStart

    ACM开始了?……重新启用Blog~

  7. uva-439

    题意:骑士在一个8*8的棋盘上移动,1-8代表行号,a-h代表列号,给出骑士的初始位置和目的位置,求骑士最少的移动步数:题目隐含一层意思(骑士移动规则是中国象棋的“马”的走法) 输入:一串字符串,包含 ...

  8. 8.4.4 Picasso

    Picasso 收到加载及显示图片的任务,创建 Request 并将它交给 Dispatcher,Dispatcher 分发任务到具体 RequestHandler,任务通过 MemoryCache ...

  9. tableindex

    在写代码的时候,失焦的第一反应便是ng-blur,没想到在一个标签上其作用了,多加了几个标签没反应,于是发现了tableindex,写的代码列子如下,希望可以帮助你: <img src='{{a ...

  10. List 源码分析笔记

    List Class Diagram: 笔记一: 1.Iterable 接口只定义一个iterator()方法. Iterator 接口有hasNext, next, remove方法. ListIt ...