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 方法删除扩展属性中的某一个属性。