我正在使用一个聚合根和子实体的域驱动设计。
聚合不变量通过聚合根ConfigurableService
方法强制执行,通过链接到子实体的方法,使用Groups
和Dependencies
列表映射组之间的需求。
一个不变的例子是ConfigurableService
,只有当skuid包含在列表中的一个组中时,才允许建立依赖关系。
如果我去使组或依赖公共(作为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);
}
}
}
}
对于每个需要持久化的内部属性,我将重复,因为这将检查域逻辑。
在构建对象时从存储库读取时,只要按照正确的顺序设置属性,就可以正常工作…例如,如果Groups设置在Dependencies
之前,我们最终会抛出一个异常(SkuID不存在)。
保护不变量和允许Mongodb持久化/检索域实体对象?
我可以控制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;
}
}