강력한 형식의 Id 를 구현하는 EntityBase

최근에 “강력한 형식의 Id” 라는 주제가 눈에 많이 띄더군요.

하여, Entity 클래스를 도메인 클래스로 혼용할 수 있도록 EntityBase를 정의해봤습니다.

어떤가 한 번 봐주세요.

정의

public readonly record struct Id<T>(Guid Value)
{
    public static Id<T> NewId => new(Guid.NewGuid());
    public static readonly Id<T> Empty = new(Guid.Empty);
}
public abstract class EntityBase<T> where T : class
{
    protected Id<T> Id { get; } = Id<T>.NewId;

    public override bool Equals(object? obj) => 
        obj is EntityBase<T> entity && Id.Equals(entity.Id);

    public override int GetHashCode() =>
        Id.GetHashCode();    
}

특징

  • 프로세스 간 안전한 정체성 비교
  • 인스턴스를 Dictionary의 Key로 안전하게 사용
  • 간결한 해시 계산
  • Id와 관련한 실수의 여지 없음.
  • 외부 도메인 로직에서 Id 가 안 보임.

예제

class A : EntityBase<A> { }
class B : EntityBase<B> { }
var a = new A();
var b = new B();
Console.WriteLine($"var a = new A();");
Console.WriteLine($"var b = new B();");
Console.WriteLine($"a.Equals(b) = {a.Equals(b)}");

var aa = a;
Console.WriteLine($"var aa = a;");
Console.WriteLine($"a.Equals(aa) = {a.Equals(aa)}");

aa = new A();
Console.WriteLine($"aa = new A();");
Console.WriteLine($"a.Equals(aa) = {a.Equals(aa)}");

a = _dbContext.Set<A>.First();
aa = _dbContext.Set<A>.First();

Console.WriteLine($"a = _dbContext.Set<A>.First();");
Console.WriteLine($"aa = _dbContext.Set<A>.First();");
Console.WriteLine($"a.Equals(aa) = {a.Equals(aa)}");

결과

var a = new A();
var b = new B();
a.Equals(b) = False
var aa = a;
a.Equals(aa) = True
aa = new A();
a.Equals(aa) = False
a = _dbContext.Set<A>.First();
aa = _dbContext.Set<A>.First();
a.Equals(aa) = True

8 Likes

record 타입을 데이터를 전달하는 모델 정도로만 사용했었는데
이렇게 사용할 수도 있네요. 잘 보고 갑니다~

4 Likes