abp學習日誌三(實體&聚合根)

實體

實體是DDD(Domain Driven Design)中核心概念.Eric Evans是這樣描述實體的 “一個沒有從其屬性,而是通過連續性和身份的線索來定義的對象”

實體通常映射到關係型數據庫的表中。1

Product實體

按照項目結構,Product應該創建在Domain項目中,所以源碼是這樣的
目錄結構
在這裏插入圖片描述
代碼

using System;
using System.Collections.Generic;
using System.Text;
using Volo.Abp.Domain.Entities;

namespace LY.Shop.Models
{
    public class Product :Entity<Guid> // AggregateRoot<Guid>
    {
        public string ProductName { get; set; }
        public string ProductUnit { get; set; }
        public string ProductDescription { get; set; }
        public decimal ProductPrice { get; set; }
        public decimal StoreNumbers { get; set; } 
        public string Note { get; set; }

        protected Product()
        {
        }
        public Product(Guid id)
         : base(id)
        {
        }
    }
}

Entity類

這個類是abp框架提供的,看一下他的源碼

using System;
using System.Collections.Generic;
using System.Reflection;
using Volo.Abp.MultiTenancy;

namespace Volo.Abp.Domain.Entities
{
    /// <inheritdoc/>
    [Serializable]
    public abstract class Entity : IEntity
    {
        /// <inheritdoc/>
        public override string ToString()
        {
            return $"[ENTITY: {GetType().Name}] Keys = {GetKeys().JoinAsString(", ")}";
        }

        public abstract object[] GetKeys();
    }

    /// <inheritdoc cref="IEntity{TKey}" />
    [Serializable]
    public abstract class Entity<TKey> : Entity, IEntity<TKey>
    {
        /// <inheritdoc/>
        public virtual TKey Id { get; protected set; }

        protected Entity()
        {

        }

        protected Entity(TKey id)
        {
            Id = id;
        }

        public bool EntityEquals(object obj)
        {
            if (obj == null || !(obj is Entity<TKey>))
            {
                return false;
            }

            //Same instances must be considered as equal
            if (ReferenceEquals(this, obj))
            {
                return true;
            }

            //Transient objects are not considered as equal
            var other = (Entity<TKey>)obj;
            if (EntityHelper.HasDefaultId(this) && EntityHelper.HasDefaultId(other))
            {
                return false;
            }

            //Must have a IS-A relation of types or must be same type
            var typeOfThis = GetType().GetTypeInfo();
            var typeOfOther = other.GetType().GetTypeInfo();
            if (!typeOfThis.IsAssignableFrom(typeOfOther) && !typeOfOther.IsAssignableFrom(typeOfThis))
            {
                return false;
            }

            //Different tenants may have an entity with same Id.
            if (this is IMultiTenant && other is IMultiTenant &&
                this.As<IMultiTenant>().TenantId != other.As<IMultiTenant>().TenantId)
            {
                return false;
            }

            return Id.Equals(other.Id);
        }
        
        public override object[] GetKeys()
        {
            return new object[] {Id};
        }

        /// <inheritdoc/>
        public override string ToString()
        {
            return $"[ENTITY: {GetType().Name}] Id = {Id}";
        }
    }
}

這份源碼沒有什麼好分析的,就是做了一個基類,重寫了幾個常用的方法。

聚合根

“聚合是域驅動設計中的一種模式.DDD的聚合是一組可以作爲一個單元處理的域對象.例如,訂單及訂單系列的商品,這些是獨立的對象,但將訂單(連同訂單系列的商品)視爲一個聚合通常是很有用的”2

源碼

namespace Volo.Abp.Domain.Entities
{
    /// <summary>
    /// Defines an aggregate root. It's primary key may not be "Id" or it may have a composite primary key.
    /// Use <see cref="IAggregateRoot{TKey}"/> where possible for better integration to repositories and other structures in the framework.
    /// </summary>
    public interface IAggregateRoot : IEntity
    {

    }

    /// <summary>
    /// Defines an aggregate root with a single primary key with "Id" property.
    /// </summary>
    /// <typeparam name="TKey">Type of the primary key of the entity</typeparam>
    public interface IAggregateRoot<TKey> : IEntity<TKey>, IAggregateRoot
    {

    }
}

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Volo.Abp.Auditing;
using Volo.Abp.Data;

namespace Volo.Abp.Domain.Entities
{
    [Serializable]
    public abstract class AggregateRoot : Entity, 
        IAggregateRoot,
        IGeneratesDomainEvents, 
        IHasExtraProperties,
        IHasConcurrencyStamp
    {
        public virtual Dictionary<string, object> ExtraProperties { get; protected set; }

        [DisableAuditing]
        public virtual string ConcurrencyStamp { get; set; }

        private readonly ICollection<object> _localEvents = new Collection<object>();
        private readonly ICollection<object> _distributedEvents = new Collection<object>();

        protected AggregateRoot()
        {
            ExtraProperties = new Dictionary<string, object>();
            ConcurrencyStamp = Guid.NewGuid().ToString("N");
        }

        protected virtual void AddLocalEvent(object eventData)
        {
            _localEvents.Add(eventData);
        }

        protected virtual void AddDistributedEvent(object eventData)
        {
            _distributedEvents.Add(eventData);
        }

        public virtual IEnumerable<object> GetLocalEvents()
        {
            return _localEvents;
        }

        public virtual IEnumerable<object> GetDistributedEvents()
        {
            return _distributedEvents;
        }

        public virtual void ClearLocalEvents()
        {
            _localEvents.Clear();
        }

        public virtual void ClearDistributedEvents()
        {
            _distributedEvents.Clear();
        }
    }

    [Serializable]
    public abstract class AggregateRoot<TKey> : Entity<TKey>, 
        IAggregateRoot<TKey>, 
        IGeneratesDomainEvents, 
        IHasExtraProperties,
        IHasConcurrencyStamp
    {
        public virtual Dictionary<string, object> ExtraProperties { get; protected set; }

        [DisableAuditing]
        public virtual string ConcurrencyStamp { get; set; }

        private readonly ICollection<object> _localEvents = new Collection<object>();
        private readonly ICollection<object> _distributedEvents = new Collection<object>();

        protected AggregateRoot()
        {
            ExtraProperties = new Dictionary<string, object>();
            ConcurrencyStamp = Guid.NewGuid().ToString("N");
        }

        protected AggregateRoot(TKey id)
            : base(id)
        {
            ExtraProperties = new Dictionary<string, object>();
            ConcurrencyStamp = Guid.NewGuid().ToString("N");
        }

        protected virtual void AddLocalEvent(object eventData)
        {
            _localEvents.Add(eventData);
        }

        protected virtual void AddDistributedEvent(object eventData)
        {
            _distributedEvents.Add(eventData);
        }

        public virtual IEnumerable<object> GetLocalEvents()
        {
            return _localEvents;
        }

        public virtual IEnumerable<object> GetDistributedEvents()
        {
            return _distributedEvents;
        }

        public virtual void ClearLocalEvents()
        {
            _localEvents.Clear();
        }

        public virtual void ClearDistributedEvents()
        {
            _distributedEvents.Clear();
        }
    }
}

聚合根中除了繼承IEntity接口外,還繼承了其他幾個接口(稍後再說),主要思想還是對IEntity再一次封裝。

官方框架已經給出了幾種聚合根,擴展了不同的常用字段

  • CreationAuditedEntity 和 CreationAuditedAggregateRoot 實現了 ICreationAuditedObject 接口.
  • AuditedEntity 和 AuditedAggregateRoot 實現了 IAuditedObject 接口.
  • FullAuditedEntity and FullAuditedAggregateRoot 實現了 IFullAuditedObject 接口.

額外屬性

關於額外屬性使用到的情況非常少,大致就是通過json存在數據庫中,映射到實體的ExtraProperties 屬性,通過GetProperty 和 SetProperty方法進行取值和寫值,可以通過HasProperty 判斷是否存在該屬性,也可以通過RemoveProperty 方法刪除擴展屬性中的某一個屬性。


  1. 官網原話,值得推敲,Eric Evans的原話咱沒看懂,但是 下面語句映射到關係數據庫表中就清晰了許多。 ↩︎

  2. 聚合根其實就是對實體的進一步封裝。繼承自IEntity接口 ↩︎

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章