Quick Reference
event payload는 publisher 내부 object를 통째로 넘기는 통로가 아니라, subscriber가 사건을 처리하는 데 필요한 사실의 계약입니다. command는 일을 요청하고 결과를 기다리며, notification은 state change 뒤에 발생한 사실을 알립니다.
| 계약 | 이름과 payload | 정할 것 |
|---|---|---|
| command | RequestPurchase(request) | return·failure·retry owner |
| in-process notification | InventoryChanged | publisher·subscription lifetime |
| queued message | RewardGranted | ordering·duplicate·acknowledgement |
| snapshot refresh | InventorySnapshotReady | version·replace/merge policy |
public readonly struct InventoryChanged
{
public readonly string ItemId;
public readonly int PreviousCount;
public readonly int CurrentCount;
public readonly string Reason;
public InventoryChanged(string itemId, int previousCount, int currentCount, string reason)
{
ItemId = itemId;
PreviousCount = previousCount;
CurrentCount = currentCount;
Reason = reason;
}
}payload의 범위
listener가 ID, old/new value, reason, tick/version만으로 처리할 수 있다면 mutable InventoryService, GameObject, raw database model까지 넣지 않습니다. receiver가 추가 query를 해도 되는지, event 시점의 snapshot이 필요한지에 따라 payload를 고릅니다. event가 delayed/queued될 수 있으면 scene object reference는 destroy 뒤 무효가 될 수 있으므로 stable ID나 immutable data를 우선합니다.
generic GameEvent { type, object, data }는 type switch·cast·숨은 required field를 늘립니다. 다만 모든 contract가 class 하나씩 필요하다는 뜻은 아닙니다. 같은 consumer·lifetime·delivery semantics를 가진 작은 family는 typed generic contract로 묶을 수 있습니다. 기준은 타입 수가 아니라 caller와 subscriber가 payload 의미를 compile time에 알 수 있는가입니다.
발생 시점과 delivery
event는 mutation 전인지 후인지 명시합니다. InventoryChanged가 subscriber에게 보일 때 source of truth는 이미 CurrentCount여야 합니다. 여러 mutation을 batch로 처리하면 이벤트마다 intermediate state를 보낼지, transaction 끝의 summary를 보낼지도 정합니다. C# event handler는 synchronous하므로 listener exception·re-entrant mutation·순서 의존을 core rule에 숨기지 않습니다.
network나 persistence queue는 C# event보다 강한 contract입니다. message ID, producer sequence, duplicate 처리, retry, cancel, observer log를 정하지 않고 Action을 큐처럼 쓰지 않습니다. 과거형 notification 이름과 동사형 command 이름을 구분하면 흐름을 읽기 쉽습니다.
contract test
publisher가 no listener일 때, listener가 disable/destroy될 때, 같은 message가 두 번 올 때, payload의 ID가 이미 삭제된 entity를 가리킬 때의 동작을 test합니다. payload field를 제거·변경할 때 어떤 subscriber가 깨지는지 source search와 contract test로 확인합니다.
넓은 payload는 미래 확장의 보험이 아니라 publisher 내부 구조를 배포하는 방식이 되기 쉽습니다. 전달 모델과 delivery 계약을 먼저 정하고, 그에 필요한 정보만 넣으세요.
참고 링크
2 sources