领域模型的设计与实现
源码及系列文章目录
Git 源码 :https://github.com/tangsong1995/TS.Microservices
CSDN 资源 :https://download.csdn.net/download/qq_33649351/34675095
系列文章目录 :https://blog.csdn.net/qq_33649351/article/details/120998558
领域模型抽象层
IEntity
IEntity 定义实体的接口:
public interface IEntity
{
object GetKey();
}
//定义泛型接口
public interface IEntity<TKey> : IEntity
{
TKey Id { get; }
}
Entity的实现:
public abstract class Entity : IEntity
{
public abstract object GetKey();
public override string ToString()
{
return $"[Entity: {GetType().Name}] Keys = {string.Join(",", GetKey())}";
}
#region
private List<IDomainEvent> _domainEvents;
public IReadOnlyCollection<IDomainEvent> DomainEvents => _domainEvents?.AsReadOnly();
public void AddDomainEvent(IDomainEvent eventItem)
{
_domainEvents = _domainEvents ?? new List<IDomainEvent>();
_domainEvents.Add(eventItem);
}
public void RemoveDomainEvent(IDomainEvent eventItem)
{
_domainEvents?.Remove(eventItem);
}
public void ClearDomainEvents()
{
_domainEvents?.Clear();
}
#endregion
}
public abstract class Entity<TKey> : Entity, IEntity<TKey>
{
int? _requestedHashCode;
public virtual TKey Id { get; protected set; }
public override object GetKey()
{
return new object[] { Id };
}
public override bool Equals(object obj)
{
if (obj == null || !(obj is Entity<TKey>))
return false;
if (Object.ReferenceEquals(this, obj))
return true;
if (this.GetType() != obj.GetType())
return false;
Entity<TKey> item = (Entity<TKey>)obj;
if (item.IsTransient() || this.IsTransient())
return false;
else
return item.Id.Equals(this.Id);
}
public override int GetHashCode()
{
if (!IsTransient())
{
if (!_requestedHashCode.HasValue)
_requestedHashCode = this.Id.GetHashCode() ^ 31;
return _requestedHashCode.Value;
}
else
return base.GetHashCode();
}
//表示对象是否为全新创建的,未持久化的
public bool IsTransient()
{
return EqualityComparer<TKey>.Default.Equals(Id, default);
}
public override string ToString()
{
return $"[Entity: {GetType().Name}] Id = {Id}";
}
public static bool operator ==(Entity<TKey> left, Entity<TKey> right)
{
if (Object.Equals(left, null))
return (Object.Equals(right, null)) ? true : false;
else
return left.Equals(right);
}
public static bool operator !=(Entity<TKey> left, Entity<TKey> right)
{
return !(left == right);
}
}
Entity对象重载了 Equals 和 GetHashCode 方法,并且借助重载的 Equals 方法实现了操作符 == 和 != ;在判断实体是否相等时,可以使用 == 和 != 操作符直接判断。
IAggregateRoot
IAggregateRoot 定义聚合根的接口:
public interface IAggregateRoot
{
}
IAggregateRoot 没有定义具体方法。它的作用是,在实现仓储层的时候,约束一个仓储对应一个聚合根。
IDomainEvent
IDomainEvent 定义领域事件的接口:
public interface IDomainEvent : INotification
{
}
IDomainEventHandler
IDomainEventHandler定义领域事件处理的接口:
public interface IDomainEventHandler<TDomainEvent> : INotificationHandler<TDomainEvent>
where TDomainEvent : IDomainEvent
{
}
ValueObject
ValueObject,值对象,在领域模型中,值对象是一个比较关键的对象
public abstract class ValueObject
{
protected static bool EqualOperator(ValueObject left, ValueObject right)
{
if (ReferenceEquals(left, null) ^ ReferenceEquals(right, null))
{
return false;
}
return ReferenceEquals(left, null) || left.Equals(right);
}
protected static bool NotEqualOperator(ValueObject left, ValueObject right)
{
return !(EqualOperator(left, right));
}
protected abstract IEnumerable<object> GetAtomicValues();
public override bool Equals(object obj)
{
if (obj == null || obj.GetType() != GetType())
{
return false;
}
ValueObject other = (ValueObject)obj;
IEnumerator<object> thisValues = GetAtomicValues().GetEnumerator();
IEnumerator<object> otherValues = other.GetAtomicValues().GetEnumerator();
while (thisValues.MoveNext() && otherValues.MoveNext())
{
if (ReferenceEquals(thisValues.Current, null) ^ ReferenceEquals(otherValues.Current, null))
{
return false;
}
if (thisValues.Current != null && !thisValues.Current.Equals(otherValues.Current))
{
return false;
}
}
return !thisValues.MoveNext() && !otherValues.MoveNext();
}
public override int GetHashCode()
{
return GetAtomicValues()
.Select(x => x != null ? x.GetHashCode() : 0)
.Aggregate((x, y) => x ^ y);
}
}
值对象是没有Id的,值对象的定义只实现了对象的相等判断。
领域模型
实体的定义
以定义一个 Order 实体为例,实体需要继承 Entity 并且实现 IAggregateRoot 聚合根接口:
public class Order : Entity<long>, IAggregateRoot
{
public string UserId { get; private set; }
public string UserName { get; private set; }
public Address Address { get; private set; }
public int ItemCount { get; private set; }
protected Order()
{ }
public Order(string userId, string userName, int itemCount, Address address)
{
this.UserId = userId;
this.UserName = userName;
this.Address = address;
this.ItemCount = itemCount;
this.AddDomainEvent(new OrderCreatedDomainEvent(this));
}
public void ChangeAddress(Address address)
{
this.Address = address;
}
}
实体的属性都定义为了私有 set ,实体的数据变更都应该由实体内部负责,不应该被外部的对象操作,实体暴露具有业务逻辑的方法给外部调用,例如 ChangeAddress 方法为改变收货地址。
值对象的定义
public class Address : ValueObject
{
public string Street { get; private set; }
public string City { get; private set; }
public string ZipCode { get; private set; }
public Address() { }
public Address(string street, string city, string zipcode)
{
Street = street;
City = city;
ZipCode = zipcode;
}
protected override IEnumerable<object> GetAtomicValues()
{
yield return Street;
yield return City;
yield return ZipCode;
}
}
同样,值对象的属性也都定义成了 private set ,并且重载了 GetAtomicValues 方法,输出了值对象的所有属性。
注意点
- 将领域模型字段的修改设置为私有
- 使用构造函数表示对象的创建
- 使用具有业务含义的动作来操作模型字段
- 领域模型负责对自己数据的处理
- 领域服务或命令处理者负责调用领域模型业务动作