Şişirilmiş yerine ince: Toplamalar ve okuma modelleri gerçekte nedir?

kapanış bildirimi

Bu makale İngilizce olarak da mevcuttur. Teknik yardımla tercüme edildi ve yayınlanmadan önce editoryal olarak gözden geçirildi.

Geliştiriciler Etki Alanı Odaklı Tasarım (DDD), CQRS ve Event Sourcing ile ilk kez temasa geçtiklerinde zaten yanlarında zihinsel modeller getiriyorlar. Yıllarca nesneler ve tablolarla çalışmak, veriler hakkındaki düşünme biçimlerini şekillendirdi.

Duyurudan sonra devamını okuyun

Golo Roden, native web GmbH'nin kurucusu ve CTO'sudur. Etkinlik ve hizmet odaklı dağıtılmış mimarilere odaklanarak web ve bulut uygulamaları ile API'lerin tasarımı ve geliştirilmesiyle ilgilenmektedir. Yol gösterici ilkesi, yazılım geliştirmenin kendi başına bir amaç olmadığı, her zaman temeldeki profesyonelliği takip etmesi gerektiğidir.

Ve böylece “toplama” kelimesini duyduklarında akıllarında hemen tanıdık bir görüntü oluşur: Toplama bir nesne gibi olmalıdır ve nesneler tablolara eşlenir. Bu sezgi doğru görünüyor. Ama öyle değil ve ekstra adımlarla şüpheli bir şekilde CRUD'a benzeyen bir sisteme yol açıyor.

Bu modeli sayısız kez gördüm. Ekipler olay odaklı sistem adını verdikleri bir sistem oluşturur ve sonunda bir birey elde ederler. booksher tu alanını içeren tablo Book-Toplam var. Esasen, aktarım mekanizması olarak yalnızca olayları içeren ilişkisel bir veritabanını yeniden yarattılar. DDD, CQRS ve Event Sourcing'in güçlü yanları, yani bu kavramların vaat ettiği esneklik henüz kullanılmamıştır.

Sorun, geliştiricilerin genel olarak bir bütün olarak düşündüğü şeydir: tek bir şeyle ilgili tüm veriler için bir kapsayıcı. Kendilerini tanıtıyorlar Book-Önce toplayın ve mülkleri listelemeye başlayın:


BookAggregate {
    id: string
    title: string
    author: string
    isbn: string
    currentBorrower: string | null
    dueDate: Date | null
    location: string
    condition: string
    purchasePrice: number
    acquisitionDate: Date
    lastInspectionDate: Date
    popularityScore: number
}

Bir nesneye benziyor. Bütün alanları var. Temiz bir şekilde bir veritabanı tablosuna eşlenebilir. Hata tam olarak burada yatıyor: Toplama bir veri kabı olarak muamele etmek.

Duyurudan sonra devamını okuyun

Bu şekilde düşündüğünüzde, toplamınız bir kitap hakkında bilmek isteyebileceğiniz her şeyin şişirilmiş bir temsili haline gelir. Okuma şeklinizin yapısını yansıtır çünkü bunların temelde farklı amaçlara hizmet eden temelde farklı kavramlar olduğunu henüz fark etmemişsinizdir.

DDD'ye göre toplam, karar vermenin tutarlılık sınırıdır. Hepsi bu. Amacı, komutları işlerken iş kurallarına uyulmasını sağlamaktır. Bir komutun geçerli olup olmadığına karar vermek için yalnızca gerekli bilgilere ihtiyacı vardır.

Bunu düşün BorrowBook-Emretmek. Bir kitabın ödünç alınıp alınamayacağına karar vermek için bilmeniz gereken tek şey şudur: Kitap şu anda mevcut mu? Başlığa, yazara, ISBN'ye, satın alma fiyatına, konuma veya son inceleme tarihine ihtiyacınız yoktur. Bu bilgilerin hiçbiri, bu özel komutun başarılı olup olmayacağına karar vermenize yardımcı olmayacaktır. Bu, yalnızca kararla ilgili durumu içerdiğinden toplamın çok küçük olabileceği anlamına gelir.

Kütüphane örneğimiz için, uygun şekilde tasarlanmış bir tane olabilir. Book-Bu nedenle toplam şuna benzer:


BookAggregate {
    isAvailable: boolean
    currentBorrower: string | null
}

Karar vermeniz yeterli:

  • Bu kitap ödünç alınabilir mi? (isAvailable === true)
  • Bu kişi iade edebilir mi? (currentBorrower === personId)

Kitapla ilgili diğer her şey, her bilgi başka bir yere aittir: topluluğa değil, okuma kalıplarına.

Geliştiriciler bir toplamanın belirli alanlara sahip olduğunu kabul eder etmez bir sonraki hata şu şekilde olur: “Toplamda bu alanlar varsa, okuma modeli tablomda da bu alanlar bulunmalıdır.”

Sonuç tahmin edilebilir. Bir tane yaratırsın books-Sütunlu tablo id, title, author, isbn, borrower, dueDate, location, condition, purchasePrice ve aklınıza gelebilecek diğer alanlar. Bu yekpare yapıda sorgular karmaşık birleşimler haline gelir. Performans zarar görüyor. Esneklik kaybolur.

Bu, olay kaynağına uygulanan CRUD düşüncesidir. Olaylar vardır ama bunlar yalnızca bir ulaşım katmanıdır. Sistem, tıpkı geleneksel ilişkisel veritabanı gibi, verilerin tek bir kanonik temsili etrafında dönüyor.

Okuma modelleri, belirli sorgular için optimize edilmiş projeksiyonlardır. Veri yapılarına değil, kullanım senaryolarına ihtiyacınız var. Temel içgörü şu: Okuma kalıpları toplamlardan değil olaylardan kaynaklanır:

  • Toplamınız ne olacağına karar verir.
  • Olaylar yaşananları kaydeder.
  • Belirli soruları etkili bir şekilde yanıtlamak için bu etkinliklerden okuma kalıpları oluşturulur.

Okuma modellerinin agregaların yapısını yansıtması gerektiğini belirten hiçbir gereklilik, hiçbir kural, hiçbir mimari prensip yoktur.

Gerçekte bunun tersi doğrudur. Bir etkinlik akışından birçok farklı okuma modeli oluşturabilirsiniz. Bu, tablolarda düşünürken kaybolan CQRS'nin gücüdür.

Bunu özellikle kütüphanemizle yapalım. Bir tane var Book– Kararları yöneten toplu:


BookAggregate {
    isAvailable: boolean
    currentBorrower: string | null
}

Olaylar sistem üzerinden akar: BookAcquired, BookBorrowed, BookReturned, BookRemoved ve benzeri. Bu olaylar yaşananlara ilişkin zengin bilgiler içermektedir.

Şimdi insanların yanıtlanmasını istediği farklı soruları ele alalım:

  • Katalog aramasında mevcut kitaplar başlıkları, yazarları ve ISBN kodlarıyla birlikte gösterilmelidir. Geçmişi veya fiziksel konumu ödünç almakla ilgilenmiyor.
  • Üyenin kontrol panelinde (“Kitaplarım” sayfası) üyenin hangi kitapları teslim aldığı, bunların teslim tarihi ve herhangi birinin süresinin dolup dolmadığı gösterilmelidir. ISBN'ye veya fiziksel konuma gerek yok.
  • Kütüphaneciler için istatistiksel kontrol panelinin hangi kitapların en popüler olduğunu, ortalama ödünç alma sürelerini ve zaman içindeki eğilimleri bilmesi gerekir. Mevcut kullanılabilirlik gerektirmez.
  • Gecikme raporu, borçlunun adını, iletişim bilgilerini, kitap başlığını ve geciktiği gün sayısını gerektirir. Satın alma fiyatları veya durum değerlendirmeleri gerektirmez.
  • Envanter yönetimi sistemi fiziksel konumları, durum derecelendirmelerini ve son denetim tarihlerini gerektirir. Borçlunun bilgisine gerek yoktur.

Bunların her biri, aynı olaylardan oluşturulan ve kendi özel kullanım durumu için optimize edilmiş ayrı bir okuma modelidir.

Bu okuma kalıpları şöyle görünebilir:

Katalog okuma modelini arayın:


{
    bookId: string
    title: string
    author: string
    isbn: string
    isAvailable: boolean
}

Borçlu kontrol paneli okuma şablonu:


{
    memberId: string
    books: [
        {
            bookId: string
            title: string
            dueDate: Date
            daysOverdue: number
        }
    ]
}

Kütüphane istatistikleri okuma şablonu:


{
    bookId: string
    title: string
    totalBorrows: number
    averageDuration: number
    popularityRank: number
}

Süresi dolmuş kitap okuma düzeni:


{
    bookId: string
    title: string
    borrowerId: string
    borrowerName: string
    contactEmail: string
    daysOverdue: number
}

Stoktaki yatak modeli:


{
    bookId: string
    location: string
    condition: string
    lastInspectionDate: Date
}

Herhangi bir okuma modeli

  • yalnızca kullanım durumu için gerekli alanlara sahiptir,
  • gerektiğinde başka bir veritabanında saklanabilir (işlemler için PostgreSQL, arama için Elasticsearch, hızlı aramalar için Redis),
  • herhangi bir zamanda olaylardan yeniden oluşturulabilir ve
  • diğer okuma modellerinden bağımsız olarak gelişir.

Burası olay kaynağının gerçek gücünü gösterdiği yerdir. Birçok özel okuma modeli bir olaylar akışından türetilir. Her biri küçük, odaklanmış ve hızlıdır. Yeni bir okuma modeli eklemek, mevcut yazma modelinin veya okuma modellerinin değiştirilmesini gerektirmez. Aynı olaylardan başka bir projeksiyon oluşturmanız yeterlidir.

Yeni bir ilişkiye mi ihtiyacınız var? Yeni bir okuma modeli oluşturun. Yavaş bir sorguyu optimize etmeniz mi gerekiyor? Bu özel okuma modelini başka hiçbir şeye dokunmadan yeniden yapılandırın. Yeni bir kullanım senaryosunu desteklemeniz mi gerekiyor? Başka bir projeksiyon ekleyin.

Bu esneklik CQRS'nin vaadidir. Ancak bu yalnızca okuma kalıplarını toplamlarınızın bir yansıması olarak görmeyi bıraktığınızda gerçekleşir.

Pratik avantajlar dikkat çekicidir:

  • Her okuma modeli küçük ve uzmanlaşmış olduğundan performans artar. Sorgular artık tam olarak ihtiyaç duydukları verilere ulaşmıyor. Dizinler belirli erişim kalıpları için optimize edilebilir.
  • Yazma desenini veya diğer okuma desenlerini etkilemeden okuma desenlerini ekleyebileceğiniz, değiştirebileceğiniz veya kaldırabileceğiniz için esneklik artar. Takımlar okuma modellerine bağımsız olarak sahip olabilirler.
  • Açıklık, her okuma modelinin açık bir amacı olması gerçeğinden gelir. Hangi verinin hangi kullanım durumu için olduğu konusunda belirsizlik yoktur. Her okuma modelinin yapısı cevapladığı soruları yansıtır.
  • Bağımsızlık, farklı ekiplerin, kalıp değişikliklerini koordine etmek zorunda kalmadan, farklı okuma kalıpları üzerinde çalışabilmesinden kaynaklanmaktadır. Olaylar sözleşmedir, veritabanı tabloları değil.

Olay kaynağı bulmanın en zor kısmı, CRUD sistemlerinde size iyi hizmet eden zihinsel modellerin öğrenilmemesidir. Nesneler ve tablolar yararlı kavramlardır ancak toplamaları anlamak ve kalıpları okumak için doğru mercek değildirler.

“Toplamda hangi alanlar var?” diye sormayı bırakın. Kendinize şu soruyu sormaya başlayın: “Bu kararı vermek için neyi bilmem gerekiyor?”

“Bu toplam için hangi tabloya ihtiyacım var?” diye sormayı bırakın. “Kullanıcılarımın hangi soruları yanıtlaması gerekiyor?” diye sormaya başlayın.

Toplam, kararınızın basitleştirilmiş ve odaklanmış sınırıdır. Olaylar, olup bitenlere dair tarihsel kaydınızdır. Okuma modelleri, belirli sorgular için optimize edilmiş görünümlerdir.

Bunlar üç farklı kavramdır. Aynı yapıya sahip olmaları gerekmez. Aslında muhtemelen yapmamaları gerekir.


(Ben)


Yayımlandı

kategorisi

yazarı:

Etiketler:

Yorumlar

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir