# Architecture Standard: DDD Entities

## Overview
Entities have identity — two entities with the same ID are the same, regardless of their attribute values.

## Entity Base Class
```csharp
public abstract class Entity
{
    public Guid Id { get; protected set; } = Guid.NewGuid();
    public DateTime CreatedAt { get; protected set; } = DateTime.UtcNow;
    public DateTime? UpdatedAt { get; protected set; }
    public bool IsDeleted { get; private set; }
    public DateTime? DeletedAt { get; private set; }

    // Identity equality (not attribute equality)
    public override bool Equals(object? obj)
        => obj is Entity other && Id == other.Id;

    public override int GetHashCode() => Id.GetHashCode();
    public static bool operator ==(Entity? a, Entity? b) => a?.Equals(b) ?? b is null;
    public static bool operator !=(Entity? a, Entity? b) => !(a == b);

    // Soft delete support
    public void SoftDelete()
    {
        IsDeleted = true;
        DeletedAt = DateTime.UtcNow;
    }
}
```

## Entity Lifecycle
```csharp
// Entities are created through factory methods (never bare constructors from outside the aggregate)
public class OrderItem : Entity
{
    private OrderItem() { } // Private for EF Core

    internal static OrderItem Create(Guid productId, string productName, decimal price, int quantity)
    {
        if (price <= 0) throw new DomainException("Price must be positive");
        if (quantity <= 0) throw new DomainException("Quantity must be positive");

        return new OrderItem
        {
            ProductId = productId,
            ProductName = productName,
            UnitPrice = price,
            Quantity = quantity
        };
    }

    public Guid ProductId { get; private set; }
    public string ProductName { get; private set; } = string.Empty;
    public decimal UnitPrice { get; private set; }
    public int Quantity { get; private set; }
    public decimal Subtotal => UnitPrice * Quantity;

    internal void IncreaseQuantity(int additionalQuantity)
    {
        if (additionalQuantity <= 0) throw new DomainException("Quantity must be positive");
        Quantity += additionalQuantity;
        UpdatedAt = DateTime.UtcNow;
    }
}
```

## EF Core Mapping

### Required Conventions
```csharp
builder.HasKey(e => e.Id);
builder.Property(e => e.CreatedAt).IsRequired();

// Soft delete filter (apply globally)
builder.HasQueryFilter(e => !e.IsDeleted);

// All string properties: max length
builder.Property(e => e.Name)
    .IsRequired()
    .HasMaxLength(200);
```

### Inheritance (TPH by default)
```csharp
// Table Per Hierarchy (single table with discriminator)
builder.HasDiscriminator<string>("Type")
    .HasValue<ProductOrder>("product")
    .HasValue<ServiceOrder>("service");
```

## Entity vs Value Object Decision
| Use Entity When | Use Value Object When |
|----------------|----------------------|
| Has lifecycle (created, updated, deleted) | Immutable by definition |
| Identity matters (two objects with same data are different) | Equality by value |
| Needs to be tracked in a repository | Owned by an entity (no separate table) |
| Examples: User, Order, Product | Examples: Money, Address, DateRange |
