İçeriğe geç →

Repository Pattern Yaklaşımı Yerine Command/Query Object Pattern Yaklaşımı

Merhaba arkadaşlar.

Bu makale konumuzda data access layer için Repository Pattern‘i yerine, Command/Query Object Pattern‘inin kullanımı ve faydaları inceliyor olacağız.

dao

Sizlerinde bildiği gibi uzun zamanlardır data access layer’larımız için, vazgeçilmez bir hal almıştır Repository Pattern’i. Peki bunca zamandır kötü tasarımlardan sıyrılabilmek ve bağımlılık yönetimi(dependency management) adına birde SOLID SOLID diye bağırırken ilk prensiplerinden birisi olan Single Responsibility‘e baktığımızda, Repository Pattern’i ne derecede uyuyor? ve zamanla git gide büyümeye ve hantallaşmaya başlıyor.

Aslında konumuz burada Repository Pattern’ini kullanmamız veya kullanmamamız değil. Bu sebepten ötürü kullanmamalıyız da demek yanlış olur. Asıl amaç ise büyüyen bir sisteme sahip olacaksak, daha iyi bir separation of concerns için neler yapılmalıdır. Unutulmamalıdır ki her patterni spesifik bir problemi çözmek için tasarlanmıştır. Var olmayan bir sorunu çözmek için değil.

Command/Query Object Pattern’i Nedir?

Command/Query Object pattern’i, data access’e erişim için kullanılan pattern’lerden birisidir. Bu yaklaşım ile tüm database işlemleri büyük data access god class’ları yerine, Command ve Query’lerden oluşur. Bu sayede her bir class, isolated bir şekilde küçük parçalardan ve Single Responsibility prensibine de uygun oluşmaktadır. Öte yandan uygulama architecture’ımızı dış kaynaklardan veya değişimlerden korumak istiyorsak, bunun en iyi yolu ise kendi abstraction’ımızı oluşturmaktır. Command/Query Object pattern’i ise bunun bir yoludur.

Dilerseniz konuya biraz örnek üzerinden devam edelim. Varsayalım ki aşağıdaki gibi bir repository interface’imiz olsun.

Peki bu interface’i implemente edecek olan “ProductRepository” gün geçtikçe ve farklı ihtiyaçlar doğdukça ne hale gelecek? İşte asıl sorun burada başlıyor. Repository pattern’i ile business logic kısmını, data source’dan başarılı bir şekilde hep ayırdık. Fakat bir süre sonra repository’ler, git gide büyüyen kodlar ve method’lar ile bir god class haline geliyorlar. Bunun yanında başka bir dezavantajına da bakmak gerekirse:

Loglama gibi işlemleri gerçekleştirmek ise, duplicate logic’lere sebep olmaktadır. Elbette Template Pattern‘i ile bu sorun aşılabilir ve büyüyen sistemlerdeki bu tarz problemler için ise çözüm Decomposition. Haydi gelin Command/Query Object Pattern’ini implemente ederek bir bakalım.

Öncelikle “ICommand” ve “IQuery” isimlerinde iki adet interface tanımlayalım.

Tanımlamış olduğumuz interface’ler Execute method’larına sahip ve parametre olarak “System.Data” namespace’i altında bulunan “IDbConnection” interface’ini alıyor. Yukarıda tanımlamış olduğumuz repository interface’inde bulunan method’ları, sırasıyla kodlayalım. Kodlamaya başlamadan önce “Product” entity’sini aşağıdaki gibi tanımlayalım.

“GetProductDetailById” method’u için “ProductDetailByIdQuery” isminde bir class tanımlayalım ve IQuery interface’ini aşağıdaki gibi implemente edelim.

Constructor üzerinden “id” property’sini inject ediyoruz ve “Execute” method’u içerisinde istenen işlemleri gerçekleştiriyoruz. Bu Query sınıfı artık sadece Product’ın Detail bilgisini, “id” property’si ile getirmekle yükümlü. Şimdi “CreateProduct” method’u için ise “CreateProductCommand” isminde bir class oluşturalım ve “ICommand” interface’ini aşağıdaki gibi implemente edelim.

Query’de olduğu gibi burada da constructor üzerinden create edeceğimiz “Product” entity’sini inject ediyoruz ve ilgili işlemleri “Execute” method’u içerisinde “dbConnection” üzerinden gerçekleştiriyoruz. Artık bu class’da sadece yeni bir Product Create etmek ile yükümlü.

Gördüğümüz gibi her bir işlem küçük parçalardan ve ufak sorumluluklardan oluşmaktadır. Şimdi database işlemlerini daha kolay yönetebilmek için ise “IDbConnection” interface’ini wrap’leyecek bir context oluşturalım.

İçerisinde iki farklı “Execute” method’u bulunmaktadır. Birisi query’leri handle ederken diğeri command’ları handle etmektedir.

Wrap’leme işleminide tamamladığımıza göre artık “FooDatabase” context’ini kullanarak, Command/Query Object pattern’inin örnek kullanımına bir bakalım.

“UpdateProduct” method’una dikkat ettiğimizde tüm işlemlerimiz “IFooDatabase” interface’i üzerindeki “Execute” method’u üzerinden ilerlemektedir. “_fooDatabase.Execute(new ProductDetailByIdQuery(productId))” satırı ile Product bilgisini çekebilirken, “_fooDatabase.Execute(new CreateProductCommand(product))” satırı ile de yeni bir Product yaratabilmekteyiz.

Bir makalenin daha sonuna geldik. Burada büyüyen bir sistemde daha iyi bir “separation of concerns” ün ve decomposition’ın nasıl uygulanabileceği yöntemlerinden birini gördük. Hangisini kullanıp kullanmamak kararı, ilgili domain’izin complexity’sine göre sizlere kalmış.

Umarım keyifli bir makale olmuştur.

Takipte kalın.

Bu makale toplam (3746) kez okunmuştur.

32
1



Kategori: Architectural

8 Yorum

  1. İrfan Evrens İrfan Evrens

    Merhaba,

    Yazı için teşekkürler. Büyük sistemler için ideal görünüyor. Uzun vadeli işlerde dosya sayısının çokluğundan ziyade o dosyaları yönetebilmek de önemli bir konu tabi. Bu anlamda faydalı bir yazı.

    Ben size bir soru sorayım, validasyon kurallarını controller içinde mi çalıştırıyorsunuz yoksa yazılan command ve query sınıfları içinde mi çalıştırıyorsunuz?

    Son olarak .net’de property incject yok mu, constructer inject’den daha okunaklı oluyor da.

    Kolay gelsin.

    • Merhaba teşekkür ederim yorumunuz için öncelikle. Validasyon biraz geniş bir kavram açıkcası. Business validasyonları, rule validasyonlar vb. Bu tarz cross-custting işlemler için eğer varsa engine katmanının üzerinde bir manager katmanı orada uygulanabilir veyahut aspect’lerle yönetilebilinir oda olmadı command’lar da da yönetmek gibi bir çok yöntem mevcut. Projenizin structure’ına göre değişir. Property injection tabi ki bulunmakta ve bu konuda okunurluğa göre de davranılmamalıdır. Dışarıya expose etmek istemediğim bir property’i inject etmek pek de uygun olmayacaktır.
      İyi günler dilerim.

  2. Hocam merhabalar,
    Çalıştığım şirket LOGO kullandığından dolayı işlerimizde genellikle stored procedure kullanıyoruz.
    Bu konuyla ilgili bir makale yazmanız mümkün mü ?
    Teşekkürler.

    • Selamlar, öncelikle teşekkür ederim yorumunuz için. SP’ler pek ilgi alanım değil açıkcası code-review’lar haricinde. 🙂

      • Murat Murat

        Merhaba Usta
        Bu konularda takıntılıyım, şuan sorumlu olduğum projemi akıla mantığa en uygun şekilde refactor ediyorum. Projemde klasör isimlerinden hangi classın ne tür bir standart yapıyla dizilimesine kadar kusursuza yakın tasarım desenleri araştırırken gördüm makaleyi. Ben repostory paterni daha kullanışlı buldum açıkcası bu da fena değil ama bu sefer class dosyaları gözümü yoracak. Şuan refactore gitmemdeki en büyük neden DTO kullanmamış olmam. DTO(auto map edecek) ve asenkron destekli bir repository patern kurgulayıp işi ilerletmeyi düşünüyorum. Validasyon, log, hata yönetimi gibi işlemler için postsharp kullanmayı düşünüyorum. Hangi orm kullanıcağıma henüz karar veremedim. Bu ihtiyaçlarımı düşünerek örnek bir proje arıyorum ve yorumlarını bekliyorum.

        • Merhaba,

          Refactor etmeniz, etmeye takıntılı olmanız çok güzel. 🙂 Açıkcası ben kusursuz bir tasarım diye bir şeye inanmıyorum. Fakat bu noktada Repository ile Command/Query’i karıştırmamak gerek. Burada gözünüzü yoracak class çokluklarından ziyade, buradaki seperation’ın amacına, elinizdeki probleme doğru odaklanmak gerek. Cross-cutting işlemler için ise aspect’ler güzel tercih. Hangi orm demişsiniz birde, EF bir çok derdinizi çözecektir. Bu arada Command/Query’deki diğer bir güzel yön ise, querying işlemlerini (örnek misali) Dapper tarzı micro orm’ler ile yaparken, diğer işlemler için EF nimetlerinden yararlanabilmek gibi işlemler de güzel handle edilebilmektedir. Dediğim gibi, probleme ve seperation’a iyi bakmak, düşünmek gerek. 🙂 Göz yormaktan öte bir olay.

  3. Efe ÖZYER Efe ÖZYER

    Bende şirketi değiştirdim zaten 😀 Bu arada kitabınızı almıştım ancak CD’nizi kaybettim. Konuyla ilgili yapabileceğimiz bir şey var mı ? 🙁

    • İletişim bölümünden adresinizi gönderebilirseniz, size yenisini gönderebilirim. 🙂

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.