MongoDB 使用来自 RDBMS World 建议的 DDD

MongoDB using DDD coming from RDBMS World advice

我正在编写一个应用程序,使用 RDBMS 一切顺利,但我知道我正在做的是 NoSql 数据库的候选。

我正在制作一个应用程序的原型,您可以在其中滑动人物,最终您会找到匹配项。但是我有一些问题,现在我进入了 NoSql 的世界,我无法理解它们,而且它们很难从我的脑海中摆脱出来..

例如,因为我会左右滑动,所以我想将每个人的那些 personId 作为参考资源放入同一个集合(人)中,但设计我的 DDD 模型很苛刻。

我的模型目前是这样的:

public class Person
{
    private List<Guid> personsNotInterestedRefs = new List<Guid>();
   
    public Guid Id { get; private set; }
    public PersonName Name { get; private set; }        

    public IReadOnlyList<Guid> PersonsNotInterestedRefs
    {
        get => personsNotInterestedRefs.AsReadOnly();
        private set { personsNotInterestedRefs = value.ToList(); }
    }
   
    public Person(PersonName name, PersonAge age, PersonGender gender)
    {
        this.Age = age;
        this.Name = name;
        this.Gender = gender;
    }

    public Person Swipe(Guid other, FeelForPerson feel)
    {
        switch (feel)
        {
            case FeelForPerson.NotLiked:
                personsNotInterestedRefs.Add(other);
                break;
            case FeelForPerson.Liked:
               ..
        }
        return this;
    }
}

在 RDBMS 世界中,我将仅将 EF Core 与域对象(不存在 ID)一起使用,当我创建一个实体时,那些集合中的那些项目在调用 SaveChanges 时将被持久化,但在 Mongo 据我所知,我们没有与 EF 相同的上下文,但有集合和处理集合的方法,所以我会在存储库级别做这样的事情:

  var filter = Builders<Person>.Filter.Eq(x => x.Id, person.Id);
        var update = Builders<Person>.Update.PushEach(p => p.PersonsInterestedRefs, person.PersonsInterestedRefs);
        personsCollection.FindOneAndUpdate(filter, update);

但是这段代码并不属于Person本身。我的意思是使用 EF 我们只会在域级别进行更改,而 EF 将负责发送命令并在数据库中更新并同步我的模型,但是对于 Mongo 我们需要手动更新它吗?如果是的话,地点在哪里?我们应该在哪里与 DDD 和更新保持同步?

我想我在这里漏掉了一个大的。

谢谢。

简单的答案是,在 NoSQL 中,一个聚合存储在单个文档中。这意味着整个集合将存储在 Person 的文档中,而不是在不同的集合中。这可能会出现问题,因为匹配列表会不断增长,可能会导致您的应用程序在未来的某个时间点因非常大的聚合而失败。

我处理 DDD 的方式主要是首先是领域模型设计,其次是技术。大多数时候,当您的域名非常正确时,技术就不是问题。综上所述,我将从探索您的模型不完全正确的可能性开始,以了解为什么 MongoDb 会出现问题。

以下只是我将如何探索域设计的示例:

我们需要 Person 来管理匹配列表吗?是否有属于 Person 的任何业务逻辑依赖于整个集合的信息并且需要以事务方式执行?

另一方面,在匹配应用程序中,Match 难道不会比集合中集合中的简单记录更重要吗?我们能不能得到像“当 PersonA 喜欢 PersonB 并且 PersonB 已经喜欢 PersonA 时,做 X”这样的功能请求?如果从 PersonA 到 PersonB 的匹配与从 PersonB 到 PersonA 的匹配在不同的聚合中,我们将如何实现这种类型的功能?

也许我们可以让 Match(PersonA, PersonB) 成为一个在两个方向上包含 FeelForPerson 的聚合,这样它就可以在所有场景中执行所需的业务逻辑。

通过这种设计,我们不会遇到聚合必须维护外部集合的问题,或者如果集合在聚合内,也不会遇到集合变得太大的问题。每次我们得到一个 Swipe 命令(将包含 PersonA.Id 和 PersonB.Id),一个用例将创建一个 Match(PersonA, PersonB) 或加载一个现有的,执行业务操作它并存储它。 Person其实不需要参与这些操作。

希望对您有所帮助。