DDD理论学习系列(12)– 仓储

DDD理论学习系列——案例和目录


1. 引言

DDD中Repository此单词,主要出有限种植翻译:资源库仓储,本文取仓储之译。

说交囤,我们必然就是悟出了仓库,仓库一般用来存放在货物,而储藏室一般由仓库管理员来治本。当工厂生产了一如既往批货物经常,只需要提交仓库管理员即可,他背货物之积聚;当需要发货的时,仓库管理员负责从仓库中捡货进行商品出库处理。当得库存盘点时,仓库管理员负责把关货物状态和库存。换句话说,仓库管理员负责了货之出入库管理。通过储藏室管理员是角色,保证了仓库和厂的独立性,工厂只待背生产即可,而至于商品如何存放工厂无需关注。

若果我们如果讲的贮存就仿佛于仓库管理员,只不过它承受的不再是商品之保管,而是聚合的管理,仓储介于领域模型和数据模型之间,主要用以汇的持久化和查找。它隔离了世界模型与数据模型,以便我们关注为世界模型如果非需要考虑怎样开展持久化。

2. DDD中之贮存

2.1. 仓储的汇特性

储存代表一个会合的成团,其作为与.Net集合一样,仓储用来囤积和去聚合,但与此同时提供针对性聚合的显式查询和集中。

2.2. 存储及数访问层的界别

  1. 积存限定了不得不通过聚合根来持久化和搜索领域对象,以确保有改变和无变性由汇处理。
  2. 储存通过隐形聚合持久化和摸索的平底技术实现世界层的的持久化无关性(即世界层不需理解怎么样持久化领域对象)。
  3. 储存在数据模型和领域模型定义了一个境界。

2.3. 囤举例

脚我们率先来拘禁一个概括仓储的概念:

namespace DomainModel
{
 public interface ICustomerRepository
 {
 Customer FindBy(Guid id);
 void Add(Customer customer);
 void Remove(Customer customer);
 }
}

习以为常来说,仓储由应用服务层调用。仓储定义应用服务执行工作用例时用之有着的数码看方法。而仓储的实现普通位于基础架构层,由持久化框架来支撑。以下的存储实现是依靠ORM框架Nhibernate的ISession接口,它装一个之网关角色,负责领域模型与数据模型的投射。

namespace Infrastructure.Persistence {
    public class CustomerRepository : ICustomerRepository {
        private ISession _session;
        public CustomerRepository (ISession session) {
            _session = session;
        }
        public IEnumerable<Customer> FindBy (Guid id)
            return _session.Load<Order> (id);
        }

        public void Add (Customer customer) {
            _session.Save (customer);
        }

        public void Remove (Customer customer) {
            _session.Delete (customer);
        }
    }
}

自打者我们得以望,将世界模型的持久化转移至基础设备层,隐藏了世界模型的艺复杂,从而使世界对象会专注于事情概念和逻辑。

2.4. 囤的误解

储存为存诸多误解,许多总人口觉着该是无必要的抽象。当使用叫简单的天地模型时,可以直接采用持久化框架来拓展数量看。然而当对复杂的世界模型进行建模时,仓储是范的扩大,它表明聚合检索的作用,可以针对天地模型进行有义之读写,而不是一个技艺框架。

为有多人数认为仓储是同种反模式,因为该隐藏了基础持久化框架的效果。而刚刚这正是仓储的要领。基础持久化框架提供了开的接口用于对数据模型的检索和改,而仓储通过应用定义的命名查询艺术来界定对聚集的拜会。通过使查询显式化,就更便于调整查询,且再度要的凡存储明确了询问的用意,便于领域专家理解。举个例子:我们以仓储中定义了一个计GetAllActiveUsers()与sql语句select * from users where isactive = 1var users =db.Users.Where(u=>u.IsActive ==1)对照,很显著仓储的计命名就会为咱们理解了询问的企图:查询有处于Active状态的用户。除此之外查询,仓储仅暴露必要的持久化方法而不是供有的CURD方法。

2.5. 囤的中心

仓储的要义并无是要是代码更便于测试,也不是为便利切换底层的持久化存储方。当然,在某种程度上,这为真正是储存所带来的利好。储存的要义是维持您的园地模型和技巧持久化框架的独立性,这样您的圈子模型可以凝集来自底层持久化技术的熏陶。如果没仓储这同样叠,你的持久化基础设备或者会见泄露及世界模型中,并影响世界模型完整性和终极一致性。

3. 天地模型 VS 数据模型

假若选择涉及项目数据库作为持久化存储,我们得靠ORM框架来落实世界模型和数据模型之间的投和持久化操作。

假如ORM又是啊为?

以文章开始中的例子,如果存储对承诺仓库管理员的角色,那ORM就相当给仓库机器人,而仓库就一定给数据库。为了有利于不同商品的分类存放,对仓进行分区,分区就一定给数据表。当公司接受一笔订单做发货处理常,销售员将发货通知单告知仓库管理员,仓库管理员再分配ORM机器人进行捡货。很明朗,ORM机器人必须能够分辨发货通知单,将发货通知只是吃之货对诺到仓库中存储的货品。这里面发货通知只是就相当给天地模型,而仓库中贮存的商品虽属数据模型。

深信不疑基于上面的比方,我们对ORM有矣主导的认识。ORM,全称是Object
Relational
Mapping,对象关系映射。ORM的前提是,将对象的性能映射到数据库字段,将目标之间的援映射到数量库表的涉及。换句话说,ORM负责将代码中定义之目标及涉映射到数据库的说明结构面临错过,并以进行数量看时还用说明数据映射到代码中定义之靶子,借助ORM我们无欲去手动写SQL语句就可成功数据的增删改查。ORM就抽象了关系数据模型,它就是以面向对象的方来代表数据模型,以造福我们以代码中轻松地拍卖数量。

下面我们来探讨一下数据模型与天地模型的异议。关系数据库中之数据模型,它由表和排成,它只是简短的存储结构,用于保存领域模型有时间点的状态。数据模型可以分散在几乎单说明还几只数据库中。此外,可以利用多种形式的持久化存储,例如文件、web服务器、关系数据库或NoSQL。领域模型是本着问题域的肤浅,具有丰富的言语及作为,由实体和价值对象成。对于有领域模型,可能跟数据模型相似,甚至同一,但每当概念上它是殊不同之。ORM和世界模型无关。仓储的意就是是拿世界模型与数据模型分开,而无是被它模糊化一个模型。ORM不是储存,但是仓储可以应用ORM来持久化领域对象的状态。

图片 1

如果你的世界模型与君的数据模型类似,ORM可以直接照射领域模型到数码存储,否则,则用对ORM进行额外的照耀配置。

4. 囤积的概念和贯彻

点也论及了,我们一般在领域层定义仓储接口,在基础设备层实现仓储,以切断领域模型和数据模型。

4.1. 仓储方法要明确

存储是极达成是圈子模型与持久化存储之间明显的契约,仓储定义的接口方法不仅是CURD方法。它是世界模型的扩展,并盖领域专家所知晓的术语编写。仓储接口的定义应该根据应用程序的用例需求来创造,而未是从接近CURD的多寡访问角度来构建。

我们来拘禁一样截代码:

namespace DomainModel {
    public interface ICustomerRepository {
        Customer FindBy (Guid id);
        IEnumerable<Customer> FindAllThatMatch (Query query);
        IEnumerable<Customer> FindAllThatMatch (String hql);
        void Add (Customer customer);
    }
}

上述仓储定义了一个FindAllThatMatch方法为支持客户端以任何方式查询领域对象。这个方法的统筹思想无可置否,灵活且可扩展,但是她并没有强烈的标志查询的打算,我们尽管去了针对查询的控制。为了真正了解什么以这些办法,开发人员需要跟相关调用堆栈,才能够知悉方法的意,更别说出现性能问题经常如何入手优化了。因为仓储定义之接口方法过于厚实泛且不现实,它模糊了世界的底概念,所以定义这样的一个接口方法是虚幻的。

咱们好如下改造:

namespace DomainModel {
    public interface ICustomerRepository {
        Customer FindBy (Guid id);
        IEnumerable<Customer> FindAllThatAreDeactivated ();
        IEnumerable<Customer> FindAllThatAreOverAllowedCredit ();
        void Add (Customer customer);
    }
}

由此上述改造,我们通过措施的命名来明确询问的来意,符合通用语言的正规化。

4.2. 泛型仓储

在实践中我们或会见意识,为各个一个会师定义一个储存会招致更代码,因为多数之数据操作都是类似之。为了代码用,泛型仓储就应时而生。

泛型仓储举例:

namespace DomainModel {
    public interface IRepository<T> where T : EntityBase {
        T GetById (int id);
        IEnumerable<T> List ();
        IEnumerable<T> List (Expression<Func<T, bool>> predicate);
        void Add (T entity);
        void Delete (T entity);
        void Edit (T entity);
    }

    public abstract class EntityBase {
        public int Id { get; protected set; }
    }
}

泛型仓储实现:

namespace Infrastructure.Persistence {
    public class Repository<T> : IRepository<T> where T : EntityBase {
        private readonly ApplicationDbContext _dbContext;
        public Repository (ApplicationDbContext dbContext) {
            _dbContext = dbContext;
        }
        public virtual T GetById (int id) {
            return _dbContext.Set<T> ().Find (id);
        }

        public virtual IEnumerable<T> List () {
            return _dbContext.Set<T> ().AsEnumerable ();
        }

        public virtual IEnumerable<T> List (Expression<Func<T, bool>> predicate) {
            return _dbContext.Set<T> ()
                .Where (predicate)
                .AsEnumerable ();
        }

        public void Insert (T entity) {
            _dbContext.Set<T> ().Add (entity);
            _dbContext.SaveChanges ();
        }

        public void Update (T entity) {
            _dbContext.Entry (entity).State = EntityState.Modified;
            _dbContext.SaveChanges ();
        }

        public void Delete (T entity) {
            _dbContext.Set<T> ().Remove (entity);
            _dbContext.SaveChanges ();
        }
    }
}

透过定义泛型仓储以及默认的贯彻,很死程度上进行了代码用。但是,尝试将泛型仓储应用拥有存储并无是一个好之主见。对于简易的聚合我们好直接下泛型仓储来简化代码。但对此复杂的集聚,泛型仓储可能就是会见无绝适合,如果因泛型仓储的办法进行数据访问,就见面搅乱对聚集的访问意图。

对于复杂的汇,我们可以再次定义:

namespace DomainModel {
    public interface ICustomerRepository {
        Customer FindBy (Guid id);
        IEnumerable<Customer> FindAllThatAreDeactivated ();
        void Add (Customer customer);
    }
}

于促成时,我们得引用泛型仓储来避免代码重复。

namespace Infrastructure.Persistence {
    public class CustomerRepository : ICustomerRepository {
        private IRepository<Customer> _customersRepository;
        public Customers (IRepository<Customer> customersRepository) {
            _customersRepository = customersRepository;
        }
        // ....
        public IEnumerable<Customer> FindAllThatAreDeactivated () {
            _customersRepository.List(c => c.IsActive == false);
        }
        public void Add (Customer customer) {
            _customersRepository.Add (customer);
        }
    }
}

由此这种方式,我们便明确了查询了意图,又简化了代码。

4.3. IQueryable Vs IEnumerable

以概念仓储方法的返值经常,我们或会见较疑惑,是应一直回到数据(IEnumerable)还是回查询(IQueryable)以便进行更的细化查询?返回IEnumerable会较安全,但IQueryable供了重好之灵活性。事实上,如果利用IQueryable当返回值,我们一味提供相同种读取数据的法即可开展各种查询。
但这种方法尽管会见引入一个问题,就是事情逻辑会渗透及应用层中错过,并出现大量再。比如,在实体中我们一般采取IsActiveIsDeleted性能来代表软删除,而设实体中的某条数据被去除,那么UI中基本不会见再度显就长长的数,那对于实体的询问都亟待包含类似Where(c=> c.IsActive)的linq表达式。对于这种问题,我们最为好当存储中的法中,比如List()或者ListActive()做默认处理,而不是在应用服务层每次去指定询问条件。
不过实际是回
IQueryable还是IEnumerable每个人之见不同,具体但参考Repository 返回
IQueryable?还是
IEnumerable?。

5. 事务管理和工作单元

东西管理重点是应用服务层的关注点。然而,因为仓储和东西管理紧密有关的。仓储就关心单一聚合的治本,而一个事务用例可能会见波及到多之集聚。

东西管理由UOW(Unit of
Work)处理。UOW模式的企图是在工作用例的操作着跟踪聚合的享有变更。一旦有了反,UOW就下工作来协调持久化存储。为了保数据的完整性,如果提交数据失败,则会回滚所有变更,以保证数量保持有效状态。

如若关于UOW又是一个扑朔迷离的话题,我们后续还出口。

6. 储存的反模式(注意事项)

  1. 绝不支持即查询(ad hoc query)
    存储不应有开放扩展,不要为支持多种形式的查询,定义比较常见的询问方式,它不光不能够明显表达仓储查询的图,更可能会见造成查询性能。
  2. 推加载是平种植设计臭味
    聚应围绕不变性构建,并包含有必要的属性去支持不变性。
    因此,当加载聚合时,要么加载所有,要么一个吧无加载。
    如果您来一个关系数据库并且正在使ORM作为数据模型,那么您可能能够延缓加载一些天地对象属性,这样就算足以推加载不待之集纳部分。但是,这样做的题目是,如果你不得不有加载聚合,可能会见造成你的会师边界错误。

  3. 甭使聚合来兑现报表需求
    报表可能会见提到到差不多独品类的集结,而仓储是处理单一聚合的。另外仓储是依据事务的,可能会见招报表的习性问题。

  4. 总结

  5. 存储当做世界模型与数据模型的中介,它肩负映射领域模型到持久化存储。

  6. 积存实现了晶莹剔透持久化,即世界层不待关注世界对象如何持久化。
  7. 储存是一个契约,而不是多少访问层。它鲜明标明聚合所必不可少的数据操作。
  8. ORM框架不是储存。仓储是同样栽架构模式。ORM用来坐面向对象的章程来代表数据模型。仓储使用ORM来协调领域模型和数据模型。
  9. 存储适用于所有丰富领域模型的鄂上下文。对于没有复杂工作逻辑的简练限界上下文,直接使用持久化框架即可。
  10. 动UOW进行事务管理。UOW负责跟踪对象的状态,仓储在UOW协调的政工中开展实际的持久化工作。
  11. 囤用于管理单个聚合,它不应控制工作。

参考资料:
世界让设计(DDD)的实践经验分享的持久化透明
Repository Pattern–A data persistence
abstraction
领域让设计(DDD)的实践经验分享的ORM的琢磨

网站地图xml地图