first commit

This commit is contained in:
2026-02-26 14:04:18 +07:00
parent 57ac80a666
commit 4b7236493f
92 changed files with 4999 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
namespace MyNewProjectName.Domain.Common;
/// <summary>
/// Base entity with audit properties
/// </summary>
public abstract class AuditableEntity : BaseEntity
{
public DateTime CreatedAt { get; set; }
public string? CreatedBy { get; set; }
public DateTime? UpdatedAt { get; set; }
public string? UpdatedBy { get; set; }
}

View File

@@ -0,0 +1,36 @@
namespace MyNewProjectName.Domain.Common;
/// <summary>
/// Base entity with common properties for all entities
/// </summary>
public abstract class BaseEntity
{
public Guid Id { get; set; }
private readonly List<IDomainEvent> _domainEvents = new();
public IReadOnlyCollection<IDomainEvent> DomainEvents => _domainEvents.AsReadOnly();
public void AddDomainEvent(IDomainEvent domainEvent)
{
_domainEvents.Add(domainEvent);
}
public void RemoveDomainEvent(IDomainEvent domainEvent)
{
_domainEvents.Remove(domainEvent);
}
public void ClearDomainEvents()
{
_domainEvents.Clear();
}
}
/// <summary>
/// Marker interface for domain events
/// </summary>
public interface IDomainEvent
{
DateTime OccurredOn { get; }
}

View File

@@ -0,0 +1,11 @@
namespace MyNewProjectName.Domain.Common;
/// <summary>
/// Interface for soft delete functionality
/// </summary>
public interface ISoftDelete
{
bool IsDeleted { get; set; }
DateTime? DeletedAt { get; set; }
string? DeletedBy { get; set; }
}

View File

@@ -0,0 +1,13 @@
using MyNewProjectName.Domain.Common;
namespace MyNewProjectName.Domain.Entities;
/// <summary>
/// Sample entity - Replace with your actual entity
/// </summary>
public class SampleEntity : AuditableEntity
{
public string Name { get; set; } = string.Empty;
public string? Description { get; set; }
public bool IsActive { get; set; } = true;
}

View File

@@ -0,0 +1,11 @@
using MyNewProjectName.Domain.Common;
namespace MyNewProjectName.Domain.Events;
/// <summary>
/// Base domain event
/// </summary>
public abstract class BaseDomainEvent : IDomainEvent
{
public DateTime OccurredOn { get; } = DateTime.UtcNow;
}

View File

@@ -0,0 +1,21 @@
namespace MyNewProjectName.Domain.Exceptions;
/// <summary>
/// Base exception for domain layer
/// </summary>
public class DomainException : Exception
{
public DomainException()
{
}
public DomainException(string message)
: base(message)
{
}
public DomainException(string message, Exception innerException)
: base(message, innerException)
{
}
}

View File

@@ -0,0 +1,27 @@
namespace MyNewProjectName.Domain.Exceptions;
/// <summary>
/// Exception thrown when an entity is not found
/// </summary>
public class NotFoundException : DomainException
{
public NotFoundException()
: base()
{
}
public NotFoundException(string message)
: base(message)
{
}
public NotFoundException(string message, Exception innerException)
: base(message, innerException)
{
}
public NotFoundException(string name, object key)
: base($"Entity \"{name}\" ({key}) was not found.")
{
}
}

View File

@@ -0,0 +1,27 @@
namespace MyNewProjectName.Domain.Exceptions;
/// <summary>
/// Exception thrown when validation fails
/// </summary>
public class ValidationException : DomainException
{
public IDictionary<string, string[]> Errors { get; }
public ValidationException()
: base("One or more validation failures have occurred.")
{
Errors = new Dictionary<string, string[]>();
}
public ValidationException(string message)
: base(message)
{
Errors = new Dictionary<string, string[]>();
}
public ValidationException(IDictionary<string, string[]> errors)
: base("One or more validation failures have occurred.")
{
Errors = errors;
}
}

View File

@@ -0,0 +1,24 @@
using System.Linq.Expressions;
using MyNewProjectName.Domain.Common;
namespace MyNewProjectName.Domain.Interfaces;
/// <summary>
/// Generic repository interface
/// </summary>
/// <typeparam name="T">Entity type</typeparam>
public interface IRepository<T> where T : BaseEntity
{
Task<T?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default);
Task<IEnumerable<T>> GetAllAsync(CancellationToken cancellationToken = default);
Task<IEnumerable<T>> FindAsync(Expression<Func<T, bool>> predicate, CancellationToken cancellationToken = default);
Task<T?> FirstOrDefaultAsync(Expression<Func<T, bool>> predicate, CancellationToken cancellationToken = default);
Task<bool> AnyAsync(Expression<Func<T, bool>> predicate, CancellationToken cancellationToken = default);
Task<int> CountAsync(Expression<Func<T, bool>>? predicate = null, CancellationToken cancellationToken = default);
Task AddAsync(T entity, CancellationToken cancellationToken = default);
Task AddRangeAsync(IEnumerable<T> entities, CancellationToken cancellationToken = default);
void Update(T entity);
void UpdateRange(IEnumerable<T> entities);
void Remove(T entity);
void RemoveRange(IEnumerable<T> entities);
}

View File

@@ -0,0 +1,12 @@
namespace MyNewProjectName.Domain.Interfaces;
/// <summary>
/// Unit of Work pattern interface
/// </summary>
public interface IUnitOfWork : IDisposable
{
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
Task BeginTransactionAsync(CancellationToken cancellationToken = default);
Task CommitTransactionAsync(CancellationToken cancellationToken = default);
Task RollbackTransactionAsync(CancellationToken cancellationToken = default);
}

View File

@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,43 @@
namespace MyNewProjectName.Domain.ValueObjects;
/// <summary>
/// Base class for value objects
/// </summary>
public abstract class ValueObject
{
protected abstract IEnumerable<object?> GetEqualityComponents();
public override bool Equals(object? obj)
{
if (obj == null || obj.GetType() != GetType())
{
return false;
}
var other = (ValueObject)obj;
return GetEqualityComponents().SequenceEqual(other.GetEqualityComponents());
}
public override int GetHashCode()
{
return GetEqualityComponents()
.Select(x => x?.GetHashCode() ?? 0)
.Aggregate((x, y) => x ^ y);
}
public static bool operator ==(ValueObject? left, ValueObject? right)
{
if (left is null && right is null)
return true;
if (left is null || right is null)
return false;
return left.Equals(right);
}
public static bool operator !=(ValueObject? left, ValueObject? right)
{
return !(left == right);
}
}