Yeni yetme öğrencilik yıllarımda kodladığım, arşivlerimin derinliklerinden şans eseri bulduğum neredeyse 10 yaşındaki IndexMaker projesini çağa uyarlamaya son hız devam ediyoruz.
Bir önceki yazıda Isolation üzerinde durmuştuk. Ne ile Nasıl ayrımını yapmak üzerine biraz ısınmıştık.
Şimdi Ne ile Nasıl’ı neredeyse tamamen birbirinden koparmak ve nihayetinde yalın, çevik ve modüler bir proje oluşturmak için domain yönelimli tasarım ve geliştirme konusunu masaya yatıracağız.
Nedir bu DDD?
Şu resim çok açıklayıcı olacaktır diye tahmin ediyorum;
Bu resmin özeti şudur, Domain’ime dokunma!
Şaka bir yana, nedir ne değildir aktarabilmek için şöyle bir kıyaslamaya başlayalım.
Geleneksel mimaride (sol), veritabanı teknolojisi, kullanılan üçüncü parti eklenti/kütüphane ve araçlar gibi projenin çevresel bileşenleri tüm katmanlardan erişilebilir durumdadır.
Bu zamana kadar hem hızlı geliştirmeye imkan sağladığı için hem de işleri başlangıçta kolaylaştırdığı için bu yaklaşım benimsenmiştir.
Ancak günümüz yazılım projeleri artık öyle küçük ve basit projeler değil ne yazık ki. Haliyle böyle bir altyapı çok ciddi sorunlara yol açabiliyor.
Örneğin en basitinden kullandığınız veritabanı teknolojisini değiştirmek isteseniz, kolları sıvayıp projenin tüm katmanlarında uzun soluklu bir gezintiye çıkmanız gerekiyor.
Ayrıca bu yaklaşımla genişlemesi, ölçeklenmesi ve bakımı zor projeler ortaya çıkıyor ki bu da yine bu devirde kabul edilemez bir problem.
Peki geleneksel mimarideki bu sorunlara DDD ile nasıl çözüm buluyoruz?
DDD, işleri gerçek hayatta olduğu gibi ele almayı savunuyor. Adında geçen domain, yapacağınız işin özünü temsil ediyor aslında.
Bir yazılım projesi hiç bir zaman bir “yazılım projesi” olsun diye geliştirilmez. Ya bir doküman yönetim sistemi, ya bir otel rezervasyon sistemi, ya da kaynak yönetim sistemi gibi süslü isimleri olur.
Daha da ötesinde, bir yazılım projesinin tam anlamıyla hangi problemi çözeceği ve kimler için bu problemi çözüyor olduğu sorularının cevapları bize Domain’i verir. Cevapta yer alan her bir eleman da aslında Domain’in elemanlarıdır.
Index Maker projesi için Domain nedir?
Index Maker, belirlediğiniz bir klasörü arayarak içindeki tüm dosya ve klasörleri size listeleyen ve içerisinde arama yapmanıza izin veren bir yazılım projesi. Bu tanımdan hareketle Index Maker’ın Domain elemanları neler olmalı?
- Dosya
- Klasör
- Dizin Gezici
- Dizin/Dosya Arama Motoru
Bu projeden beklediğim her şeyi karşılayan elemanlar bunlar.
Bunları sağda gördüğünüz o soğanın en içine gömeceğiz çünkü bu projenin işlevi tamamen değişmediği sürece yani yukarıda yazdığım tanımda ciddi bir değişiklik olmadığı sürece bu elemanlar sabit kalacak.
Şimdi gelelim bu elemanları yani Domain’i çevreleyecek diğer katmanlara. Evet bir dosya ve klasör elemanı olacağından bahsettik. Ve belirlenen bir klasör elemanı içerisinde gezip bize tüm alt klasör ve dosyaları listeleyecek bir gezici sınıf olmalı.
Bir dakika durup düşünelim.
Bu gezici sınıfı domaine dahil edersek ne olur?
Bir dizin gezgini yazmak teknoloji bağımlı bir iş. Yani teknolojinin değişimiyle değişmesi çok muhtemel. Bu sebeple dizin gezginini domaine dahil etmek doğru değil. Ancak kendisi domain’in bir elemanı/gereksinimi. Yani domain’i tanımlayan elemanlardan biri. Haliyle domain’den çıkarmamız da uygun değil.
Ne yapacağız peki?
Keşke gereksinimleri ve fonksiyonelliği tanımlayabileceğimiz ama implementasyonu başka bir yerde yapabileceğimiz bir teknoloji olsa değil mi?
DI’a girmeye başlıyoruz, kemerleri bağlayın.
Dizin gezgininin limitlerini domainde belirlemek için dizin gezginini ifade eden bir interface oluşturacağız. Infrastructure hariç tüm yazılım dizin gezgininin yalnızca interface’ini bilecek. Böylece teknolojik detaylar işin içine girmeden dizin gezgininin tüm özelliklerini kullanabileceğiz.
Sonrasında Autofac ile bu interface ile Infrastructure katmanında yazdığımız ve teknoloji içeren Dizin Gezgini’ni register edeceğiz.
Bu sayede NE’yi ve NASIL’ı birbirinden tamamen ayırmış olduk.
Mevcut dizin gezgini windows dosya dizinlerinde arama yapmak üzere geliştirildi. Ama DDD ile geliştirme yaptığımız için, şu an desek ki bunu Linux işletim sistemlerinde de çalışacak hale getirelim (.Net Core’a geçtiğimizi varsayalım) bunu yapmak için değiştirmemiz gereken tek proje Infrastructure projesi olacak. Kod karmaşası içerisinde referans aramalar, bir değişiklik yüzünden hata veren absürt sınıflar yok!
Tamamsak koda bir göz atalım.
Önce proje yapısının yeni halini görelim;
1 projeden oluşan yazılımımız 3 projeye bölündü. Eskiden yalnızca bir Winforms projesinden oluşuyor iken şimdi;
- IndexMaker.App – WinForms projesi
- IndexMaker.Domain – Domain’i temsil eden Class Library
- IndexMakar.Infrastructure – Infra’yı temsil eden Class Library
Şimdi sırasıyla projelerin içinde neler olduğunu inceleyelim.
Çok temiz değil mi? Yalnızca bir form dosyası, hepsi bu.
Diğer tüm dependency’ler referans verilen projelerden geliyor.
Şimdi Domain’i inceleyelim.
Domain projesi için temelde kullandığım 4 ana klasör var.
- Constants
Domain’e özgü constant değişkenleri ve sınıfları tanımladığım klasör. - Entities
Domain’de yer alan her bir entity’yi yani domain’e özgü varlıkları karşılayan sınıfları tanımladığım klasör. - Repositories
Veriye erişim ve manipulasyon için kullanılacak repository sınıflarını tanımladığım klasör. Tıpkı dizin gezgininde olduğu gibi repository sınıflarında da yalnızca interface oluşturup implementasyonu Infrastructure katmanında gerçekleştireceğiz. - Services
Uygulama içerisinde domain’e özgü servislerin (dizin gezgini dahil) interface’lerinin yer aldığı klasör. Implementasyon nerede? Infra’da…
Şimdi de infra projesine bir göz atalım.
Buradaki klasör yapısı da;
- Data
Veriye erişim ve veriyi yönetmek ile ilgili tüm teknoloji bağımlı fonksiyonelliği tanımladığım klasör. Altındaki Entities klasörü DB tarafında yer alacak entity’leri karşılıyor. EF klasörü ise bu proje için şimdilik eklenmemiş de olsa ilerleyen süreçte ekleyeceğimiz DBContext gibi EntityFramework sınıflarını tanımladığım klasör. - Services
Domain’de bahsettiğimiz Services klasöründe yer alan interface’lerin implementasyonlarının yer aldığı klasör. Görebileceğiniz gibi Dizin gezgini sınıfımız da burada.
Form uygulamasındaki Program.cs dosyasına da bir göz atalım.
Burada daha önce bahsettiğim tüm Registration’ları gerçekleştireceğiz. Bunun için Autofac kütüphanesini kullanacağız. Nuget paketi olarak NPM ile edinebilirsiniz.
Buradaki mantık da kabaca şöyle. Dikkat ederseniz MainForm.cs form sınıfımız constructor’ında bir interface alıyor. Ancak bu sınıfı ilkleyen bir başka sınıf mevcut değil. Peki nasıl oluyor da bu sınıf ilkleniyor?
ConfigureDependencies() metodunu incelediğimizde builder üzerinden çağırılmış bir RegisterType() komutunun builder’a “IDirectoryManagementService interface’ini gördüğünde DirectoryManagerService sınıfından bir instance yarat ve onu kullan” dediğini görebiliyoruz.
Ayrıca MainForm sınıfının constructor’ında bir parametre alacak şekilde değişiklik yaptığımız için artık eski usül form oluşturamıyoruz. Oluşturabiliriz, ancak bu durumda DirectoryManagerService sınıfından bir instance yaratıp bunu MainForm()’a geçirmemiz gerekecek ki bu hareket ile de Dependency Inject etmiş olmayacak, dependency kullanmış olacağız.
Bu sebeple MainForm sınıfını da builder’ımıza register ediyoruz. (Şu cümledeki kelimelerin yarısı İngilizce olmasına rağmen cümlenin Türkçe olması sinir bozuyor biliyorum, ama tümünü Türkçe yazınca da hiç bir şey anlaşılmıyor)
Bu işlem sayesinde artık MainForm’dan bir instance yaratma işi de builder’a aktarılmış durumda. Ve builder IDirectoryManagerService interface’ini gördüğünde DirectoryManagerService sınıfından bir instance yaratıp onu kullanması gerektiğini bildiği için sorunsuz bir şekilde formu kullanabiliyoruz.
Tüm kaynak kodlar github’da DDD klasörü altında mevcut.
Linki de buraya bırakayım: https://github.com/ycansener/IndexMaker
Sorular için Twitter’dan ya da comment ile bana ulaşabilirsiniz.
Ortasından başlayan ve merak edenler için bir önceki yazıda Isolation nedir? Neden yapılır? Nasıl yapılır? demiştik.
Buradan erişilebilir: http://blog.ycansener.com/?p=21
Bir sonraki yazıda işin içine bir de EF ve Code First yaklaşım ile bir veri tabanı ekleyelim diyorum.
Kısmet.