ดูเหมือนจะมีข้อตกลงอย่างกว้างขวางในชุมชน OOP ที่ตัวสร้างคลาสไม่ควรปล่อยให้วัตถุบางส่วนหรือไม่เตรียมการอย่างเต็มที่
ฉันหมายถึงอะไรโดย "การเริ่มต้น"? ประมาณพูดที่อะตอมกระบวนการที่จะนำวัตถุที่สร้างขึ้นใหม่ในสภาพที่ทั้งหมดของค่าคงที่ระดับนี้ถือเป็น มันควรเป็นสิ่งแรกที่เกิดขึ้นกับวัตถุ (ควรเรียกใช้เพียงครั้งเดียวต่อวัตถุ) และไม่ควรอนุญาตสิ่งใด ๆ ให้จับวัตถุที่ยังไม่ได้เริ่มต้น (ดังนั้นคำแนะนำบ่อยครั้งในการดำเนินการเริ่มต้นวัตถุในคลาส Constructor ด้วยเหตุผลเดียวกัน
Initialize
วิธีการที่มักจะขมวดคิ้วเมื่อเหล่านี้แตกอะตอมและทำให้เป็นไปได้ที่จะได้รับและใช้วัตถุที่ยังไม่ได้ อยู่ในสถานะที่กำหนดไว้อย่างดี)
ปัญหา:เมื่อรวม CQRS กับการจัดหาเหตุการณ์ (CQRS + ES) ซึ่งการเปลี่ยนแปลงสถานะทั้งหมดของวัตถุถูกจับในชุดลำดับเหตุการณ์ (กระแสเหตุการณ์) ที่สั่งไว้ฉันสงสัยว่าเมื่อวัตถุมาถึงสถานะเริ่มต้นเต็มที่: ในตอนท้ายของตัวสร้างคลาสหรือหลังจากเหตุการณ์แรกถูกนำไปใช้กับวัตถุหรือไม่
หมายเหตุ:ฉันไม่ต้องการใช้คำว่า "มวลรวมราก" หากคุณต้องการให้แทนที่เมื่อใดก็ตามที่คุณอ่าน "วัตถุ"
ตัวอย่างสำหรับการอภิปราย:สมมติว่าแต่ละวัตถุมีการระบุId
ค่าทึบแสงบางอย่าง(คิดว่า GUID) กระแสเหตุการณ์ที่เป็นตัวแทนของการเปลี่ยนแปลงสถานะของวัตถุนั้นสามารถระบุได้ในที่เก็บเหตุการณ์ด้วยId
ค่าเดียวกัน: (ไม่ต้องกังวลเกี่ยวกับลำดับเหตุการณ์ที่ถูกต้อง)
interface IEventStore
{
IEnumerable<IEvent> GetEventsOfObject(Id objectId);
}
สมมติต่อไปว่ามีสองประเภทวัตถุและCustomer
ShoppingCart
มามุ่งเน้นที่ShoppingCart
: เมื่อสร้างแล้วตะกร้าสินค้านั้นว่างเปล่าและต้องเชื่อมโยงกับลูกค้าหนึ่งราย บิตสุดท้ายนั้นเป็นค่าคงที่ของคลาส: ShoppingCart
วัตถุที่ไม่เกี่ยวข้องกับ a 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-onInitialize()
)? - หรือผู้สร้างควรได้รับกระแสเหตุการณ์และเล่นกลับ (ซึ่งทำให้การเริ่มต้นอะตอมอีกครั้ง แต่หมายความว่าคอนสตรัคเตอร์ของคลาสแต่ละคลาสมีตรรกะ "การเล่นและใช้งาน" ทั่วไปเหมือนกันเช่นการทำซ้ำโค้ดที่ไม่ต้องการ)
- หรือควรจะมีทั้งตัวสร้าง OOP แบบดั้งเดิม (ดังที่แสดงด้านบน) ที่เริ่มต้นวัตถุอย่างถูกต้องและจากนั้นเหตุการณ์ทั้งหมด แต่เหตุการณ์แรกจะถูก
void Apply(…)
ผูกไว้กับมัน?
ฉันไม่คาดหวังคำตอบเพื่อให้การสาธิตการทำงานอย่างสมบูรณ์; ฉันมีความสุขมากถ้ามีใครสามารถอธิบายได้ว่าเหตุผลของฉันมีข้อบกพร่องหรือการเริ่มต้นวัตถุเป็น "จุดปวด" ในการใช้งาน CQRS + ES หรือไม่
Initialize
วิธีการ) จะครอบครอง นั่นทำให้ฉันสงสัยว่าโรงงานของคุณมีลักษณะอย่างไร