ASP.NET MVC2项目的DDD体系结构

Kum*_*mar 3 c# asp.net domain-driven-design entity-framework-4 asp.net-mvc-2

我正在尝试使用域驱动开发(DDD)来实现我的新ASP.NET MVC2项目和实体框架4.在做了一些研究之后,我在自己的类项目中为每个层提出了以下层约定:

MyCompany.Domain

     public class User
    {
        //Contains all the properties for the user entity
    }

    public interface IRepository<T> where T : class
    {
        IQueryable<T> GetQuery();
        IQueryable<T> GetAll();
        IQueryable<T> Find(Func<T, bool> condition);
        T Single(Func<T, bool> condition);
        T First(Func<T, bool> condition);
        T GetByID(int id);
        void Delete(T entity);
        void Add(T entity);
        void Attach(T entity);
        void SaveChanges();
    }

  public interface IUserRepository: IRepository<User> {}

    public class UserService
    {
        private IUserRepository _userRepository;
        public UserService(IUserRepository userRepository)
        {
            _userRepository = userRepository;
        }
    // This class will hold all the methods related to the User entity
    }
Run Code Online (Sandbox Code Playgroud)

MyCompany.Repositories

public class UserRepository : IRepository<User>
{
    // Repository interface implementations
}
Run Code Online (Sandbox Code Playgroud)

MyCompany.Web - >这是MVC2项目

目前,我的Repositories层包含对Domain层的引用.根据我的理解,将UserRepository注入UserService类可以很好地进行单元测试,因为我们可以传入虚假的用户存储库.因此,使用此体系结构,我的Web项目看起来需要引用我的域和存储库层.但这是有效的吗?因为历史上表示层只有对业务逻辑层的引用.

Arn*_*psa 10

关于@ RPM1984的一些注释答案......

但是在你的问题中,你在域项目中有了IRepository,我会把它放在你的Repositories程序集中.

虽然在物理上放置物品并不重要,但重要的是要记住存储库的抽象是域的一部分.

我们还有一个在UI(控制器)和存储库之间进行调解的服务层.这允许中央位置放置不属于存储库的逻辑 - 诸如分页和验证之类的东西.

我认为,仅为分页和验证创建服务并不是一个好主意.更糟糕的是,如果它是人工创建的 - 只是因为'一切都通过服务'和'那样你不需要从UI引用存储库'.应尽可能避免应用程序服务.

对于输入验证 - 开箱即用已经很好了.如果您使用asp.net mvc,请考虑以下MVVM模式和框架将提供足够好的方法来验证您的输入视图模型.

如果您需要跨多个聚合根进行交互(这表明您可能缺少新的聚合根) - 编写服务.

您似乎走在正确的轨道上,但由于它似乎想要"DDD-All-The-Way",您是否考虑过实施工作单元模式来管理共享相同上下文的多个存储库?

我认为应该避免工作单位.原因如下:

另一个例子,UoW实际上是一个基础设施问题,为什么你会把它带到域?如果您的聚合边界正确,则不需要工作单元.


存储库不属于域.存储库是关于持久性(即基础架构),域是关于业务的.

存储库负有混合责任.从一方面来看 - 企业并不关心数据的持久性和方式.来自其他人 - 通过购物车内容可以找到客户的知识(简化示例).因此 - 我认为只有抽象应该是域的一部分.另外 - 我反对从域模型直接使用存储库,因为它应该是持久性无知的.

服务层允许"业务验证"的中心点.

应用服务基本上只是外观.如果复杂性增加了它所解决的超重问题,那么每个外观都是糟糕的.

这是一个糟糕的外观:

public int incrementInteger(int val){
  return val++;
}
Run Code Online (Sandbox Code Playgroud)

应用程序服务不得包含业务规则.这就是域驱动设计的重点 - 无处不在的语言和孤立的代码,它们尽可能清晰,简单地反映业务.

MVC适用于简单验证,但不适用于复杂的业务规则(思考规范模式).

这就是它应该用于的东西.例如 - 检查发布的值是否可以解析为日期时间,该日期时间应该作为参数传递给域.我称之为UI验证.他们中有很多人.

RE UoW - 我被这句话打扰了,但你能详细说明吗?UoW确实是一个基础设施问题,但这又是我的存储库/数据层,而不是我的域.我的所有域名都是业务对象/规范.

工作单元模式鼓励我们放松聚合根边界.基本上 - 它允许我们在多个根目录上运行事务.尽管它位于域之外,但它仍然会对我们制定的域和建模决策产生隐含影响.通常它会导致所谓的贫血领域模型.

还有一件事:图层!=层.


我要做的另一点是DDD是一个指导方针,而不是一个全能的方式.

是的,但这不应该被用作借口.:)

我们服务层的另一个原因是我们有一个Web API.我们不希望我们的Web API调用我们的存储库,我们需要一个很好的流畅的界面,Web App和API都可以调用它.

如果没有什么比检索root并调用它的方法 - 只是为了包装而不必要的服务.因此 - 我怀疑你真正的问题是缺乏这种孤立,因此 - 需要协调根之间的互动.

在这里您可以看到我当前方法的一些细节.

我们的服务层的另一个重要原因是我们的存储库返回IQueryable,因此它们没有任何逻辑.我们的服务层将linq表达式投射到混凝土中

这听起来不对.同样的问题 - 缺乏隔离.

在这种情况下 - 存储库不足以抽象持久性.他们应该有逻辑 - 一个与持久性相关的逻辑.了解如何存储和检索数据.委托"外面某处"会破坏存储库的整个点以及剩下的所有内容 - 一个模拟数据访问以进行测试的扩展点(这不是坏事).

另一件事 - 如果存储库返回raw IQueryable,它会自动将服务层与未知(!)LINQ提供程序绑定.

并且不那么糟糕(你甚至可能不需要) - 由于高耦合,将持久性转换为另一种技术可能非常困难.

不要忘记我们正在谈论技术问题.这些东西与设计本身无关,它们只是让它成为可能.


如果您不使用服务层,您将如何(例如)检索产品的订单列表?您需要一个名为"GetOrdersForProduct"的存储库中的方法 - 我认为这不是一个好的设计.您的存储库界面变得庞大,无法维护.我们使用非常通用的存储库(查找,添加,删除等).这些方法可以解决模型中设置的对象.多功能/查询功率被提供给服务层

这个很棘手.我只想留下好的参考.

对于未知的提供者,我的意思是 - 你现在不能真正从服务层下面得到什么.它可能是Linq to objects,它可能是Linq to xml,Linq to sql,NHIbernate.Linq以及其他一些提供程序实现.不好的是 - 你不知道支持什么.

如果它是对象的Linq,这将运行正常,如果需要转换为sql将失败:

customers.Where(c=>{var pickItUp=c.IsWhatever; return pickItUp;});
Run Code Online (Sandbox Code Playgroud)


RPM*_*984 8

这非常有效,实际上与我们的设置非常相似.

但是在你的问题中,你已经进入IRepository了域项目,我会把它放在你的Repositories程序集中.

您的层应具有您的域实体和业务逻辑.您的存储库层应该具有通用存储库接口,以及每个聚合根的一个具体实现.

我们还有一个在UI(控制器)和存储库之间进行调解的服务层.这允许中央位置放置不属于存储库的逻辑 - 诸如分页和验证之类的东西.

这样,UI不引用存储库,仅引用服务层.

我们还使用DI将EntityFrameworkRepository注入我们的服务层,并将MockRepository注入我们的测试项目.

您似乎走在正确的轨道上,但由于它似乎想要"DDD-All-The-Way",您是否考虑过实施工作单元模式来管理共享相同上下文的多个存储库?