Tampaknya ada kesepakatan luas dalam komunitas OOP bahwa konstruktor kelas tidak boleh meninggalkan objek sebagian atau bahkan sepenuhnya diinisialisasi.
Apa yang saya maksud dengan "inisialisasi"? Secara kasar, proses atom yang membawa objek yang baru dibuat ke dalam keadaan di mana semua kelas invariannya bertahan. Ini harus menjadi hal pertama yang terjadi pada suatu objek, (seharusnya hanya berjalan sekali per objek,) dan tidak ada yang diizinkan untuk mendapatkan objek yang tidak diinisialisasi. (Demikianlah saran yang sering dilakukan untuk melakukan inisialisasi objek tepat di konstruktor kelas. Untuk alasan yang sama,
Initialize
metode sering disukai, karena ini memecah atomicity dan memungkinkan untuk mendapatkan, dan menggunakan, objek yang belum dalam kondisi terdefinisi dengan baik.)
Masalah: Ketika CQRS dikombinasikan dengan event sourcing (CQRS + ES), di mana semua perubahan keadaan suatu objek terperangkap dalam serangkaian peristiwa yang terurut (aliran acara), saya bertanya-tanya kapan suatu objek benar-benar mencapai kondisi diinisialisasi penuh: Di akhir konstruktor kelas, atau setelah peristiwa pertama diterapkan pada objek?
Catatan: Saya menahan diri untuk tidak menggunakan istilah "root agregat". Jika Anda suka, gantilah setiap kali Anda membaca "objek".
Contoh untuk diskusi: Asumsikan bahwa setiap objek diidentifikasi secara unik oleh beberapa Id
nilai buram (pikirkan GUID). Aliran peristiwa yang mewakili perubahan keadaan objek itu dapat diidentifikasi di toko peristiwa dengan nilai yang sama Id
: (Jangan khawatir tentang urutan peristiwa yang benar.)
interface IEventStore
{
IEnumerable<IEvent> GetEventsOfObject(Id objectId);
}
Asumsikan lebih lanjut bahwa ada dua jenis objek Customer
dan ShoppingCart
. Mari kita fokus pada ShoppingCart
: Ketika dibuat, kereta belanja kosong dan harus dikaitkan dengan tepat satu pelanggan. Bit terakhir itu adalah kelas invarian: ShoppingCart
Objek yang tidak terkait dengan a Customer
dalam keadaan tidak valid.
Dalam OOP tradisional, orang mungkin memodelkan ini dalam konstruktor:
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;
}
}
Namun saya bingung bagaimana memodelkan ini dalam CQRS + ES tanpa berakhir dengan inisialisasi ditangguhkan. Karena inisialisasi sederhana ini secara efektif merupakan perubahan keadaan, bukankah harus dimodelkan sebagai suatu peristiwa ?:
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.
Ini jelas harus menjadi peristiwa pertama dalam ShoppingCart
aliran peristiwa objek apa pun , dan objek itu hanya akan diinisialisasi setelah acara diterapkan.
Jadi jika inisialisasi menjadi bagian dari "pemutaran" aliran acara (yang merupakan proses yang sangat umum yang kemungkinan akan bekerja sama, baik untuk Customer
objek atau ShoppingCart
objek atau jenis objek lainnya dalam hal ini) ...
- Haruskah konstruktor menjadi parameter-kurang dan tidak melakukan apa-apa, membiarkan semua pekerjaan untuk beberapa
void Apply(CreatedEmptyShoppingCart)
metode (yang hampir sama dengan yang disukaiInitialize()
)? - Atau haruskah konstruktor menerima aliran peristiwa dan memutarnya (yang membuat inisialisasi atom lagi, tetapi berarti konstruktor masing-masing kelas berisi logika generik yang sama "putar & terapkan", yaitu duplikasi kode yang tidak diinginkan)?
- Atau haruskah ada konstruktor OOP tradisional (seperti yang ditunjukkan di atas) yang menginisialisasi objek dengan benar, dan kemudian semua peristiwa tetapi yang pertama
void Apply(…)
terkait dengannya?
Saya tidak mengharapkan jawaban untuk menyediakan implementasi demo yang berfungsi penuh; Saya sudah sangat senang jika seseorang bisa menjelaskan di mana alasan saya cacat, atau apakah inisialisasi objek benar - benar merupakan "titik nyeri" di sebagian besar implementasi CQRS + ES.
Initialize
dihuni oleh konstruktor agregat (+ mungkin suatu metode). Itu mengarahkan saya ke pertanyaan, seperti apa pabrik Anda nantinya?