场景: 我有一个现有的UI,它不是非常关注有界的上下文,我有一些屏幕需要执行多个BC中的命令。我有想法根据聚合根ID(值对象)的值和聚合的类型来计算聚合ID(GUID)。我有一个事件表(SQL Server),其中存储了表示同一业务对象的多个聚合类型,并具有相同的值 - 对象ID(例如,PersonId)。
聚合根ID对象:
[Serializable]
public class PersonId : IEquatable<PersonId>
{
public PersonId(string id)
{
Id = id;
}
public string Id { get; private set; }
public bool Equals(PersonId other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return string.Equals(Id, other.Id);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != GetType()) return false;
return Equals((PersonId) obj);
}
public override int GetHashCode()
{
return (Id != null ? Id.GetHashCode() : 0);
}
}
计算聚合ID的扩展方法:
public static class AggregateIdExtensions
{
public static Guid ToAggregateId(this object id, Type aggregateType)
{
var combinedIdentifiers = new Tuple<Type, object>(aggregateType, id);
var md5 = MD5.Create();
var bytes = ObjectToByteArray(combinedIdentifiers);
var hash = md5.ComputeHash(bytes);
return new Guid(hash);
}
private static byte[] ObjectToByteArray(Object obj)
{
if (obj == null)
return null;
var bf = new BinaryFormatter();
var ms = new MemoryStream();
bf.Serialize(ms, obj);
return ms.ToArray();
}
}
用法:
var aggregateId = new PersonId("personId")
.ToAggregateId(typeof (BoundedContextNumberOne.Person));
这里的目标是能够从UI执行命令而无需在读取的模型中携带来自多个BC的GUID聚合ID(我甚至不确定我能做到),或者强迫我携带额外的读取DB中的模型表以存储aggregate-root-id-to-aggregate-id关系,并在命令处理程序中强制执行其他查询。
这个解决方案“太过分了吗”?是否有更好的方法提出建议?
我也不确定如何计算使用不同类型/值生成相同GUID的聚合ID冲突的可能性。