# Architecture Standard: DDD Value Objects

## Overview
Value objects have no identity — equality is based on their attribute values. They are immutable.

## Value Object Base Class
```csharp
public abstract class ValueObject
{
    protected abstract IEnumerable<object?> GetEqualityComponents();

    public override bool Equals(object? obj)
    {
        if (obj is null || obj.GetType() != GetType()) return false;
        return GetEqualityComponents().SequenceEqual(((ValueObject)obj).GetEqualityComponents());
    }

    public override int GetHashCode()
        => GetEqualityComponents().Aggregate(0, (hash, c) => HashCode.Combine(hash, c?.GetHashCode() ?? 0));

    public static bool operator ==(ValueObject? a, ValueObject? b) => a?.Equals(b) ?? b is null;
    public static bool operator !=(ValueObject? a, ValueObject? b) => !(a == b);
}
```

## Money Value Object
```csharp
public sealed class Money : ValueObject
{
    public decimal Amount { get; }
    public string Currency { get; }

    private Money(decimal amount, string currency)
    {
        Amount = amount;
        Currency = currency.ToUpper();
    }

    public static Money Of(decimal amount, string currency = "BRL")
    {
        if (amount < 0) throw new DomainException("Money amount cannot be negative");
        if (string.IsNullOrWhiteSpace(currency)) throw new DomainException("Currency required");
        return new Money(amount, currency);
    }

    public Money Add(Money other)
    {
        if (Currency != other.Currency)
            throw new DomainException($"Cannot add {Currency} and {other.Currency}");
        return new Money(Amount + other.Amount, Currency);
    }

    public Money Multiply(decimal factor)
        => new(Amount * factor, Currency);

    public override string ToString() => $"{Amount:F2} {Currency}";

    protected override IEnumerable<object?> GetEqualityComponents()
    {
        yield return Amount;
        yield return Currency;
    }
}
```

## Address Value Object
```csharp
public sealed class Address : ValueObject
{
    public string Street { get; }
    public string City { get; }
    public string State { get; }
    public string ZipCode { get; }
    public string Country { get; }

    public Address(string street, string city, string state, string zipCode, string country)
    {
        Street = street ?? throw new DomainException("Street required");
        City = city ?? throw new DomainException("City required");
        State = state ?? throw new DomainException("State required");
        ZipCode = zipCode ?? throw new DomainException("ZipCode required");
        Country = country ?? throw new DomainException("Country required");
    }

    protected override IEnumerable<object?> GetEqualityComponents()
    {
        yield return Street;
        yield return City;
        yield return State;
        yield return ZipCode;
        yield return Country;
    }
}
```

## EF Core Configuration (Owned Types)
```csharp
public class OrderConfiguration : IEntityTypeConfiguration<Order>
{
    public void Configure(EntityTypeBuilder<Order> builder)
    {
        // Embedded in same table (no separate table)
        builder.OwnsOne(o => o.ShippingAddress, addr =>
        {
            addr.Property(a => a.Street).HasMaxLength(200).IsRequired();
            addr.Property(a => a.City).HasMaxLength(100).IsRequired();
            addr.Property(a => a.ZipCode).HasMaxLength(20).IsRequired();
        });

        // Money: stored as separate Amount + Currency columns
        builder.OwnsOne(o => o.Total, money =>
        {
            money.Property(m => m.Amount).HasColumnType("decimal(18,2)").IsRequired();
            money.Property(m => m.Currency).HasMaxLength(3).IsRequired();
        });
    }
}
```

## Rules
- Value objects are always immutable — no setters, return new instance from methods
- Use `record` types only when they don't need to inherit from `ValueObject` base class
- Never use value objects as EF Core entity (no `Id` column)
- Never create value objects with invalid state — validate in constructor/factory
