域驱动设计和聚合

时间:2012-02-24 18:17:57

标签: c# asp.net-mvc-3 entity-framework domain-driven-design repository-pattern

我正在构建一个小型的Recipe Organizer MVC项目,首先使用EntityFramework Code和Sql Server Compact版本,以了解有关DDD和Repository模式的更多信息。

以下是我的一些域名实体,我已针对此问题对其进行了简化。

public class Recipe {

    public int Id { get; set; }

    [Required]
    public string Name { get; set; }

    /// <summary>
    /// Represents a common meal type of “Breakfast, Lunch or Dinner” 
    /// or type of course “Appetisers, Soups, Salads, Fish, Poultry & Game, Meat, Side dishes, Desserts, Miscellaneous", etc.
    /// (for scope I am not considering brunch or supper or any other type) 
    /// </summary>
    [Required]
    public Course Course { get; set; }


    public virtual ICollection<Ingredient> Ingredients { get; set; }
}

public class Ingredient {
    public int Id { get; set; }
    [Required]
    public string Name { get; set; }
}

public class Course {
    public int Id { get; set; }
    [Required]
    public string Name { get; set; }
}

在我看来,对于这个项目的范围,我会说Recipe是一个聚合根,成分和课程是食谱的聚合。

如果我采用这个例子并使用MVC脚手架给我一个存储库模式的示例实现,我得到了下面的接口和一个实现接口的EFRecipeRepository类,并且引用了EF DB上下文。

public interface IRecipeRepository
{
    IQueryable<Recipe> All { get; }
    IQueryable<Recipe> AllIncluding(params Expression<Func<Recipe, object>>[] includeProperties);
    Recipe Find(int id);
    void InsertOrUpdate(Recipe recipe);
    void Delete(int id);
    void Save();
}

遵循此指导:

  

在存储库模式的上下文中,聚合根是唯一的   客户端代码从存储库加载的对象。

     

存储库封装了对来自调用者的子对象的访问   透视它自动加载它们,或者同时加载它们   root被加载或实际需要时(如延迟加载)。

我希望在此界面中添加更多内容,以便通过食谱获取成分或课程。

我遇到的问题是我想在我的视图中添加一些仅涉及成分或课程的功能,例如:所有不同类型课程的导航(不仅仅是与食谱相关的课程)和自动 - 完整的食材清单。

在我的项目范围内,配方不存在于食谱之外,所以它应该是食谱的聚合似乎是可行的。然而,看起来它应该是一个聚合根本身,因为它本身就存在吗?

我正在努力想象这一切应该如何运作,如果我想获得每个成分的列表意味着我首先必须使用食谱库来加载所有的食谱以便找到所有的成分?

课程有一个类似的问题,除了它被用作分类食谱的方式,因此也可以存在于该边界之外。

我缺少一些概念吗?也许是一种创建用于此类表示层要求的服务的方法?有没有人对如何做到这一点有任何意见?或者更好的建议?感谢

另一方面,这是一个小项目,DDD可能不是最好的方法,但我确实发现存储库模式对于我的Domain模型中的持久性无知非常有用。

由于

1 个答案:

答案 0 :(得分:2)

  

我正在努力想象这一切应该如何运作,如果我想获得每个成分的列表意味着我首先必须使用食谱库来加载所有的食谱以便找到所有的成分?

如果您需要的是自动完成的不同成分名称列表,我确实只提供从数据库中提取必要报告的服务。我完全可以使用聚合中的报告,因为它们只是 - 报告。您不应该让服务从聚合内部返回可用实体(即不返回Ingredient []),因为这会打开调用者的聚合边界。只需返回一个字符串[]或类似字符串。

然后,我会考虑课程和成分聚合根源,除了最琐碎的应用程序。成分可以没有配方吗?绝对! “糖”#1(属于食谱#1)是否与“糖”#2(属于食谱#2)不同?我不这么认为。有糖。还有一些使用糖的食谱。制作成分聚合根源很有意义。例如,您可以按成分轻松浏览食谱。

同样适用于课程,这是一个绝对有意义的概念。

一个不应该是聚合根的概念的一个很好的例子是一个订单行(也就是说,“3件Foobar电视每件700美元”)。如果没有订单,订单行就毫无意义。但即使在这种情况下,在订单行上生成报告的服务也是有意义的(在理论上)并且是合法的IMO。例如,人们可能想知道通常一起购买多少Foobar电视,以便推出新的Foobar TV 3-pack(是的,无论如何......)。这将再次意味着在数据库中搜索通常无法直接访问的数据。