0

两个月前,我买了 Scott Millet 的《专业的 ASP.NET 设计模式》一书,因为我想学习如何使用设计模式构建一个分层的 Web 应用程序。我在自己的应用程序中使用了本书中的案例研究,所以一切都设置好了。

我的问题是我不确定我的总根。

我有一个可以创建集合的用户。用户可以将类别添加到集合中,并将关键字添加到类别中。在我的数据库中看起来像这样:

- Users
    - PK: UserId

- Collections
    - PK: CollectionId
    - FK: UserId

- Categories
    - PK: CategoryId
    - FK: CollectionId

- Keywords
    - PK: KeywordId
    - FK: CategoryId

我认为让用户成为集合的聚合根不合逻辑,但类别和关键字一起形成一个集合。所以我让用户成为一个还没有孩子的聚合根,并收集一个聚合根。一个集合可以有多个类别,类别可以有多个关键字。所以当我想添加一个类别时,我会这样做:

public void CreateCategory(CreateCategoryRequest request)
    {
        Collection collection = _collectionRepository.FindCollection(request.IdentityToken, request.CollectionName);

        Category category = new Category { Collection = collection, CategoryName = request.CategoryName };

        ThrowExceptionIfCategoryIsInvalid(category);

        collection.AddCategory(category);

        _collectionRepository.Add(collection);
        _uow.Commit();
    }

效果很好,但是当我想添加关键字时,我首先需要获取集合,然后获取可以添加关键字的类别,然后提交集合:

public void CreateKeyword(CreateKeywordRequest request)
    {
        Collection collection = _collectionRepository.FindCollection(request.IdentityToken, request.CollectionName);

        Category category = collection.Categories.Where(c => c.CategoryName == request.CategoryName).FirstOrDefault();

        Keyword keyword = new Keyword { Category = category, KeywordName = request.KeywordName, Description = request.KeywordDescription };

        category.AddKeyword(keyword);

        _collectionRepository.Add(collection);
        _uow.Commit();
    }

而这只是感觉不对(是吗?)是什么让我相信我应该让类别成为关键字的总根。但这提出了另一个问题:我有一个集合聚合,它像我在第一个代码示例中所做的那样创建一个类别聚合,这仍然有效吗?示例:collection.Add(category);

4

2 回答 2

1

聚合根当然可以包含嵌套的子级,但是如果这些子级也是聚合,这可能是一个警告,即聚合可能做的太多了。在您的情况下,我认为Collection是聚合而Category不是聚合,它只是一个实体,甚至是属于Collection聚合的值对象,它恰好包含Keyword也是值对象的实例。

我会更改实现,使CreateCategory服务方法看起来更像这样:

public void CreateCategory(CreateCategoryRequest request)
{
        var collection = _collectionRepository.Get(request.IdentityToken, request.CollectionName);

        collection.AddCategory(request.CategoryName);

        _uow.Commit();
}

上的AddCategory方法Collection负责创建Category实例以及错误检查。这是有道理的,因为它是聚合根,它负责管理它包含的实体集群和值对象。没有调用存储库上的 Add 方法,因为环境工作单元应该提交更改。

我会改变的CreateKeyword方法看起来更像:

public void CreateKeyword(CreateKeywordRequest request)
{
        var collection = _collectionRepository.Get(request.IdentityToken, request.CollectionName);

        collection.AddKeyword(request.CategoryName, request.KeywordName, request.KeywordDescription);

        _uow.Commit();
}

AddKeywordon 方法Collection检索适当的内容Category,然后向其添加关键字,如果需要,则抛出异常以强制一致性和有效性。

如您所见,这两种方法有一个模式 - 首先通过键检索聚合,然后调用聚合上的方法,最后提交所有内容。通过这种方式,聚合可以更好地控制它们自己的状态,并且您可以避免出现贫乏的域模型以及减少服务中存在的代码量。

有关聚合设计的深入处理,请查看Vaughn Vernon 的有效聚合设计

于 2012-08-21T23:50:14.373 回答
0

这可能感觉不对,因为要添加一个新的关键字,您一直从 Collection 引用开始,而您完全可以从 Category 引用开始。当用户添加关键字时,他肯定是在类别的上下文中这样做的,因此您当时可能在内存中拥有该类别。

另外,我没有得到_collectionRepository.Add(collection)你每个方法的结尾Add[SomeSubEntity]()。Collection 应该已经存在于存储库中,所以您应该做的就是保存它,对吗?

至于嵌套聚合,我发现它有点复杂,并且与 2 个单独的聚合(类别和集合)相比并没有真正看到好处。

于 2012-08-21T11:51:10.843 回答