Mongodb持久化域实体并保护不变量

本文关键字:保护 不变量 实体 持久化 Mongodb | 更新日期: 2023-09-27 18:09:43

我正在使用一个聚合根和子实体的域驱动设计。

聚合不变量通过聚合根ConfigurableService方法强制执行,通过链接到子实体的方法,使用GroupsDependencies列表映射组之间的需求。

一个不变的例子是ConfigurableService,只有当skuid包含在列表中的一个组中时,才允许建立依赖关系。

如果我去使组或依赖公共(作为mongodb需要持久性),那么这个域逻辑可以被绕过-所以我认为类可以扩展与公共属性,这将调用实体方法,如下面的DependenciesGroups属性:

public class ConfigurableService
{
    List<Groups> groups = new List<Groups>();
    Dependencies dependencies = new Dependencies();
    public void AddDependency(SkuId on, SkuId requires)
    {
        if(IsContainsSku(on) && IsContainsSku(requires))
            this.dependencies.SetRequiresFor(on, requires);
        else
            throw new Exception("SkuId doesnt exist");
    }
    public bool IsContainsSku(SkuId sku)
    {
        foreach(var group in groups)
        {
            if(group.ContainsSku(sku)==true)
            {
                return true;
            }
        }
        return false;
    }
    // other code snipped for breverity
    IEnumerable<Dependency> Dependencies
    {
        get { return this.dependencies.GetAllDependencies(); }
        set
        {
            foreach(var dependency in value)
            {
                this.AddDependency(
                    new SkuId(dependency.Source),
                    new SkuId(dependency.Target)
                    );
            }
        }
    }
    IEnumerable<Group> Groups
    {
        get { return this.groups; }
        set
        {
            this.groups.Clear();
            foreach(var group in groups)
            {
                this.groups.Add(group);
            }
        }
    }
}

对于每个需要持久化的内部属性,我将重复,因为这将检查域逻辑。

在构建对象时从存储库读取时,只要按照正确的顺序设置属性,就可以正常工作…例如,如果Groups设置在Dependencies之前,我们最终会抛出一个异常(SkuID不存在)。

  1. 保护不变量和允许Mongodb持久化/检索域实体对象?

  2. 我可以控制mongodb水合物时属性设置的顺序吗从它的数据库?

  3. 或者最好创建一个自定义序列化方法(如何我会这样做吗?

Mongodb持久化域实体并保护不变量

我的同事想出了一个自定义序列化器来解决这个问题,实体可以从BsonSerializer DTO手动重建:

public class ConfigurableServicePersistenceMapper
{
    public ConfigurableServiceId Id { get; set; }
    public string Description { get; set; }
    public HashSet<Group> Groups { get; set; }
    public Dependencies Dependencies { get; set; }
}
public class ConfigurableServiceSerializer : BsonBaseSerializer
{
    public override void Serialize(BsonWriter bsonWriter, Type nominalType, object value, IBsonSerializationOptions options)
    {
        // implement using bsonWriter
        if (nominalType != typeof(ConfigurableService))
            throw new Exception("Object should be of type 'ConfigurableService'");
        var obj = (ConfigurableService)value;
        var map = new ConfigurableServicePersistenceMapper()
        {
            Dependencies = obj.Dependencies,
            Description = obj.Description,
            Groups = obj.Groups,
            Id = obj.Id
        };
        BsonSerializer.Serialize(bsonWriter, map);
    }
    public override object Deserialize(BsonReader bsonReader, Type nominalType, Type actualType, IBsonSerializationOptions options)
    {
        // implement using bsonreader
        if (nominalType != typeof(ConfigurableService))
            throw new Exception("object should be of type 'ConfigurableService'");
        var bson = BsonSerializer.Deserialize<BsonDocument>(bsonReader);
        var configurableServiceMapper = BsonSerializer.Deserialize<ConfigurableServicePersistenceMapper>(bson);
        var configurableService = new ConfigurableService(configurableServiceMapper.Id)
        {
            Description = configurableServiceMapper.Description
        };

        foreach (var group in configurableServiceMapper.Groups)
        {
            configurableService.NewGroup(group.Id);
            var retrievedGroup = configurableService.GetGroup(group.Id);
            retrievedGroup.Description = group.Description;
            foreach (var sku in group.Skus)
            {
                retrievedGroup.Add(sku);
            }
            // set requirements
            List<Group> groupList = new List<Group>(configurableServiceMapper.Groups);
            foreach (var sku in group.Skus)
            {
                List<string> dependencies =
                    new List<string>(configurableServiceMapper.Dependencies.GetDependenciesFor(sku));

                foreach (var dependencySkuString in dependencies)
                {
                    retrievedGroup.SetRequirementFor(sku)
                        .Requires(new SkuId(dependencySkuString));
                }
            }
        }
        return configurableService;
    }
}