OOP 커뮤니티에는 클래스 생성자가 객체를 부분적으로 또는 완전히 초기화되지 않은 상태로 두지 말아야한다는 광범위한 동의가있는 것으로 보입니다.
"초기화"는 무엇을 의미합니까? 대략적으로 말하자면 새로 생성 된 객체를 모든 클래스 불변 값이 유지되는 상태로 만드는 원자 프로세스입니다. 객체에 대해 가장 먼저 발생해야하며 (객체 당 한 번만 실행해야 함) 초기화되지 않은 객체를 잡을 수있는 것은 없습니다. (따라서 클래스 생성자에서 바로 객체 초기화를 수행하라는 빈번한 조언이 있습니다. 같은 이유로,
Initialize
메소드는 원 자성을 분리하고 아직 그렇지 않은 객체를 가져 와서 사용할 수있게하기 때문에 종종 눈살을 찌푸립니다. 잘 정의 된 상태로)
문제 : CQRS가 이벤트 소싱 (CQRS + ES)과 결합 될 때 개체의 모든 상태 변경이 정렬 된 일련의 이벤트 (이벤트 스트림)에서 포착되는 경우, 개체가 실제로 완전히 초기화 된 상태에 도달하는 시점이 궁금합니다. 클래스 생성자의 끝에서 또는 첫 번째 이벤트가 객체에 적용된 후?
참고 : "aggregate root"라는 용어는 사용하지 않습니다. 원하는 경우 "object"를 읽을 때마다이를 대체하십시오.
토론의 예 : 각 객체가 불투명 한 Id
값으로 고유하게 식별된다고 가정합니다 (GUID 생각). 해당 객체의 상태 변경을 나타내는 이벤트 스트림은 동일한 Id
값으로 이벤트 저장소에서 식별 될 수 있습니다 (올바른 이벤트 순서에 대해 걱정하지 마십시오).
interface IEventStore
{
IEnumerable<IEvent> GetEventsOfObject(Id objectId);
}
Customer
그리고 두 개의 객체 유형이 있다고 가정합니다 ShoppingCart
. 집중하자 ShoppingCart
: 쇼핑 카트가 비어 있으며 정확히 하나의 고객과 연결되어 있어야합니다. 마지막 비트는 클래스 불변입니다. a ShoppingCart
와 연관되지 않은 객체가 Customer
유효하지 않은 상태입니다.
전통적인 OOP에서는 생성자에서이를 모델링 할 수 있습니다.
partial class ShoppingCart
{
public Id Id { get; private set; }
public Customer Customer { get; private set; }
public ShoppingCart(Id id, Customer customer)
{
this.Id = id;
this.Customer = customer;
}
}
그러나 지연 된 초기화로 끝나지 않고 CQRS + ES에서이를 모델링하는 방법을 잃어 버렸습니다. 이 간단한 초기화 비트는 사실상 상태 변경이므로 이벤트로 모델링 할 필요는 없습니까?
partial class CreatedEmptyShoppingCart
{
public ShoppingCartId { get; private set; }
public CustomerId { get; private set; }
}
// Note: `ShoppingCartId` is not actually required, since that Id must be
// known in advance in order to fetch the event stream from the event store.
이것은 분명히 모든 ShoppingCart
객체의 이벤트 스트림 에서 첫 번째 이벤트 여야 하며 해당 객체는 이벤트가 적용된 후에 만 초기화됩니다.
따라서 초기화가 이벤트 스트림 "재생"의 일부가되면 ( Customer
객체 또는 ShoppingCart
객체 또는 해당 문제에 대한 다른 객체 유형에 관계없이 동일하게 작동하는 매우 일반적인 프로세스입니다 )…
- 생성자가 매개 변수가없고 아무것도하지 않아야합니까? 모든 작업을 일부
void Apply(CreatedEmptyShoppingCart)
방법으로 남겨 두어야합니까 (frowned-upon과 거의 동일Initialize()
)? - 또는 생성자가 이벤트 스트림을 수신하여 재생해야합니까 (초기화를 다시 수행하지만 각 클래스의 생성자에는 동일한 일반 "재생 및 적용"논리, 즉 원치 않는 코드 복제가 포함됨을 의미 함)?
- 또는 객체를 올바르게 초기화하는 전통적인 OOP 생성자 (위 그림 참조)와 첫 번째 이벤트 를 제외한 모든 이벤트가
void Apply(…)
있어야합니까?
나는 완전히 작동하는 데모 구현을 제공하기위한 답을 기대하지는 않는다. 누군가가 내 추론이 결함 또는 개체 초기화가 정말 있는지 위치를 설명 할 수 있다면 나는 이미 아주 행복 할 것 입니다 가장 CQRS + ES 구현에 "고통 지점".
Initialize
메소드) 차지했을 것처럼 팩토리가 퍼블릭 인터페이스에서 본질적으로 같은 위치에있는 것처럼 들립니다 . 그것은 당신의 공장이 어떻게 생겼을까 요?