Init
This commit is contained in:
37
Prefab/Domain/Common/Entity.cs
Normal file
37
Prefab/Domain/Common/Entity.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
namespace Prefab.Domain.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Base implementation for aggregate entities that support domain events.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Entity identifier type.</typeparam>
|
||||
public abstract class Entity<T> : IEntity<T>, IHasDomainEvents
|
||||
{
|
||||
private readonly List<Event> _events = [];
|
||||
|
||||
/// <inheritdoc />
|
||||
public T Id { get; set; } = default!;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyCollection<Event> Events => _events.AsReadOnly();
|
||||
|
||||
/// <inheritdoc />
|
||||
public void AddEvent(Event e) => _events.Add(e);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RemoveEvent(Event e) => _events.Remove(e);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ClearEvents() => _events.Clear();
|
||||
|
||||
/// <summary>
|
||||
/// Generates a UUIDv7 identifier using the supplied timestamp, primarily for testing scenarios.
|
||||
/// </summary>
|
||||
/// <param name="timestamp">The timestamp used to seed the UUID generator.</param>
|
||||
/// <exception cref="InvalidOperationException">Thrown when the entity identifier type is not <see cref="Guid"/>.</exception>
|
||||
protected void GenerateIdWithTimestamp(DateTime timestamp)
|
||||
{
|
||||
Id = typeof(T) == typeof(Guid)
|
||||
? (T)(object)Guid.CreateVersion7(timestamp)
|
||||
: throw new InvalidOperationException("Cannot generate a UUIDv7 for a non-Guid entity.");
|
||||
}
|
||||
}
|
||||
25
Prefab/Domain/Common/EntityWithAudit.cs
Normal file
25
Prefab/Domain/Common/EntityWithAudit.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
namespace Prefab.Domain.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Interface representation of an entity. All entities that require auditing should implement this.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type that will represent the entity's ID field.</typeparam>
|
||||
public interface IEntityWithAudit<T> : IEntity<T>, IAudit
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IEntityWithAudit{T}" />
|
||||
public abstract class EntityWithAudit<T> : Entity<T>, IEntityWithAudit<T>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public Guid CreatedBy { get; init; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public DateTimeOffset CreatedOn { get; init; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Guid LastModifiedBy { get; init; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public DateTimeOffset LastModifiedOn { get; init; }
|
||||
}
|
||||
118
Prefab/Domain/Common/EntityWithAuditAndStatus.cs
Normal file
118
Prefab/Domain/Common/EntityWithAuditAndStatus.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Prefab.Data;
|
||||
|
||||
namespace Prefab.Domain.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an audited entity that also tracks status transitions such as deactivate and delete.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The identifier type.</typeparam>
|
||||
public interface IEntityWithAuditAndStatus<T> : IEntityWithAudit<T>, IStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the identifier of the user that deactivated the entity.
|
||||
/// </summary>
|
||||
Guid? InactivatedBy { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets when the entity was deactivated.
|
||||
/// </summary>
|
||||
DateTimeOffset? InactivatedOn { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the identifier of the user that soft-deleted the entity.
|
||||
/// </summary>
|
||||
Guid? DeletedBy { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets when the entity was soft-deleted.
|
||||
/// </summary>
|
||||
DateTimeOffset? DeletedOn { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the concurrency token used for optimistic locking.
|
||||
/// </summary>
|
||||
byte[] RowVersion { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base type for audited entities that support activation, deactivation, and soft-delete flows.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The identifier type.</typeparam>
|
||||
public abstract class EntityWithAuditAndStatus<T> : EntityWithAudit<T>, IEntityWithAuditAndStatus<T>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public AuditStatus AuditStatus => DeletedOn.HasValue
|
||||
? AuditStatus.Deleted
|
||||
: InactivatedOn.HasValue
|
||||
? AuditStatus.Inactive
|
||||
: AuditStatus.Active;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Guid? InactivatedBy { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public DateTimeOffset? InactivatedOn { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Guid? DeletedBy { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public DateTimeOffset? DeletedOn { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[Timestamp]
|
||||
public byte[] RowVersion { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Marks the entity as active by clearing any inactive markers.
|
||||
/// </summary>
|
||||
/// <param name="userId">Identifier of the user performing the change.</param>
|
||||
public virtual void Activate(Guid? userId = null)
|
||||
{
|
||||
if (InactivatedOn.HasValue)
|
||||
{
|
||||
InactivatedOn = null;
|
||||
InactivatedBy = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks the entity as inactive if it is not already inactive.
|
||||
/// </summary>
|
||||
/// <param name="userId">Identifier of the user performing the change.</param>
|
||||
public virtual void Deactivate(Guid? userId = null)
|
||||
{
|
||||
if (!InactivatedOn.HasValue)
|
||||
{
|
||||
InactivatedOn = DateTimeOffset.UtcNow;
|
||||
InactivatedBy = userId;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Soft deletes the entity when it has not already been deleted.
|
||||
/// </summary>
|
||||
/// <param name="userId">Identifier of the user performing the change.</param>
|
||||
public virtual void Delete(Guid? userId = null)
|
||||
{
|
||||
if (!DeletedOn.HasValue)
|
||||
{
|
||||
DeletedOn = DateTimeOffset.UtcNow;
|
||||
DeletedBy = userId;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restores a previously soft-deleted entity to an inactive state.
|
||||
/// </summary>
|
||||
/// <param name="userId">Identifier of the user performing the change.</param>
|
||||
public virtual void Restore(Guid? userId = null)
|
||||
{
|
||||
if (DeletedOn.HasValue)
|
||||
{
|
||||
DeletedOn = null;
|
||||
DeletedBy = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
17
Prefab/Domain/Common/EntityWithAuditAndStatusAndTenant.cs
Normal file
17
Prefab/Domain/Common/EntityWithAuditAndStatusAndTenant.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
namespace Prefab.Domain.Common;
|
||||
|
||||
/// <summary>
|
||||
/// All entities that require auditing, statuses and tenant information should use this.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type that will represent the entity's ID field.</typeparam>
|
||||
public interface IEntityWithAuditAndStatusAndTenant<T> : IEntityWithAuditAndStatus<T>, ITenant
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IEntityWithAuditAndStatusAndTenant{T}" />
|
||||
public abstract class EntityWithAuditAndStatusAndTenant<T> : EntityWithAuditAndStatus<T>,
|
||||
IEntityWithAuditAndStatusAndTenant<T>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public Guid TenantId { get; set; }
|
||||
}
|
||||
16
Prefab/Domain/Common/EntityWithAuditAndTenant.cs
Normal file
16
Prefab/Domain/Common/EntityWithAuditAndTenant.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
namespace Prefab.Domain.Common;
|
||||
|
||||
/// <summary>
|
||||
/// All entities that require auditing, statuses and tenant information should use this.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type that will represent the entity's ID field.</typeparam>
|
||||
public interface IEntityWithAuditAndTenant<T> : IEntityWithAudit<T>, ITenant
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IEntityWithAuditAndTenant{T}" />
|
||||
public class EntityWithAuditAndTenant<T> : EntityWithAudit<T>, IEntityWithAuditAndTenant<T>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public Guid TenantId { get; set; }
|
||||
}
|
||||
17
Prefab/Domain/Common/EntityWithTenant.cs
Normal file
17
Prefab/Domain/Common/EntityWithTenant.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
namespace Prefab.Domain.Common;
|
||||
|
||||
/// <summary>
|
||||
/// All entities that require tenant information should use this. Use
|
||||
/// <see cref="EntityWithAuditAndStatusAndTenant{T}" />
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type that will represent the entity's ID field.</typeparam>
|
||||
public interface IEntityWithTenant<T> : IEntity<T>, ITenant
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IEntityWithTenant{T}" />
|
||||
public abstract class EntityWithTenant<T> : Entity<T>, IEntityWithTenant<T>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public Guid TenantId { get; set; }
|
||||
}
|
||||
22
Prefab/Domain/Common/Event.cs
Normal file
22
Prefab/Domain/Common/Event.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Prefab.Base;
|
||||
|
||||
namespace Prefab.Domain.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Base type for domain events captured during aggregate processing.
|
||||
/// </summary>
|
||||
[NotMapped]
|
||||
public abstract record Event : IEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the event has been dispatched to external transports.
|
||||
/// </summary>
|
||||
public bool IsPublished { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the timestamp for when the event occurred.
|
||||
/// </summary>
|
||||
public DateTimeOffset DateOccurred { get; protected set; } = DateTimeOffset.UtcNow;
|
||||
}
|
||||
|
||||
17
Prefab/Domain/Common/IAudit.cs
Normal file
17
Prefab/Domain/Common/IAudit.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
namespace Prefab.Domain.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Describes audit metadata captured on entities.
|
||||
/// </summary>
|
||||
public interface IAudit : ICreated
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the identifier of the user that last updated the entity.
|
||||
/// </summary>
|
||||
Guid LastModifiedBy { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets when the entity was last updated.
|
||||
/// </summary>
|
||||
DateTimeOffset LastModifiedOn { get; }
|
||||
}
|
||||
17
Prefab/Domain/Common/ICreated.cs
Normal file
17
Prefab/Domain/Common/ICreated.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
namespace Prefab.Domain.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Captures creation metadata for auditable entities.
|
||||
/// </summary>
|
||||
public interface ICreated
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the identifier of the user that created the entity.
|
||||
/// </summary>
|
||||
Guid CreatedBy { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets when the entity was created.
|
||||
/// </summary>
|
||||
DateTimeOffset CreatedOn { get; }
|
||||
}
|
||||
13
Prefab/Domain/Common/IEntity.cs
Normal file
13
Prefab/Domain/Common/IEntity.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
namespace Prefab.Domain.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Interface representation of an entity. All entities should implement this.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type that will represent the entity's ID field.</typeparam>
|
||||
public interface IEntity<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the ID of the entity.
|
||||
/// </summary>
|
||||
T Id { get; set; }
|
||||
}
|
||||
29
Prefab/Domain/Common/IHasDomainEvents.cs
Normal file
29
Prefab/Domain/Common/IHasDomainEvents.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using Prefab.Base;
|
||||
|
||||
namespace Prefab.Domain.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Interface representation of an event in the domain model.
|
||||
/// </summary>
|
||||
public interface IHasDomainEvents : IEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the events of the entity.
|
||||
/// </summary>
|
||||
IReadOnlyCollection<Event> Events { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Adds an event to the entity.
|
||||
/// </summary>
|
||||
public void AddEvent(Event e);
|
||||
|
||||
/// <summary>
|
||||
/// Removes an event from the entity.
|
||||
/// </summary>
|
||||
public void RemoveEvent(Event e);
|
||||
|
||||
/// <summary>
|
||||
/// Clears all events from the entity.
|
||||
/// </summary>
|
||||
public void ClearEvents();
|
||||
}
|
||||
8
Prefab/Domain/Common/IRoot.cs
Normal file
8
Prefab/Domain/Common/IRoot.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace Prefab.Domain.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Interface to represent a marker on an entity, informing that the entity is considered an aggregate root.
|
||||
/// </summary>
|
||||
public interface IRoot
|
||||
{
|
||||
}
|
||||
14
Prefab/Domain/Common/IStatus.cs
Normal file
14
Prefab/Domain/Common/IStatus.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using Prefab.Data;
|
||||
|
||||
namespace Prefab.Domain.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Interface representation of an entity's status.
|
||||
/// </summary>
|
||||
public interface IStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the current status of the entity.
|
||||
/// </summary>
|
||||
public AuditStatus AuditStatus { get; }
|
||||
}
|
||||
12
Prefab/Domain/Common/ITenant.cs
Normal file
12
Prefab/Domain/Common/ITenant.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace Prefab.Domain.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Marks an entity as belonging to a tenant.
|
||||
/// </summary>
|
||||
public interface ITenant
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the unique tenant identifier.
|
||||
/// </summary>
|
||||
Guid TenantId { get; }
|
||||
}
|
||||
Reference in New Issue
Block a user