.Net Core 微服务实战 - 领域模型的设计与实现

源码及系列文章目录

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 方法,输出了值对象的所有属性。

注意点

  • 将领域模型字段的修改设置为私有
  • 使用构造函数表示对象的创建
  • 使用具有业务含义的动作来操作模型字段
  • 领域模型负责对自己数据的处理
  • 领域服务或命令处理者负责调用领域模型业务动作
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值