first commit
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
namespace Vizhvizh.BuildingBlocks.Domain.Contract;
|
||||
|
||||
public abstract record CommandBase<T> : ICommand<T>
|
||||
{
|
||||
public Guid Id { get; private set; }
|
||||
protected CommandBase()
|
||||
{
|
||||
Id = Guid.NewGuid();
|
||||
}
|
||||
protected CommandBase(Guid id)
|
||||
{
|
||||
Id = id;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract record CommandBase : ICommand
|
||||
{
|
||||
public Guid Id { get; private set; }
|
||||
protected CommandBase()
|
||||
{
|
||||
Id = Guid.NewGuid();
|
||||
}
|
||||
protected CommandBase(Guid id)
|
||||
{
|
||||
Id = id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace Vizhvizh.BuildingBlocks.Domain.Contract;
|
||||
|
||||
public interface ICommand<out TResult>
|
||||
{
|
||||
Guid Id { get; }
|
||||
}
|
||||
|
||||
public interface ICommand
|
||||
{
|
||||
Guid Id { get; }
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
namespace Vizhvizh.BuildingBlocks.Domain.Contract;
|
||||
|
||||
public interface INotification
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
namespace Vizhvizh.BuildingBlocks.Domain.Contract;
|
||||
|
||||
public interface IQuery<out TResult>
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace Vizhvizh.BuildingBlocks.Domain;
|
||||
|
||||
public class DomainEventBase : IDomainEvent
|
||||
{
|
||||
public Guid Id { get; }
|
||||
|
||||
public DateTime OccurredOn { get; }
|
||||
|
||||
public DomainEventBase()
|
||||
{
|
||||
this.Id = Guid.NewGuid();
|
||||
this.OccurredOn = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
|
||||
using Vizhvizh.BuildingBlocks.Domain.Exceptions;
|
||||
|
||||
namespace Vizhvizh.BuildingBlocks.Domain;
|
||||
|
||||
public abstract class Entity<T>
|
||||
where T : ITypeIdValueBase
|
||||
{
|
||||
public Entity()
|
||||
{
|
||||
CreateOn = DateTime.Now;
|
||||
}
|
||||
public T Id { get; protected set; }
|
||||
public DateTime CreateOn { get; init ; }
|
||||
private List<IDomainEvent> _domainEvents;
|
||||
|
||||
public void ClearDomainEvents()
|
||||
{
|
||||
_domainEvents?.Clear();
|
||||
}
|
||||
|
||||
protected void AddDomainEvent(IDomainEvent domainEvent)
|
||||
{
|
||||
_domainEvents ??= [];
|
||||
|
||||
this._domainEvents.Add(domainEvent);
|
||||
}
|
||||
public IReadOnlyList<IDomainEvent> DomainEvents => _domainEvents;
|
||||
protected void CheckRule(IBusinessRule rule)
|
||||
{
|
||||
if (rule.IsBroken())
|
||||
{
|
||||
throw new BusinessRuleValidationException(rule);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
namespace Vizhvizh.BuildingBlocks.Domain.Exceptions;
|
||||
|
||||
public class BusinessRuleValidationException : Exception
|
||||
{
|
||||
public IBusinessRule BrokenRule { get; }
|
||||
|
||||
public string Details { get; }
|
||||
|
||||
public BusinessRuleValidationException(IBusinessRule brokenRule)
|
||||
: base(brokenRule.Message)
|
||||
{
|
||||
BrokenRule = brokenRule;
|
||||
this.Details = brokenRule.Message;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{BrokenRule.GetType().FullName}: {BrokenRule.Message}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Vizhvizh.BuildingBlocks.Domain.Exceptions;
|
||||
|
||||
public interface IBusinessRule
|
||||
{
|
||||
bool IsBroken();
|
||||
|
||||
string Message { get; }
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Vizhvizh.BuildingBlocks.Domain.Exceptions;
|
||||
|
||||
public class ValueObjectValidationException : Exception
|
||||
{
|
||||
public ValueObjectValidationException(string message)
|
||||
:base(message)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Vizhvizh.BuildingBlocks.Domain.Extensions;
|
||||
|
||||
public static class EnumExtensions
|
||||
{
|
||||
// Cache to store display names of enum values
|
||||
private static readonly ConcurrentDictionary<Enum, string> DisplayNameCache = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets an attribute on an enum field value.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the attribute to retrieve.</typeparam>
|
||||
/// <param name="enumValue">The enum value.</param>
|
||||
/// <returns>
|
||||
/// The attribute of the specified type or null.
|
||||
/// </returns>
|
||||
public static T GetAttributeOfType<T>(this Enum enumValue) where T : Attribute
|
||||
{
|
||||
var type = enumValue.GetType();
|
||||
// Use GetField to get the field info for the enum value
|
||||
var memInfo = type.GetField(enumValue.ToString(), BindingFlags.Public | BindingFlags.Static);
|
||||
|
||||
if (memInfo == null)
|
||||
return null;
|
||||
|
||||
// Retrieve the custom attributes of type T
|
||||
var attributes = memInfo.GetCustomAttributes<T>(false);
|
||||
return attributes.FirstOrDefault();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the enum display name.
|
||||
/// </summary>
|
||||
/// <param name="enumValue">The enum value.</param>
|
||||
/// <returns>
|
||||
/// Use <see cref="DisplayAttribute"/> if it exists.
|
||||
/// Otherwise, use the standard string representation.
|
||||
/// </returns>
|
||||
public static string GetDisplayName(this Enum enumValue)
|
||||
{
|
||||
// Retrieve the display name from the cache if it exists
|
||||
return DisplayNameCache.GetOrAdd(enumValue, e =>
|
||||
{
|
||||
// Get the DisplayAttribute
|
||||
var attribute = e.GetAttributeOfType<DisplayAttribute>();
|
||||
|
||||
// Return the DisplayAttribute name if it exists, otherwise return the enum's string representation
|
||||
return attribute == null ? e.ToString() : attribute.Name;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
|
||||
|
||||
namespace Vizhvizh.BuildingBlocks.Domain;
|
||||
|
||||
public interface IAggregateRoot
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
using Vizhvizh.BuildingBlocks.Domain.Contract;
|
||||
|
||||
namespace Vizhvizh.BuildingBlocks.Domain
|
||||
{
|
||||
public interface IDomainEvent : INotification
|
||||
{
|
||||
Guid Id { get; }
|
||||
|
||||
DateTime OccurredOn { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Vizhvizh.BuildingBlocks.Domain;
|
||||
|
||||
public interface IRepositoryBase<TDomain, TId>
|
||||
where TDomain : Entity<TId> where TId : ITypeIdValueBase
|
||||
{
|
||||
Task CreateAsync(TDomain entity, CancellationToken cancellationToken = default);
|
||||
Task UpdateAsync(TDomain entity, CancellationToken cancellationToken = default);
|
||||
Task DeleteAsync(TId id, CancellationToken cancellationToken = default);
|
||||
Task<TDomain> GetByIdAsync(TId id, CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Vizhvizh.BuildingBlocks.Domain;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
|
||||
public class IgnoreMemberAttribute : Attribute
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
namespace Vizhvizh.BuildingBlocks.Domain;
|
||||
|
||||
public abstract class TypedIdValueBase<T> : IEquatable<TypedIdValueBase<T>> , ITypeIdValueBase
|
||||
{
|
||||
public T Value { get; }
|
||||
|
||||
protected TypedIdValueBase(T value)
|
||||
{
|
||||
|
||||
if (EqualityComparer<T>.Default.Equals(value, default(T)))
|
||||
{
|
||||
throw new InvalidOperationException($"Id value cannot be empty ! : id is {value}");
|
||||
}
|
||||
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return obj is TypedIdValueBase<T> other && Equals(other);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Value!.GetHashCode();
|
||||
}
|
||||
|
||||
public bool Equals(TypedIdValueBase<T>? other)
|
||||
{
|
||||
return EqualityComparer<T>.Default.Equals(this.Value, other.Value);
|
||||
}
|
||||
|
||||
public static bool operator ==(TypedIdValueBase<T>? obj1, TypedIdValueBase<T>? obj2)
|
||||
{
|
||||
if (object.Equals(obj1, null))
|
||||
{
|
||||
if (object.Equals(obj2, null))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return obj1.Equals(obj2);
|
||||
}
|
||||
|
||||
public static bool operator !=(TypedIdValueBase<T>? x, TypedIdValueBase<T>? y)
|
||||
{
|
||||
return !(x == y);
|
||||
}
|
||||
}
|
||||
public interface ITypeIdValueBase { }
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
|
||||
|
||||
using System.Reflection;
|
||||
using Vizhvizh.BuildingBlocks.Domain.Exceptions;
|
||||
|
||||
namespace Vizhvizh.BuildingBlocks.Domain;
|
||||
|
||||
public abstract class ValueObject : IEquatable<ValueObject>
|
||||
{
|
||||
private List<PropertyInfo> _properties;
|
||||
|
||||
private List<FieldInfo> _fields;
|
||||
|
||||
public static bool operator ==(ValueObject? obj1, ValueObject? obj2)
|
||||
{
|
||||
if (object.Equals(obj1, null))
|
||||
{
|
||||
if (object.Equals(obj2, null))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return obj1.Equals(obj2);
|
||||
}
|
||||
|
||||
public static bool operator !=(ValueObject? obj1, ValueObject? obj2)
|
||||
{
|
||||
return !(obj1 == obj2);
|
||||
}
|
||||
|
||||
public bool Equals(ValueObject? obj)
|
||||
{
|
||||
return Equals(obj as object);
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (obj == null || GetType() != obj.GetType())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return GetProperties().All(p => PropertiesAreEqual(obj, p))
|
||||
&& GetFields().All(f => FieldsAreEqual(obj, f));
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
int hash = 17;
|
||||
foreach (var prop in GetProperties())
|
||||
{
|
||||
var value = prop.GetValue(this, null);
|
||||
hash = HashValue(hash, value);
|
||||
}
|
||||
|
||||
foreach (var field in GetFields())
|
||||
{
|
||||
var value = field.GetValue(this);
|
||||
hash = HashValue(hash, value);
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
protected static void CheckRule(IBusinessRule rule)
|
||||
{
|
||||
if (rule.IsBroken())
|
||||
{
|
||||
throw new BusinessRuleValidationException(rule);
|
||||
}
|
||||
}
|
||||
|
||||
private bool PropertiesAreEqual(object obj, PropertyInfo p)
|
||||
{
|
||||
return object.Equals(p.GetValue(this, null), p.GetValue(obj, null));
|
||||
}
|
||||
|
||||
private bool FieldsAreEqual(object obj, FieldInfo f)
|
||||
{
|
||||
return object.Equals(f.GetValue(this), f.GetValue(obj));
|
||||
}
|
||||
|
||||
private IEnumerable<PropertyInfo> GetProperties()
|
||||
{
|
||||
if (this._properties == null)
|
||||
{
|
||||
this._properties = GetType()
|
||||
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
|
||||
.Where(p => p.GetCustomAttribute(typeof(IgnoreMemberAttribute)) == null)
|
||||
.ToList();
|
||||
|
||||
// Not available in Core
|
||||
// !Attribute.IsDefined(p, typeof(IgnoreMemberAttribute))).ToList();
|
||||
}
|
||||
|
||||
return this._properties;
|
||||
}
|
||||
|
||||
private IEnumerable<FieldInfo> GetFields()
|
||||
{
|
||||
if (this._fields == null)
|
||||
{
|
||||
this._fields = GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
|
||||
.Where(p => p.GetCustomAttribute(typeof(IgnoreMemberAttribute)) == null)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
return this._fields;
|
||||
}
|
||||
|
||||
private int HashValue(int seed, object value)
|
||||
{
|
||||
var currentHash = value?.GetHashCode() ?? 0;
|
||||
|
||||
return (seed * 23) + currentHash;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user