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

一、前言

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

二、什么是规约模式

  讲到规约模式,自然想到的是什么是规约模式呢?从名字上看,规约模式就是一个约束条件,我们在使用仓储进行查询的时候,这时候就会牵涉到很多查询条件,例如名字包含C#的书名等条件。这样就自然需要引入规约模式了。规约模式的作用可以自由组装业务逻辑元素。Specification类有一个IsSatisifiedBy函数,用于校验某个对象是否满足该Specification所表达的条件。多个Specification对象可以组装起来,生成新的Specification对象,这样可以通过组装的方式来定制新的条件。简单地说,规约模式就是对查询条件表达式用类的形式进行封装。那这样的话,规约模式引入有什么作用呢?

三、为什么需要引入规约模式模式

  上面只是简单介绍了规约模式的作用——可以自由组装业务逻辑元素。这样文字表述未免枯燥了点,下面通过一个具体例子来说明下。

  对于在仓储中,我们经常会定义下面的接口

 public interface IProductRespository
    {
        Product GetById(Guid id);
        Product GetByName(string name);
        IEnumerable<Product> GetNewProducts();
    }

  接下来就是实现这个接口,并在类中分别实现接口中的方法。这样设计的好处就是一目了然,可以方便地看到Product仓储到底提供了哪些功能。

  对于这种设计,对于简单系统并且今后扩展的可能性不大,那么这样的设计非常合适,因为其简洁高效。但如果你正在设计一个中大型系统,那么,针对上面的设计,你就需要考虑下面的问题了:

  1. 今后如果需要添加新的查询逻辑,结果一大堆相关代码都需要修改,上面的设计能便于扩展吗?
  2. 由于业务的扩展,上面的设计会导致接口变得越来越大,团队成员可能会对这个接口进行修改,添加新的接口方法。

  规约模式就是DDD引入解决上面问题的一种模式。下面让我们来看看规约模式的定义与实现。

四、规约模式的传统实现

  首先来看下规约模式的类结构图:

  上图是摘自维基百科里面的,通过设计图我们很容易实现规约模式,这样之所以称为的传统实现,因为后面会对该实现应用C#的特性来对该实现进行简化,使其更加简单轻量。首先我们需要定义一个ISpecification接口,在接口中定义四个方法:And、Not、Or和IsSatifiedBy方法,具体接口的定义如下所示:

// 规约接口的定义
    public interface ISpecification<T>
    {
        bool IsSatisfiedBy(T candidate);
        ISpecification<T> And(ISpecification<T> specification);
        ISpecification<T> Or(ISpecification<T> specification);
        ISpecification<T> Not(ISpecification<T> specification);
    }

  实现了ISpeification的对象意味着是一个Specification,即一种筛选条件,我们可以与其他Specification对象通过And、Or和Not操作来生成新的逻辑,即组合成新的筛选条件,为了方便“组合逻辑”的实现,这里还需要定义一个抽象的CompositeSpecification类:

// 因为And,OR和Not方法在所有的Specification都需要实现,只有IsSatisfiedBy方法才依赖业务规则
    // 所以为了复用,定义一个抽象类来实现And,Or和And操作,并且留IsSatisfiedBy方法给子类去实现,所以定义其为abstract
    public abstract class CompositeSpecification<T>: ISpecification<T>
    {
        public abstract bool IsSatisfiedBy(T candidate);

        public ISpecification<T> And(ISpecification<T> specification)
        {
            return new AndSpecification<T>(this, specification);
        }

        public ISpecification<T> Or(ISpecification<T> specification)
        {
            return new OrSpecification<T>(this, specification);
        }

        public ISpecification<T> Not(ISpecification<T> specification)
        {
            return new NotSpecification<T>(specification);
        }
    }

  CompositeSpecification提供了构建符合Specification的基础逻辑,它提供了And、Or和Not方法的实现,让其他Specification类只需要专注于IsSatisfiedBy方法的实现即可(这里有点模板方法模式的影子)。下面是And、Or和Not规约的具体实现:

// AndSpecification,OrSpecification and NotSpecification主要为了组合
    public class AndSpecification<T> : CompositeSpecification<T>
    {
        private  readonly ISpecification<T> _lefSpecification;
        private readonly ISpecification<T> _rightSpecification;

        public AndSpecification(ISpecification<T> left, ISpecification<T> right)
        {
            this._lefSpecification = left;
            this._rightSpecification = right;
        }

        public override bool IsSatisfiedBy(T candidate)
        {
            return this._lefSpecification.IsSatisfiedBy(candidate)
                && this._rightSpecification.IsSatisfiedBy(candidate);
        }
    }

public class OrSpecification<T> : CompositeSpecification<T>
    {
        private readonly ISpecification<T> _leftSpecification;
        private readonly ISpecification<T> _rightSpecification;

        public OrSpecification(ISpecification<T> left, ISpecification<T> right)
        {
            this._leftSpecification = left;
            this._rightSpecification = right;
        }

        public override bool IsSatisfiedBy(T candidate)
        {
            return _leftSpecification.IsSatisfiedBy(candidate)
                || _rightSpecification.IsSatisfiedBy(candidate);
        }
    }

 public class NotSpecification<T> : CompositeSpecification<T>
    {
        private readonly ISpecification<T> _specification;

        public NotSpecification(ISpecification<T> specification)
        {
            this._specification = specification;
        }
        public override bool IsSatisfiedBy(T candidate)
        {
            return !_specification.IsSatisfiedBy(candidate);
        }
    }

  接下来我们可以定义具体的规约模式,如果IdEqualSpecification、NameEqualSpecification规约等。下面就看下引入规约模式后,是如何解决上面仓储接口设计所存在的问题的。

// 引入规约模式,IProductRespository接口的定义
    public interface IProductRespository
    {
        Product GetBySpecification(ISpecification<Product> spec);
        IEnumerable<Product> FindBySpecification(ISpecification<Product> spec);
    }

    public class IdEqualSpecification : CompositeSpecification<Product>
    {
        private readonly Guid _id;
        public IdEqualSpecification(Guid id)
        {
            _id = id;
        }

        public override bool IsSatisfiedBy(Product candidate)
        {
            return candidate.Id.Equals(_id);
        }
    }

    public class NameEqualSpecification : CompositeSpecification<Product>
    {
        private readonly string _name;

        public NameEqualSpecification(string name)
        {
            _name = name;
        }

        public override bool IsSatisfiedBy(Product candidate)
        {
            return candidate.Name.Equals(_name);
        }
    }

    public class NewProductsSpecification : CompositeSpecification<Product>
    {
        public override bool IsSatisfiedBy(Product candidate)
        {
            return candidate.IsNew == true;
        }
    }

  通过引入规约后,Product仓储中所有特定用途的操作都删除了,取而代之的是2个非常简洁的方法。规约模式解耦了仓储操作和筛选条件,如果业务扩展,我们可以定制我们的Specification,并将其注入到仓储即可。仓储的接口和实现无需任何修改。

  下面通过一个具体的演示例子来看下传统规约模式的应用。具体的场景是这样的:我们想筛选一批int数组中的偶数和大于0的数字出来。因为这里涉及2个筛选条件,一个是偶数,一个是大于0的数,这样我们就可以通过定义偶数规约和正数规约。具体的实现如下所示:

// 具体规约,偶数规约
    public class EvenSpecification : CompositeSpecification<int>
    {
        public override bool IsSatisfiedBy(int candidate)
        {
            return candidate % 2 == 0;
        }
    }

    // 具体的规约,正数规约
    public class PlusSpecification  : CompositeSpecification<int>
    {
        public override bool IsSatisfiedBy(int candidate)
        {
            return candidate > 0;
        }
    }

  接下来通过And操作和将2中规约组合起来形成新的规约。具体的测试代码如下所示:

using spec1 =SpecificationPatternDemo.Specification;

class Program
    {
        static void Main(string[] args)
        {
            Demo1();
            Console.Read();
        }

        public static void Demo1()
        {
            var items = Enumerable.Range(-5, 10);            Console.WriteLine("产生的数组为:{0}", string.Join(",  ", items.ToArray()));
            spec1.ISpecification<int> evenSpec = new spec1.EvenSpecification();
            // 获得一个组合规约
            var compositeSpecification = GetCompositeSpecification(evenSpec);
            // 类似Where(it=>it%2==0 && it > 0)
            // 前者是把两个条件合并写死成一个条件,而后者是将其组合成一个新条件。就如拼图出一架飞机和直接制造一个飞机模型概念是完全不同的
            foreach (var item in items.Where(it=>compositeSpecification.IsSatisfiedBy(it)))
            {
                // 输出既是正数又是偶数的数
                Console.WriteLine(item);
            }
        }

        private static spec1.ISpecification<int> GetCompositeSpecification(spec1.ISpecification<int> spec)
        {
            spec1.ISpecification<int> plusSpec = new spec1.PlusSpecification();
            return spec.And(plusSpec);
        }
}

  具体的运行结果如下图所示:

  上面我们已经介绍完规约模式的实现,并且通过对比的方式来介绍引入规约模式所解决之前的问题。但是传统规约模式的实现显得非常臃肿。因为你想实现一个新的规约,你需要新增一个新的Specification类,这样下来,我们的项目中必然会堆积大量的Specification类。有些规约可能只只使用了一次。这就好比.NET里的委托方法一样,为了解决类似的问题,.NET引入了匿名方法和lamada表达式。同样,我们借助C#的特性也可以使得传统规约模式的实现更轻量。下面就具体看下规约模式的轻量实现是如何去实现的。

五、规约模式的轻量实现

  从上面可以看出,规约模式的关键在于IsSatisifiedBy函数,该函数用于校验某个对象是否满足该规约所表示的条件,IsSatisifiedBy函数的返回类型为bool类型,这样我们完全可以让一个ISpecification只具有IsSatisifiedBy函数。然后该函数返回一个委托调用结果。至于原本ISpecification中的And、Or和Not方法,我们将它们提起成扩展方法。经过上面的分析,轻量化后的规约模式实现也就出来了。具体实现如下所示:

 public interface ISpecification<in T>
    {
        bool IsSatisfiedBy(T candidate);
    }

    public class Specification<T> : ISpecification<T>
    {
        private readonly Func<T, bool> _isSatisfiedBy;

        public Specification(Func<T, bool> isSatisfiedBy)
        {
            this._isSatisfiedBy = isSatisfiedBy;
        }

        public bool IsSatisfiedBy(T candidate)
        {
            return _isSatisfiedBy(candidate);
        }
    }

    public static class SpecificationExtensions
    {
        public static ISpecification<T> And<T>(this ISpecification<T> left, ISpecification<T> right)
        {
            return new Specification<T>(candidate => left.IsSatisfiedBy(candidate) && right.IsSatisfiedBy(candidate));
        }

        public static ISpecification<T> Or<T>(this ISpecification<T> left, ISpecification<T> right)
        {
            return new Specification<T>(candidate => left.IsSatisfiedBy(candidate) || right.IsSatisfiedBy(candidate));
        }

        public static ISpecification<T> Not<T>(this ISpecification<T> one)
        {
            return new Specification<T>(candidate => !one.IsSatisfiedBy(candidate));
        }
    }

  使用扩展方法的好处在于,如果我们要加一个逻辑运行,如异或,那么就不需要修改接口了。修改接口是一个不推荐的的事情。因为接口修改会破坏之前已经发布的接口实现。因此,一旦接口发布之后,它就不能被修改了。这意味着,我们在定义接口时应该仔细推敲,做到接口的职责应该尤其单一。

  轻量的实现使得使用Specification对象容易多了,我们不需要为每段逻辑创建一个独立的Specification类。下面具体看下规约模式的轻量实现的使用示例:

using SpecificationPatternDemo.Specification_2;
using spec2 = SpecificationPatternDemo.Specification_2;
class Program
    {
        static void Main(string[] args)
        {
            Demo2();
            Console.Read();
        }

         public static void Demo2()
        {
            var items = Enumerable.Range(-5, 10);
            Console.WriteLine("产生的数组为:{0}", string.Join(",  ", items.ToArray()));
            spec2.ISpecification<int> evenSpec = new spec2.Specification<int>(it => it % 2 == 0);

            var compositeSpec = GetCompositeSpecification2(evenSpec);

            foreach (var i in items.Where(it => compositeSpec.IsSatisfiedBy(it)))
            {
                Console.WriteLine(i);
            }
        }

        private static spec2.ISpecification<int> GetCompositeSpecification2(spec2.ISpecification<int> spec)
        {
            spec2.ISpecification<int> plusSpec = new spec2.Specification<int>(it => it > 0);
            return spec.And(plusSpec);
        }
}

  从上面的例子可以看出,此时并不需要定义单独的Specification对象了,只需要用委托来代替即可。其运行结果与上面传统实现一样。其实,还可以更简单,我们可以直接使用一个委托,而不不需要定义ISpecification接口和其Specification实现。其实现方式如下所示:

// 更轻量的实现
    public static  class SpecExtensitions
    {
        public static Func<T, bool> And<T>(this Func<T, bool> left, Func<T, bool> right)
        {
            return candidate => left(candidate) && right(candidate);
        }

        public static Func<T, bool> Or<T>(this Func<T, bool> left, Func<T, bool> right)
        {
            return candidate => left(candidate) || right(candidate);
        }

        public static Func<T, bool> Not<T>(this Func<T, bool> one)
        {
            return candidate => !one(candidate);
        }
    }

  上面的实现,我们就只需要一个扩展方式就可以了,其使用示例代码如下所示:

class Program
    {
        static void Main(string[] args)
        {
            Demo3();
            Console.Read();
        }

        public static void Demo3()
        {
            var items = Enumerable.Range(-5, 10);
            Console.WriteLine("产生的数组为:{0}", string.Join(",  ", items.ToArray()));

           Func<int, bool> evenSpec = it => it % 2 == 0;

            var compositeSpec = GetCompositeSpec(evenSpec);

            foreach (var i in items.Where(it => compositeSpec(it)))
            {
                Console.WriteLine(i);
            }
        }

        private static Func<int, bool> GetCompositeSpec(Func<int, bool> spec)
        {
            return spec.And(it => it > 0);
        }
    }

六、规约模式的轻量实现的完善——对Linq查询支持

  上面轻量级的Specification模式抛弃了具体的Specification类型,而是使用一个委托对象关键的IsSatisfiedBy方法。其优势在于使用简单,但是该实现不能支持Linq查询或表达式的场景。因为EF中的DbContext.Dbset集合的进行where筛选参数只能是表达式树。所以我们不能用委托对象来判断逻辑,取而代之的使用表达式树。对于表达式树的构造主要由参数和主体构造,所以针对于Not方法可以如下的方式来实现:

 public static class SpecExprExtensions
    {
        public static Expression<Func<T, bool>> Not<T>(this Expression<Func<T, bool>> one)
        {
            var candidateExpr = one.Parameters[0];
            var body = Expression.Not(one.Body);

            return Expression.Lambda<Func<T, bool>>(body, candidateExpr);
        }
}

  对于Not方法,我们只要获取它的参数表达式,再将它的Body外包一个Not表达式,便可以此构造一个新的表达式了。但And和Or方法实现不能像Not一样简单处理:

// 不能这么处理public static Expression<Func<T, bool>> And<T>(
    this Expression<Func<T, bool>> one, Expression<Func<T, bool>> another)
{
    var candidateExpr = one.Parameters[0];
    var body = Expression.And(one.Body, another.Body);
    return Expression.Lambda<Func<T, bool>>(body, candidateExpr);
}

  因为one和another两个表达式虽然都是同样的形式(Expression<Func<T, bool>>),但是它们的“参数”不是同一个对象。即one.Body和another.Body并没有公用一个ParameterExpression实例,于是无论采用哪个表达式的参数,在Expression.Lambda方法调用的时候,都会出现body中的某个参数对象并没有出现在参数列表中的错误。

  既然参数不一致,所以要实现And和Or方法,必须统一两个表达式树的参数。为了达到这个目标,我们可以利用ExpressionVisitor类来实现。这里定义一个派生于ExpressionVisitor的类。具体实现如下:

internal class ParameterReplacer : ExpressionVisitor
    {
        public ParameterReplacer(ParameterExpression paramExpr)
        {
            this.ParameterExpression = paramExpr;
        }

        public ParameterExpression ParameterExpression { get; private set; }

        public Expression Replace(Expression expr)
        {
            return this.Visit(expr);
        }

        protected override Expression VisitParameter(ParameterExpression p)
        {
            return this.ParameterExpression;
        }
    }

  Expressionvistor可以用于求值、变形等各种操作。它提供了遍历表达式树的标准方式,如果你直接继承这个类并调用Visit方法(如上面Replace方法的实现一样),那么最终返回的结果便是传入的Expresssion参数本身。但是,如果你覆盖任意一个方法,返回了与传入时不同的对象,那么最终的结果就是一个新的Expression对象。就如上面VisitParameter方法实现一样。它直接返回我们定义的ParameterExpression对象。

  通过上面分析,ParameterExpression类的作用是将一个表达式里的所有ParameterExpression替换成我们指定的新对象,这样就可以解决之前参数不一致的情况。所以我们And和Or方法的实现就是将两个表达式树参数替换成我们首先定义好的参数表达式。具体的实现方式如下所示:

public static class SpecExprExtensions
    {
        public static Expression<Func<T, bool>> Not<T>(this Expression<Func<T, bool>> one)
        {
            var candidateExpr = one.Parameters[0];
            var body = Expression.Not(one.Body);

            return Expression.Lambda<Func<T, bool>>(body, candidateExpr);
        }

        public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> one,
            Expression<Func<T, bool>> another)
        {
            // 首先定义好一个ParameterExpression
            var candidateExpr = Expression.Parameter(typeof (T), "candidate");
            var parameterReplacer = new ParameterReplacer(candidateExpr);

            // 将表达式树的参数统一替换成我们定义好的candidateExpr
            var left = parameterReplacer.Replace(one.Body);
            var right = parameterReplacer.Replace(another.Body);

            var body = Expression.And(left, right);

            return Expression.Lambda<Func<T, bool>>(body, candidateExpr);
        }

        public static Expression<Func<T, bool>> Or<T>(
            this Expression<Func<T, bool>> one, Expression<Func<T, bool>> another)
        {
            var candidateExpr = Expression.Parameter(typeof (T), "candidate");
            var parameterReplacer = new ParameterReplacer(candidateExpr);

            var left = parameterReplacer.Replace(one.Body);
            var right = parameterReplacer.Replace(another.Body);
            var body = Expression.Or(left, right);

            return Expression.Lambda<Func<T, bool>>(body, candidateExpr);
        }
    }

  到此,我们就完成了规约模式对Linq支持的轻量实现了。下面让我们看看上面轻量实现是如何调用的呢?具体调用代码如下:

 class Program
    {
        private static void Main(string[] args)
        {
            Demo1();
            Console.Read();
        }

        public static void Demo1()
        {
            var items = Enumerable.Range(-5, 10);
            Console.WriteLine("产生的数组为:{0}", string.Join(",  ", items.ToArray()));

            Expression<Func<int, bool>> f = i => i % 2 != 0;
            f = f.Not().And(i => i > 0);

            // 通过AsQueryable成IQueryable<int>,因为IQueryable<T>的Where方法的参数要求是表达式树
            foreach (var i in items.AsQueryable().Where(f))
            {
                Console.WriteLine(i);
            }
        }
    }

  其运行结果与前面的例子中的运行结果一样,一样成功返回了即是偶数又是正数的集合。

七、总结

  到这里,规约模式的实现就结束了,后期将会在网上书店的案例中引入规约模式,dax.net的Byteart Retail案例中规约模式的实现即包括了传统实现,也包括了对Linq支持的轻量实现。开始我认为传统实现是多余的,因为你已经有了规约模式的轻量实现了,何必又有传统实现呢?这不是包括两种实现吗?后面仔细想想,这样设计也有其存在的道理,因为对于一些逻辑复杂的规约实现,我们可以新建一个具体的规约类,但对于一些简单和仅使用一次的规约逻辑,就可以直接用表达式树来代替,就不需要单独为该段逻辑单独新建一个具体的规约类。这样的实现就如同,有了匿名方法和Lambda表达式,是不是委托就可以不需要了。显然不是的,所以我在我的网上书店案例中也将会引入这两种实现,让用户可以灵活选择这两种方式。在下一专题,我继续介绍一个前期准备的内容,即工作单元模式(Unit Of Work,即UOW)。

  本专题的所有源码下载:SpecificationPatternDemo.zip

时间: 2024-10-13 00:55:36

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

[.NET领域驱动设计实战系列]专题十一:.NET 领域驱动设计实战系列总结

一.引用 其实在去年本人已经看过很多关于领域驱动设计的书籍了,包括Microsoft .NET企业级应用框架设计.领域驱动设计C# 2008实现.领域驱动设计:软件核心复杂性应对之道.实现领域驱动设计和Asp.net 设计模式等书,但是去年的学习仅仅限制于看书,当时看下来感觉,领域驱动设计并没有那么难,并且感觉有些领域驱动设计的内容并没有好的,反而觉得有点华而不实的感觉,所以去年也就放弃了领域驱动设计系列的分享了,但是到今年,在博客园看到还是有很多人写领域驱动的文章,以及介绍了领域驱动设计相关的

[.NET领域驱动设计实战系列]专题一:前期准备之EF CodeFirst

一.前言 从去年已经接触领域驱动设计(Domain-Driven Design)了,当时就想自己搭建一个DDD框架,所以当时看了很多DDD方面的书,例如领域驱动模式与实战,领域驱动设计:软件核心复杂性应对之道和领域驱动设计C# 2008实现等书,由于当时只是看看而已,并没有在自己代码中进行实现,只是初步了解一些DDD分层的思想和一些基本概念,例如实体,聚合根.仓储等概念,今年有机会可以去试试面试一个架构岗位的时候,深受打击,当面试官问起是否在项目中使用过DDD思想来架构项目时,我说没有,只是了解

[.NET领域驱动设计实战系列]专题二:结合领域驱动设计的面向服务架构来搭建网上书店

一.前言 在前面专题一中,我已经介绍了我写这系列文章的初衷了.由于dax.net中的DDD框架和Byteart Retail案例并没有对其形成过程做一步步分析,而是把整个DDD的实现案例展现给我们,这对于一些刚刚接触领域驱动设计的朋友可能会非常迷茫,从而觉得领域驱动设计很难,很复杂,因为学习中要消化一个整个案例的知识,这样未免很多人消化不了就打退堂鼓,就不继续研究下去了,所以这样也不利于DDD的推广.然而本系列可以说是刚接触领域驱动设计朋友的福音,本系列将结合领域驱动设计的思想来一步步构建一个网

[.NET领域驱动设计实战系列]专题十:DDD扩展内容:全面剖析CQRS模式实现

一.引言 前面介绍的所有专题都是基于经典的领域驱动实现的,然而,领域驱动除了经典的实现外,还可以基于CQRS模式来进行实现.本专题将全面剖析如何基于CQRS模式(Command Query Responsibility Segregation,命令查询职责分离)来实现领域驱动设计. 二.CQRS是什么? 在介绍具体的实现之前,对于之前不了解CQRS的朋友来说,首先第一个问题应该是:什么是CQRS啊?你倒是详细介绍完CQRS后再介绍具体实现啊?既然大家会有这样的问题,所以本专题首先全面介绍下什么是

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

一.前言 在前面2篇博文中,我分别介绍了规约模式和工作单元模式,有了前面2篇博文的铺垫之后,下面就具体看看如何把这两种模式引入到之前的网上书店案例里. 二.规约模式的引入 在第三专题我们已经详细介绍了什么是规约模式,没看过的朋友首先去了解下.下面让我们一起看看如何在网上书店案例中引入规约模式.在网上书店案例中规约模式的实现兼容了2种模式的实现,兼容了传统和轻量的实现,包括传统模式的实现,主要是为了实现一些共有规约的重用,不然的话可能就要重复写这些表达式.下面让我们具体看看在该项目中的实现. 首先

[.NET领域驱动设计实战系列]专题七:DDD实践案例:引入事件驱动与中间件机制来实现后台管理功能

一.引言 在当前的电子商务平台中,用户下完订单之后,然后店家会在后台看到客户下的订单,然后店家可以对客户的订单进行发货操作.此时客户会在自己的订单状态看到店家已经发货.从上面的业务逻辑可以看出,当用户下完订单之后,店家或管理员可以对客户订单进行跟踪和操作.上一专题我们已经实现创建订单的功能,则接下来自然就是后台管理功能的实现了.所以在这一专题中将详细介绍如何在网上书店案例中实现后台管理功能. 二.后台管理中的权限管理的实现 后台管理中,首先需要实现的自然就是权限管理了,因为要进行商品管理等操作的

[.NET领域驱动设计实战系列]专题九:DDD案例:网上书店AOP和站点地图的实现

一.引言 在前面一专题介绍到,要让缓存生效还需要实现对AOP(面向切面编程)的支持.所以本专题将介绍了网上书店案例中AOP的实现.关于AOP的概念,大家可以参考文章:http://www.cnblogs.com/jin-yuan/p/3811077.html.这里我简单介绍下AOP:AOP可以理解为对方法进行截获,这样就可以在方法调用前或调用后插入需要的逻辑.例如可以在方法调用前,加入缓存查找逻辑等.这里缓存查找逻辑就在方法调用前被执行.通过对AOP的支持,每个方法就可以分为3部分了,方法调用前

[.NET领域驱动设计实战系列]专题八:DDD案例:网上书店分布式消息队列和分布式缓存的实现

一.引言 在上一专题中,商家发货和用户确认收货功能引入了消息队列来实现的,引入消息队列的好处可以保证消息的顺序处理,并且具有良好的可扩展性.但是上一专题消息队列是基于内存中队列对象来实现,这样实现有一个弊端,就是一旦服务重启或出现故障时,此时消息队列中的消息会丢失,并且也记录不了日志.所以就会出现,商家发货成功后,用户并没有收到邮件通知,并且也没有日志让我们发现是否发送了邮件通知.为了解决这个问题,就需要引入一种可恢复的消息队列.目前有很多开源的消息队列都支持可恢复的,例如TibcoEms.ne

NET 领域驱动设计实战系列总结

NET 领域驱动设计实战系列总结 一.引用 其实在去年本人已经看过很多关于领域驱动设计的书籍了,包括Microsoft .NET企业级应用框架设计.领域驱动设计C# 2008实现.领域驱动设计:软件核心复杂性应对之道.实现领域驱动设计和Asp.net 设计模式等书,但是去年的学习仅仅限制于看书,当时看下来感觉,领域驱动设计并没有那么难,并且感觉有些领域驱动设计的内容并没有好的,反而觉得有点华而不实的感觉,所以去年也就放弃了领域驱动设计系列的分享了,但是到今年,在博客园看到还是有很多人写领域驱动的