简体   繁体   English

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

[英]DDD Architecture for ASP.NET MVC2 Project

I am trying to use Domain Driven Development (DDD) for my new ASP.NET MVC2 project with Entity Framework 4. After doing some research I came up with the following layer conventions with each layer in its own class project: 我正在尝试使用域驱动开发(DDD)来实现我的新ASP.NET MVC2项目和实体框架4.在做了一些研究之后,我在自己的类项目中为每个层提出了以下层约定:

MyCompany.Domain 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
    }

MyCompany.Repositories MyCompany.Repositories

public class UserRepository : IRepository<User>
{
    // Repository interface implementations
}

MyCompany.Web --> This is the MVC2 Project MyCompany.Web - >这是MVC2项目

Currently my Repositories layer holds a reference to the Domain layer. 目前,我的Repositories层包含对Domain层的引用。 From my understanding injecting a UserRepository to the UserService class works very well with unit testing as we can pass in fake user repositories. 根据我的理解,将UserRepository注入UserService类可以很好地进行单元测试,因为我们可以传入虚假的用户存储库。 So with this architecture it looks like my Web project needs to have a references to both my Domain and Repositories layers. 因此,使用此体系结构,我的Web项目看起来需要引用我的域和存储库层。 But is this a valid? 但这是有效的吗? Because historically the presentation layer only had a reference to the Business Logic layer. 因为历史上表示层只有对业务逻辑层的引用。

Just some notes on @RPM1984 answer... 关于@ RPM1984的一些注释答案......

However in your question, you've got IRepository in the domain project, i would put this in your Repositories assembly. 但是在你的问题中,你在域项目中有了IRepository,我会把它放在你的Repositories程序集中。

While it does not matter that much where You put things physically, it's important to remember that abstractions of repositories are part of domain. 虽然在物理上放置物品并不重要,但重要的是要记住存储库的抽象是域的一部分。

We also have a Service layer mediating between the UI (Controllers) and the Repository. 我们还有一个在UI(控制器)和存储库之间进行调解的服务层。 This allows a central location to put logic which does not belong in the Repository - things like Paging and Validation. 这允许中央位置放置不属于存储库的逻辑 - 诸如分页和验证之类的东西。

I believe that it's not good idea to create service just for paging and validation. 我认为,仅为分页和验证创建服务并不是一个好主意。 Even worse if it's created artificially - just because 'everything goes through services' and 'that way You don't need to reference repositories from UI'. 更糟糕的是,如果它是人工创建的 - 只是因为'一切都通过服务'和'那样你不需要从UI引用存储库'。 Application services should be avoided when possible. 应尽可能避免应用程序服务。

For input validation - there's already nice point for that out of the box. 对于输入验证 - 开箱即用已经很好了。 If You use asp.net mvc, consider following MVVM pattern and framework will provide good enough ways to validate Your input view models. 如果您使用asp.net mvc,请考虑以下MVVM模式和框架将提供足够好的方法来验证您的输入视图模型。

If You need interaction across multiple aggregate roots (which is a sign that You might be missing new aggregate root) - write domain service. 如果您需要跨多个聚合根进行交互(这表明您可能缺少新的聚合根) - 编写服务。

You seem to be on the right track, but since it seems to want to go "DDD-All-The-Way", have you considered implementing the Unit of Work pattern to manage multiple repositories sharing the same context? 您似乎走在正确的轨道上,但由于它似乎想要“DDD-All-The-Way”,您是否考虑过实施工作单元模式来管理共享相同上下文的多个存储库?

I think unit of work should be avoided too. 我认为应该避免工作单位。 Here's why : 原因如下:

Another example, UoW is really a infrastructure concern why would you bring that into the domain?. 另一个例子,UoW实际上是一个基础设施问题,为什么你会把它带到域? If you have your aggregate boundaries right, you don't need a unit of work. 如果您的聚合边界正确,则不需要工作单元。


Repositories do not belong in the domain. 存储库不属于域。 Repositories are about persistence (ie infrastructure), domains are about business. 存储库是关于持久性(即基础架构),域是关于业务的。

Repositories got kind a mixed responsibility. 存储库负有混合责任。 From one side - business don't care how and where it will persist data. 从一方面来看 - 企业并不关心数据的持久性和方式。 From other - knowledge that customers can be found by their shopping cart content is (oversimplified example). 来自其他人 - 通过购物车内容可以找到客户的知识(简化示例)。 Hence - I'm arguing that only abstractions should be part of domain. 因此 - 我认为只有抽象应该是域的一部分。 Additionally - I'm against direct usage of repositories from domain model cause it should be persistence ignorant. 另外 - 我反对从域模型直接使用存储库,因为它应该是持久性无知的。

Service Layer allows a central point for 'business validation'. 服务层允许“业务验证”的中心点。

Application services basically are just facades . 应用服务基本上只是外观 And every facade is bad if complexity it adds overweights problems it solves. 如果复杂性增加了它所解决的超重问题,那么每个外观都是糟糕的。

Here's a bad facade: 这是一个糟糕的外观:

public int incrementInteger(int val){
  return val++;
}

Application services must not contain business rules. 应用程序服务不得包含业务规则。 That's the point of domain driven design - ubiquitous language and isolated code that reflects business as clear and simply as possible. 这就是域驱动设计的重点 - 无处不在的语言和孤立的代码,它们尽可能清晰,简单地反映业务。

MVC is good for simple validation, but not for complex business rules (think specification pattern). MVC适用于简单验证,但不适用于复杂的业务规则(思考规范模式)。

And that's what it should be used for. 这就是它应该用于的东西。 Eg - to check if posted value can be parsed as datetime which is supposed to be passed as argument to domain. 例如 - 检查发布的值是否可以解析为日期时间,该日期时间应该作为参数传递给域。 I call it UI validation. 我称之为UI验证。 There are many of them . 他们中有很多人

RE UoW - im intrugued by that sentence, but can you elaborate? RE UoW - 我被这句话打扰了,但你能详细说明吗? UoW IS indeed an insfrastructure concern, but again this is in my repositories/data tier, not my domain. UoW确实是一个基础设施问题,但这又是我的存储库/数据层,而不是我的域。 All my domain has is business objects/specifications. 我的所有域名都是业务对象/规范。

Unit of work pattern encourages us to loosen aggregate root boundaries. 工作单元模式鼓励我们放松聚合根边界。 Basically - it allows us to run transactions over multiple roots. 基本上 - 它允许我们在多个根目录上运行事务。 Despite that it sits outside domain, it still does implicit impact on domain and modeling decisions we make. 尽管它位于域之外,但它仍然会对我们制定的域和建模决策产生隐含影响。 Often enough it leads back to so called anemic domain model. 通常它会导致所谓的贫血领域模型。

One more thing: layers != tiers . 还有一件事: 图层!=层


The other point i'd make is DDD is a guideline, not a be-all-and-end-all. 我要做的另一点是DDD是一个指导方针,而不是一个全能的方法。

Yeah, but that shouldn't be used as an excuse. 是的,但这不应该被用作借口。 :) :)

The other reason for our service layer is that we have an Web API. 我们服务层的另一个原因是我们有一个Web API。 We do NOT want our Web API calling into our repositories, we want a nice fluent interface for which both the Web App and API can call through. 我们不希望我们的Web API调用我们的存储库,我们需要一个很好的流畅的界面,Web App和API都可以调用它。

If there's nothing more than retrieving root and calling it's method - service just to wrap that is not necessary. 如果没有什么比检索root并调用它的方法 - 只是为了包装而没有必要。 Therefore - I suspect that Your real issue is lack of this isolation, hence - there's need to orchestrate interaction between roots. 因此 - 我怀疑你真正的问题是缺乏这种孤立,因此 - 需要协调根之间的互动。

Here You can see some details of my current approach. 在这里您可以看到我当前方法的一些细节。

Another BIG reason for our service layer, is our Repositories return IQueryable, so they have no logic whatsoever. 我们的服务层的另一个重要原因是我们的存储库返回IQueryable,因此它们没有任何逻辑。 Our Service Layer projects the linq expressions, into concretes 我们的服务层将linq表达式投射到混凝土中

That does not sound right. 这听起来不对。 The same problem - lack of isolation. 同样的问题 - 缺乏隔离。

In this case - repositories do not abstract persistence enough. 在这种情况下 - 存储库不足以抽象持久性。 They should have logic - one which is persistence related. 他们应该有逻辑 - 一个与持久性相关的逻辑。 Knowledge how to store and retrieve data. 了解如何存储和检索数据。 Delegating that "somewhere outside" ruins whole point of repositories and all what will be left - an extension point to mock out data access for testing (which ain't bad thing). 委托“外面某处”会破坏存储库的整个点以及剩下的所有内容 - 一个模拟数据访问以进行测试的扩展点(这不是坏事)。

Another thing - if repositories return raw IQueryable , that automatically ties service layer with unknown (!) LINQ provider. 另一件事 - 如果存储库返回原始IQueryable ,它会自动将服务层与未知(!)LINQ提供程序联系起来。

And less bad thing (You might not even need that) - because of high coupling , it might be quite hard to switch persistence to another technology. 并且不那么糟糕(你甚至可能不需要) - 由于高耦合 ,将持久性转换为另一种技术可能非常困难。

Don't forget that we are talking about technical concerns. 不要忘记我们正在谈论技术问题。 These things got nothing to do with design itself, they just make it possible. 这些东西与设计本身无关,它们只是让它成为可能。


If you don't use a service layer, how would you (for example) retrieve a list of orders for a product? 如果您不使用服务层,您将如何(例如)检索产品的订单列表? You would need a method in your Repository called "GetOrdersForProduct" - which i do not think is good design. 您需要一个名为“GetOrdersForProduct”的存储库中的方法 - 我认为这不是一个好的设计。 Your Repository interface becomes huge, impossible to maintain. 您的存储库界面变得庞大,无法维护。 We use a very generic Repository (Find, Add, Remove, etc). 我们使用非常通用的存储库(查找,添加,删除等)。 These methods work off the object set in our model. 这些方法可以解决模型中设置的对象。 The versatility/querying power is brought forward to the service layer 多功能/查询功率被提供给服务层

This one is tricky. 这个很棘手。 I'll just leave good reference . 我只想留下好的参考

With unknown provider i mean - You can't really now what's underneath from service layer. 对于未知的提供者,我的意思是 - 你现在不能真正从服务层下面得到什么。 It might be Linq to objects, it might be Linq to xml, Linq to sql, NHIbernate.Linq and bunch of other provider implementations. 它可能是Linq to objects,它可能是Linq to xml,Linq to sql,NHIbernate.Linq以及其他一些提供程序实现。 Bad thing is - You can't know what's supported. 不好的是 - 你不知道支持什么。

This will run just fine if it's Linq to objects and will fail if it needs to be translated to sql: 如果它是对象的Linq,这将运行正常,如果需要转换为sql将失败:

customers.Where(c=>{var pickItUp=c.IsWhatever; return pickItUp;});

This is very valid, and in fact very similar to the setup we have. 这非常有效,实际上与我们的设置非常相似。

However in your question, you've got IRepository in the domain project, i would put this in your Repositories assembly. 但是在你的问题中,你在域项目中有了IRepository ,我会把它放在你的Repositories程序集中。

Your Domain layer should have your domain entities and business logic. 您的层应具有您的域实体和业务逻辑。 Your Repository layer should have the generic repository interface, and concrete implementations of this, one for each aggregate root. 您的存储库层应该具有通用存储库接口,以及每个聚合根的一个具体实现。

We also have a Service layer mediating between the UI (Controllers) and the Repository. 我们还有一个在UI(控制器)和存储库之间进行调解的服务层。 This allows a central location to put logic which does not belong in the Repository - things like Paging and Validation. 这允许中央位置放置不属于存储库的逻辑 - 诸如分页和验证之类的东西。

This way, the UI does not reference the Repository, only the Service Layer. 这样,UI不引用存储库,仅引用服务层。

We also use DI to inject the EntityFrameworkRepository into our Service Layer, and a MockRepository into our test project. 我们还使用DI将EntityFrameworkRepository注入我们的服务层,并将MockRepository注入我们的测试项目。

You seem to be on the right track, but since it seems to want to go "DDD-All-The-Way", have you considered implementing the Unit of Work pattern to manage multiple repositories sharing the same context? 您似乎走在正确的轨道上,但由于它似乎想要“DDD-All-The-Way”,您是否考虑过实施工作单元模式来管理共享相同上下文的多个存储库?

You may want to explore the way the Sharp Architecture framework is tackling this problem, as it looks like you're basically re-implementing the same ideas. 您可能想要探索Sharp Architecture框架解决此问题的方式,因为看起来您基本上是在重新实现相同的想法。 The Northwind application tutorial has some nice discussion on these concepts. Northwind应用程序教程对这些概念进行了一些很好的讨论。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM