DDD(九,十)【基础设施层~续】【 领域层】

在之前写的DDD~基础设施层文章中,提到了UnitOfWork,它里面有一些方法,但经过项目证明,不应该有SaveIsExplicitSubmit,而这个工作单元只起到了数据上下文统一的作用,如A和B对象需要在同一个上下文中工作,这时,我们可以引用工作单元的概念,而对于保存和提交操作,还是应该在局部方法里完成的。

为了不去触发MSDTC,我会封装一个特殊的事务,来实现这个工作,而对于SQL2008来说,可以直接使用.net自己的TransactionScope实现,对于同一个数据库来说,它不会被提升为分布式事务,而SQL2008以下的版本,则需要使用占占封装的事务,代码如下:

    /// <summary>
    /// Author:zhang.zhanling
    /// 同步文章:http://www.cnblogs.com/lori/p/3455393.html
    /// 对TransactionScope,让它对同一个数据库不产生msdtc服务
    /// </summary>
    public class TransactionScopeNoMsdtc
    {
        /// <summary>
        /// 产生包裹事务
        /// 支持LINQ表提交,并同时返回提交结果到实体
        /// </summary>
        /// <param name="db">数据上下文,多个方法使用的上下文必须是同一个</param>
        /// <param name="isOutermost">是否为最外层,默认为false</param>
        /// <param name="action">处理代码块</param>
        public static void UsingNoMsdtc(DbContext db, bool isOutermost, Action action)
        {
            var objectContext = ((System.Data.Entity.Infrastructure.IObjectContextAdapter)db).ObjectContext;
            try
            {
                if (objectContext.Connection.State == System.Data.ConnectionState.Closed)
                    objectContext.Connection.Open();
                using (TransactionScope trans = new TransactionScope())
                {
                    try
                    {
                        action();
                        trans.Complete();
                    }
                    catch (Exception)
                    {
                        throw;
                    }
                    finally
                    {
                        trans.Dispose();
                    }
                }
            }
            catch (Exception)
            {
                throw;
            }
            finally
            {
                if (isOutermost)//如果是最外层事务,而将连接关闭
                {
                    objectContext.Connection.Close();
                }
            }
        }
        /// <summary>
        /// 产生包裹事务,它不是最外层的,如果是最外层的需要调用其它重载
        /// </summary>
        /// <param name="db">数据上下文,多个方法使用的上下文必须是同一个</param>
        /// <param name="action">处理代码块</param>
        public static void UsingNoMsdtc(DbContext db, Action action)
        {
            UsingNoMsdtc(db, false, action);
        }
    }

而最新的IUnitOfWork接口就变成了一个标识接口,代码如下

    /// <summary>
    /// 数据上下文标识接口,它对于业务层应该是公开的
    /// 它对于实现上下文的方法,它并不关心,可以是linq2sql,ef,ado.net,nhibernate,memory,nosql等
    /// </summary>
    public interface IUnitOfWork
    {
    }

我们看到,IUnitOfWork主要作用是标示各个仓储在同一个上下文当中,或者说,在一个工作单元之中,下面是项目中使用的代码,我们可以参考一下

    /// <summary>
    /// Point_Info仓储
    /// </summary>
    /// <remarks>create:cyr_(Ben)_20131128</remarks>
    public class Point_InfoRepository : TsingDa_NewLearningBarRepository<Point_Info>
    {
        #region Constructors
        public Point_InfoRepository() : this(null) { }
        public Point_InfoRepository(IUnitOfWork db) : base(db) { }
        #endregion
    }

我们可以看到,上面的仓储提供了两个构造方法的实现,默认为空参,还有一个可以为它传入一个IUnitOfWork工作单元,本仓储也支持IoC的构造方法注入。


领域层

再论DomainInfrastructure

在面向领域的设计中,领域层(Domain)实现上是位于最底层的,其它层有对它的引用,包括基础设施层(Infrastructure)也是去引用领域层的,我认为,这是对的,事实上,在Domain中会规定如何去进行数据持久化的操作,包括方法名,方法签名等等,而采用哪种架构去实现这种持久化的方法则是Infrastructure层需要做的,这种设计绝对是把领域,业务放在第一位的,完全符合Eric 的DDD

Domain.Core Layer & Domain Layer

我们在进行软件设计时,一个习惯就是把仅供代码抽象出来,这是对的,也是符合标准的,对于domain层我们会把与具体领域无关的代码抽象成一个domain.core,这个项目位于整个DDD解决方案的最底层,而具体的domain则会引入它,看一下我们的DDD中的domain
在这里插入图片描述
domain.core中有数据持久化的规范,规约实现Specification及一些公用的功能代码。
在这里插入图片描述
具体的domain项目中包括三部分内容,具体业务规范(如产品业务规范Products),数据模型POCO实体,用于网络传输的DTO实体(这部分可以单拿出来,本例直接放在了实体层),我们先来看一下products这个业务规范:

IProductRepository:它是产品实体的数据持久化规范,Infrastructure层会去实现它,这里不会关心Infrastructure是采用linq to sql还是ef,我只规定要做什么,至少怎么去做,由Infrastructure自己去决定!

  public interface IProductRepository : IExtensionRepository<Product>
{
    /// <summary>
    /// 获取产品列表
    /// </summary>
    /// <returns></returns>
    IQueryable<Product> GetProduct();

    /// <summary>
    /// 建立产品
    /// </summary>
    void AddProduct(Product entity);

    /// <summary>
    /// 修改产品
    /// </summary>
    void ModifyProduct(System.Linq.Expressions.Expression<Action<Product>> entity);
}

ProductSpecification:这是一个与业务息息相关的规约类,它会根据具体业务去设计每一个业务的具体规约

  /// <summary>
/// 通过用户信息得到他的订单列表
/// </summary>
public class ProductSpecification : Specification<Product>
{
    string _productName = default(string);
    public ProductSpecification(string productName)
    {
        _productName = productName;
    }
    public override Expression<Func<Product, bool>> SatisfiedBy()
    {
        Specification<Product> spec = new TrueSpecification<Product>();
        if (string.IsNullOrWhiteSpace(_productName))
            spec &= new DirectSpecification<Product>(o => o.ProductName.Contains(_productName));
        return spec.SatisfiedBy();
    }
}

网络传输对象DTO,它是在进行WEB通讯时为了减少网站负载,而提出的新的实体层,将与本服务有关的实体属性提出,形成一个新的实体,这在SOA服务中用的比较多。

[DataContract]
    public class ProductDTO
    {
        [DataMember]
        [DisplayName("商品ID")]
        public int ProductID { get; set; }
        [DataMember]
        [DisplayName("商品名称")]
        public string ProductName { get; set; }
        [DataMember]
        [DisplayName("建立日期")]
        public System.DateTime CreateDate { get; set; }
        [DataMember]
        public int SaleCount { get; set; }
        [DataMember]
        public Nullable<int> ClickCount { get; set; }
        [DataMember]
        [DisplayName("产品描述")]
        public string Info { get; set; }
        [DataMember]
        public int UserID { get; set; }
        [DataMember]
        [DisplayName("销售价格")]
        public decimal SalePrice { get; set; }
        [DataMember]
        [DisplayName("折扣")]
        public int Discount { get; set; }
    }

数据模型EDMX,这种模块我们称为entity frameworks模型,也叫EF模型,它可以把物理数据库映射到EDMX文件中,它是以XML形式保存的,而我们的POCO简单数据实体也可以由工具自动生成,这些实体与原始数据表一一对应。ef poco 模型实体采用partial class方便开发人员以后对实体类进行扩展。

public partial class Product
    {
        public Product()
        {
            this.ProductDetail = new HashSet<ProductDetail>();
        }
    
        public int ProductID { get; set; }
        public string ProductName { get; set; }
        public System.DateTime CreateDate { get; set; }
        public int SaleCount { get; set; }
        public Nullable<int> ClickCount { get; set; }
        public string Info { get; set; }
        public int UserID { get; set; }
        public decimal SalePrice { get; set; }
        public int Discount { get; set; }
        public System.DateTime UpdateDate { get; set; }
    
        public virtual User_Info User_Info { get; set; }
        public virtual ICollection<ProductDetail> ProductDetail { get; set; }
    }

最后,我们看一个domainInfrastructure之间的数据通信,它们一般使用IOC容器进行实现,像AutofacUnity都是不错的选择,而unity是一个强大的工具集,它不仅包括IOC的功能,而且还提供了对AOP的实现!下次我们将着重介绍一下UnityDDD中的使用,介请期待!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值