using BeyondNetCode.Shell.Ddd.Rules;
using BeyondNetCode.Shell.Ddd.Interfaces;
using BeyondNetCode.Shell.Ddd.Extensions;
using BeyondNetCode.Shell.Ddd.Rules.Impl;
using BeyondNetCode.Shell.Ddd.Services.Impl;
namespace BeyondNetCode.Shell.Ddd
{
///
/// Represents an abstract base class for entities in the domain-driven design.
///
/// The type of the entity.
/// The type of the entity properties.
public abstract class Entity : IEntity where TEntity : class
where TProps : class, IProps
{
#region Members
///
/// The properties of the entity.
///
private TProps _props;
#endregion
#region Properties
public IdValueObject Id { get; private set; }
public IdValueObject SetId(string id)
{
return IdValueObject.Load(id);
}
public BrokenRulesManager BrokenRules { get; }
public TrackingStateManager TrackingState { get; }
public ValidatorRuleManager> ValidatorRules { get; }
#endregion
#region Constructors
///
/// Initializes a new instance of the class.
///
/// The properties of the entity.
protected Entity(TProps props)
{
BrokenRules = new BrokenRulesManager();
TrackingState = new TrackingStateManager();
ValidatorRules = new ValidatorRuleManager>();
Id = IdValueObject.Create();
_props = props;
Validate();
TrackingState.MarkAsNew();
}
#endregion
#region Methods
///
/// Gets a copy of the entity properties.
///
/// A copy of the entity properties.
public TProps GetPropsCopy()
{
var copyProps = _props!.Clone();
return (TProps)copyProps;
}
///
/// Gets the properties of the entity.
///
public TProps Props
{
get { return _props!; }
}
///
/// Sets the properties of the entity.
///
/// The properties to set.
public void SetProps(TProps props)
{
_props = props;
TrackingState.MarkAsDirty();
}
#endregion
#region BusinessRules
///
/// Gets a value indicating whether the entity is valid.
///
/// true if the entity is valid; otherwise, false.
public bool IsValid()
{
Validate();
return !BrokenRules.GetBrokenRules().Any();
}
///
/// Validates the entity.
///
public void Validate()
{
Guard();
// Add validators for Entity
AddValidators();
// Get broken rules for Entity
BrokenRules.Add(ValidatorRules.GetBrokenRules().ToList());
if (BrokenRules.GetBrokenRules().Any())
{
TrackingState.MarkAsDirty();
return;
}
// Explore broken rules for properties
var props = GetPropsCopy().GetType().GetProperties();
var propsBrokenRules = props.GetPropertiesBrokenRules(_props);
if (propsBrokenRules.Any())
{
BrokenRules.Add(propsBrokenRules);
TrackingState.MarkAsDirty();
}
}
private void Guard()
{
if (!(this is TEntity))
throw new InvalidOperationException($"Entity '{GetType().Print()}' specifies '{typeof(TEntity).Print()}' as generic argument, it should be its own type");
}
///
/// Adds the validators for the value object.
///
public virtual void AddValidators()
{
}
#endregion
#region Equality
///
public override bool Equals(object? obj)
{
if (obj == null || !(obj is Entity))
return false;
if (ReferenceEquals(this, obj))
return true;
if (GetType() != obj.GetType())
return false;
return ReferenceEntityPropertiesEquals(obj);
}
private bool ReferenceEntityPropertiesEquals(object? obj)
{
if (obj is not Entity entity)
return false;
return Id.Equals(entity.Id);
}
///
public override int GetHashCode()
{
return Id.GetHashCode();
}
///
/// Determines whether two entities are equal.
///
/// The left entity.
/// The right entity.
/// true if the entities are equal; otherwise, false.
public static bool operator ==(Entity left, Entity right)
{
if (Equals(left, null))
return Equals(right, null) ? true : false;
else
return left.Equals(right);
}
///
/// Determines whether two entities are not equal.
///
/// The left entity.
/// The right entity.
/// true if the entities are not equal; otherwise, false.
public static bool operator !=(Entity left, Entity right)
{
return !(left == right);
}
#endregion
#region TransitionStatus
///
/// Finite State Machine (FSM) Pattern
///
private readonly Dictionary