
Baru-baru ini, di podcast Zinc Prod , saya dan teman-teman membahas pola CQRS / ES dan beberapa fitur penerapannya di Elixir. Karena Saya menggunakan Laravel dalam pekerjaan saya, adalah dosa untuk tidak menyelidiki Internet dan tidak menemukan bagaimana Anda dapat menyesap pendekatan ini dalam ekosistem kerangka kerja ini.
Saya mengundang semua orang di bawah potongan, saya mencoba menggambarkan topik seabstrak mungkin.
Beberapa definisi
CQRS (Segregation Command Query Responsibility) - alokasi operasi baca dan tulis menjadi entitas yang terpisah. Sebagai contoh, kami menulis kepada master, membaca dari replika. CQRS. Fakta dan Kesalahpahaman - Membantu mendapatkan pengetahuan menyeluruh tentang Zen CQRS.
ES (Event Sourcing) - penyimpanan semua perubahan status suatu entitas atau kumpulan entitas.
CQRS / ES adalah pendekatan arsitektur di mana kami menyimpan semua peristiwa perubahan status entitas dalam tabel acara dan menambahkan agregat dan proyektor untuk ini.
Agregat - menyimpan dalam memori properti yang diperlukan untuk membuat keputusan logika bisnis (untuk mempercepat penulisan), membuat keputusan (logika bisnis) dan menerbitkan acara.
Proyektor - mendengarkan acara dan menulis ke tabel atau basis data yang terpisah (untuk membaca lebih cepat).

Dalam pertempuran
Proyektor acara Laravel - perpustakaan CQRS / ES untuk Laravel
Larabank adalah repositori dengan pendekatan CQRS / ES. Kami akan membawanya untuk diadili.
Konfigurasi pustaka akan memberi tahu Anda ke mana harus mencari dan memberi tahu apa itu. Kami melihat file event-projector.php . Dari yang diperlukan untuk menggambarkan pekerjaan:
projectors
- daftar proyektor;reactors
- daftar reaktor. Reactor - di perpustakaan ini menambahkan efek samping ke pemrosesan acara, misalnya, dalam repositori ini, jika Anda mencoba untuk melebihi batas penarikan tiga kali, acara MoreMoneyNeeded ditulis dan pesan dikirim kepada pengguna tentang kesulitan keuangannya;replay_chunk_size
- ukuran potongan replay. Salah satu fitur ES adalah kemampuan untuk memulihkan sejarah dari berbagai peristiwa. Proyektor acara Laravel disiapkan untuk kebocoran memori selama operasi semacam itu menggunakan pengaturan ini.
Perhatikan migrasi. Selain tabel Laravel standar, kami memiliki
stored_events
- tabel ES utama dengan beberapa kolom data tidak terstruktur untuk data acara meta, kami menyimpan jenis acara dalam satu baris. Important column aggregate_uuid
- menyimpan uuid dari agregat untuk menerima semua peristiwa yang terkait dengannya;accounts
- tabel proyektor akun pengguna, diperlukan untuk pengembalian cepat data saat ini pada keadaan keseimbangan;transaction_counts
- tabel proyektor dari jumlah transaksi pengguna, yang diperlukan untuk pengembalian cepat dari jumlah transaksi yang diselesaikan.
Dan sekarang saya mengusulkan untuk berangkat dengan permintaan untuk membuat akun baru.
Pembuatan akun
Perutean resource
standar menjelaskan AccountsController . Kami tertarik dengan metode store
public function store(Request $request) { $newUuid = Str::uuid();
AccountAggregateRoot mewarisi pustaka AggregateRoot . Mari kita lihat metode yang disebut pengendali.
Metode persist
memanggil metode storeMany
model yang ditentukan dalam konfigurasi event-projector.php sebagai stored_event_model
dalam kasus kami, StoredEvent
public static function storeMany(array $events, string $uuid = null): void { collect($events) ->map(function (ShouldBeStored $domainEvent) use ($uuid) { $storedEvent = static::createForEvent($domainEvent, $uuid); return [$domainEvent, $storedEvent]; }) ->eachSpread(function (ShouldBeStored $event, StoredEvent $storedEvent) {
* Proyek Antrian
Proyektor AccountProjector dan TransactionCountProjector mengimplementasikan Projector
oleh karena itu mereka akan merespons peristiwa secara serempak dengan rekaman mereka.
Oke, sebuah akun telah dibuat. Saya mengusulkan untuk mempertimbangkan bagaimana klien akan membacanya.
Tampilan faktur
Jika proyektor akun mengimplementasikan antarmuka QueuedProjector , maka pengguna tidak akan melihat apa-apa sampai acara diproses secara bergantian.
Akhirnya, kita akan mempelajari bagaimana pengisian dan penarikan uang dari akun bekerja.
Isi ulang dan penarikan
Sekali lagi, lihat AccountsController :
Pertimbangkan AccountAggregateRoot
saat mengisi akun:
public function addMoney(int $amount) { $this->recordThat(new MoneyAdded($amount)); return $this; }
* AgregatRoot
saat menarik dana:
public function subtractMoney(int $amount) { if (!$this->hasSufficientFundsToSubtractAmount($amount)) {

Kesimpulan
Saya mencoba menggambarkan proses "onboarding" di CQRS / ES di Laravel sebebas mungkin dalam air. Konsepnya sangat menarik, tetapi bukan tanpa fitur. Sebelum menerapkan, ingat tentang:
- konsistensi akhirnya;
- diinginkan untuk menggunakan DDD di domain terpisah, Anda tidak harus membuat sistem besar pada pola ini;
- perubahan skema tabel acara bisa sangat menyakitkan;
- Secara bertanggung jawab, perlu mendekati pilihan granularitas peristiwa, semakin banyak kejadian konkret, semakin mereka akan berada dalam tabel dan semakin banyak sumber daya yang dibutuhkan untuk bekerja dengannya.
Saya akan senang melihat kesalahan.