个人未完成的网站

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接口 ↩︎

posted on 2020-03-15 22:52  我是小虫  阅读(723)  评论(0编辑  收藏  举报

导航