聚合不变量是否可以包含基于来自其他地方的信息的规则?
聚合总是可以使用它们自己状态的信息以及它们的命令接收到的参数。
有人过去通过单例、服务定位器等访问应用程序服务,但在 IMO,这是一种紧密耦合的应用程序的味道。他们忘记了方法的参数是有效的依赖注入器!:-)
在 DDD 中,聚合不变量是否可以包含基于另一个聚合中信息的规则?
不
。当然,除非第二个聚合是通过命令的参数提供的。
警告 !!!
我有一个名为 Asset(设备)的实体
......(和一个名为 AssetType 的第二个聚合......
上次我不得不处理类似的结构时,我感到很痛苦。
您可能选择了错误的抽象。
我的不变量错了吗?
可能......你问过领域专家吗?他谈论“TagTypes”吗?
你永远不应该自己抽象。
X
持有对 an 实例的引用的类型的实体X-Type
几乎总是带有过度抽象的味道,希望重用,这使得模型对业务发展变得僵硬和不灵活。
回答
如果(且仅当)领域专家确实用这些术语描述了模型,可能的方法如下:
- 您可以使用工厂方法创建一个
AssetType
类,该方法将 aIEnumerable<Tag>
转换为 a并在某些标记丢失或意外时抛出TagSet
。MissingMandatoryTagException
UnexpectedTagException
- 在
Asset
类中,一个命令RegisterTags
将接受一个AssetType
和一个IEnumerable<Tag>
,抛出一个MissingMandatoryTagException
和WrongAssetTypeException
(注意异常对于确保不变量是多么重要)。
编辑
这样的东西,但更多的记录:
public class AssetType
{
private readonly Dictionary<TagType, bool> _tagTypes = new Dictionary<TagType, bool>();
public AssetType(AssetTypeName name)
{
// validation here...
Name = name;
}
/// <summary>
/// Enable a tag type to be assigned to asset of this type.
/// </summary>
/// <param name="type"></param>
public void EnableTagType(TagType type)
{
// validation here...
_tagTypes[type] = false;
}
/// <summary>
/// Requires that a tag type is defined for any asset of this type.
/// </summary>
/// <param name="type"></param>
public void RequireTagType(TagType type)
{
// validation here...
_tagTypes[type] = false;
}
public AssetTypeName Name { get; private set; }
/// <summary>
/// Builds the tag set.
/// </summary>
/// <param name="tags">The tags.</param>
/// <returns>A set of tags for the current asset type.</returns>
/// <exception cref="ArgumentNullException"><paramref name="tags"/> is <c>null</c> or empty.</exception>
/// <exception cref="MissingMandatoryTagException">At least one of tags required
/// by the current asset type is missing in <paramref name="tags"/>.</exception>
/// <exception cref="UnexpectedTagException">At least one of the <paramref name="tags"/>
/// is not allowed for the current asset type.</exception>
/// <seealso cref="RequireTagType"/>
public TagSet BuildTagSet(IEnumerable<Tag> tags)
{
if (null == tags || tags.Count() == 0)
throw new ArgumentNullException("tags");
TagSet tagSet = new TagSet();
foreach (Tag tag in tags)
{
if(!_tagTypes.ContainsKey(tag.Key))
{
string message = string.Format("Cannot use tag {0} in asset type {1}.", tag.Key, Name);
throw new UnexpectedTagException("tags", tag.Key, message);
}
tagSet.Add(tag);
}
foreach (TagType tagType in _tagTypes.Where(kvp => kvp.Value == true).Select(kvp => kvp.Key))
{
if(!tagSet.Any(t => t.Key.Equals(tagType)))
{
string message = string.Format("You must provide the tag {0} to asset of type {1}.", tagType, Name);
throw new MissingMandatoryTagException("tags", tagType, message);
}
}
return tagSet;
}
}
public class Asset
{
public Asset(AssetName name, AssetTypeName type)
{
// validation here...
Name = name;
Type = type;
}
public TagSet Tags { get; private set; }
public AssetName Name { get; private set; }
public AssetTypeName Type { get; private set; }
/// <summary>
/// Registers the tags.
/// </summary>
/// <param name="tagType">Type of the tag.</param>
/// <param name="tags">The tags.</param>
/// <exception cref="ArgumentNullException"><paramref name="tagType"/> is <c>null</c> or
/// <paramref name="tags"/> is either <c>null</c> or empty.</exception>
/// <exception cref="WrongAssetTypeException"><paramref name="tagType"/> does not match
/// the <see cref="Type"/> of the current asset.</exception>
/// <exception cref="MissingMandatoryTagException">At least one of tags required
/// by the current asset type is missing in <paramref name="tags"/>.</exception>
/// <exception cref="UnexpectedTagException">At least one of the <paramref name="tags"/>
/// is not allowed for the current asset type.</exception>
public void RegisterTags(AssetType tagType, IEnumerable<Tag> tags)
{
if (null == tagType) throw new ArgumentNullException("tagType");
if (!tagType.Name.Equals(Type))
{
string message = string.Format("The asset {0} has type {1}, thus it can not handle tags defined for assets of type {2}.", Name, Type, tagType.Name);
throw new WrongAssetTypeException("tagType", tagType, message);
}
Tags = tagType.BuildTagSet(tags);
}
}