Mongodb сохранит доменную сущность и защитит инварианты

Я использую управляемый доменом дизайн с совокупными корневыми и дочерними объектами.

Агрегатные инварианты применяются через агрегатный корень ConfigurableService методы, связанные с методами дочерних объектов, со списком Groups а также Dependencies какая карта требований между группами.

Один инвариантный пример ConfigurableService разрешить создание зависимости можно только в том случае, если SkuIds содержатся в одной из групп в списке.

Если я пойду и сделаю "Группы" или "Зависимости" общедоступными (поскольку mongodb требует постоянства), то эту логику домена можно обойти - поэтому я подумал, что класс можно расширить с помощью открытых свойств, которые будут вызывать методы сущностей, такие как Dependencies а также Groups свойства:

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);
            }
        }
    }
}

Я бы повторил для каждого внутреннего свойства, которое необходимо сохранить, поскольку это проверит логику домена.

Это будет хорошо работать при чтении из репозитория при построении объекта, если свойства установлены в правильном порядке... например, если группы были установлены ранее Dependencies мы бы в итоге бросили исключение (SkuID не существует).

Вопросы

  1. Каковы рекомендуемые способы защиты инвариантов и разрешения Mongodb сохранять / извлекать объект сущности домена?

  2. Могу ли я контролировать порядок установки свойств, когда mongodb гидратируется из своей базы данных?

  3. Или мне лучше всего создать собственный метод сериализации (как бы я это сделал)?

1 ответ

Решение

Мой коллега придумал специальный сериализатор, который решил проблему, объект можно было восстановить вручную из 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;
    }
}
Другие вопросы по тегам