Ana içeriğe atla

C# ve Asp.net MVC'de Çok katmanlı Soğan mimarisi (Onion Architecture)

Çok katmanlı mimari, güçlü ve kolay geliştirelebilen ve katmanlarının kolaylıkla değiştirilebilen büyük uygulamalarda çok önemli bir rol oynar.
Eski ve en ünlü çok katmanlı mimari, 3 katmandan oluşmakta
  • Data Access Layer - Veri Katmanı
  • Business Process Layer - İş Modeli Katmanı
  • Presentation Layer - Kullanıcı Arayüzü Katmanı
 Bu mimaride, Kullanıcı Arayüzü Katmanı sadece ve sadece İş Modeli Katmanıyla iletişimdedir, ve Veri Katmanıyla direk iletişime geçmesine izin verilmiyor, böylece hem güvenlik sağlanıyor, hem de bir katman değiştirilmek istendiğinde diğer katmanlarda minimum değişiklikle bu işlem yapılabiliyor. bu mimari her ne kadar küçük ölçekli uygulamalarda başarılı olsa da, daha büyük ve karmaşık uygulamalarda yetersiz kalmaktadır.

Geleneksel Katmanlı Mimari

Onion Architecture veya Soğan mimarisi Jeffrey Palermo tarafından onerilmiştir. bu mimaride her katman soğan halkaları gibi düşünülmüş olup kolaylıkla değiştirilebilmesi veya düzenlenmesi amaçlanmıştır. bu mimariyi Repository Pattern ile düşündüğümüzde, bu işlem daha da kolay bir hal alıyor.

Soğan mimarisinde temel kural şöyledir: her üst düzey katman daha merkezi katmanlara bağlıdır, ancak merkezi katmanlar ondan sonrakilere bağlı olamaz. yani her hangi katmanda yapılan değişiklikler merkeze doğru olan katmanları etkilemez ancak ondan sonraki katmanları etkileyebilir. Burada dikkat edilmesi gereken nokta, harici katmanların iç katmanların hepsine bağlı olma zorunluluğu yoktur. örneğin "Data Access" Katmanı "Application Services" katmanını atlayabilir.

Soğan Mimarisi Şeması
Bu Mimaride geleneksel mimarinin aksine veri katmanı en iç katman olarak değil, en dış katman olarak belirlenmiş, böylece uygulamada verinin nerden geldiğinden bağımsız olarak bir uygulama geliştirilebilir. Ayrıca en dış katmanlardan diğeri Test Katmanıdır, ki uygulamanın testlerini uygulamanın diğer katmanlarından bağımsız kılıyor. bir diğer dış katman ise Kullanıcı arayüzüdür. bu sayede bir uygulama çekirdeği için farklı arayüzler tasarlanabilir. ister theme olarak düşünün, ister aynı uygulamanın mobil arayüzü ve web arayüzü olarak düşünün.
Jeffrey Palermo'nun Soğan mimarisini, C# ve Asp.net Mvc'de repository pattern kullanarak bir taslak hazırladım, aşağıda bu taslağın ve genel olarak yeni bir uygulamaya başladığınızda (enterprise uygulama) adım adım nasıl oluşturulabileceğini okuyabilirsiniz.

Taslağın kaynak kodlarına bu linkten ulaşabilirsiniz: https://github.com/mesuttalebi/NTierCSharpExample

1- Projeleri oluştur
Öncelikle boş bir "solution" oluşturunuz. ilk olarak "kullanıcı arayüzü katmanı" için bir "console application" projesi veya asp.net mvc projesi ekleyiniz ve projenin adını "multilayer.presentation" olarak seçiniz. Bu ismi istediğiniz isimle değiştirebilirsiniz ancak genel olarak bir solution'daki tüm projeleri belirli bir namespace'le başlamanız önerilir, bu namespace proje ismi olabilir. örneğin eticaret bir proje geliştiriyorsanız proje ismini "eticaret.presentation" seçebilirsiniz.
bu proje de (Kullanıcı arayüzü katmanı) kullanıcı arayüzünü geliştireceksiniz.
ikinci üçüncü ve dördüncü projeleri "class library" olarak ekleyiniz ve isimlerini "Domain Katmanı"  için"multilayer.domain", "veri katmanı" için "multilayer.infrastructure" ve "İş modeli katmanı" için "multilayer.services" olarak adlandırınız.
birinci aşamanın sonunda solution explorer aşağıdaki şekilde olmalıdır.
Solution Explorer
Her proje neleri kapsiyor?

"multilayer.domain"  projesinde "object" klaslarını tutacağız. (Blog, Author, ...).
bu proje soğan mimarisindeki "Domain Model" katmanını kapsıyor.

"multilayer.services" projesinde "iş model katmanı" olarak kullanıyoruz ve soğan mimarisindeki "Application Services" katmanını kapsıyor.

"multilayer.infrastructure" projesi ise, "Data Access katmanı"nı kapsıyor ve repository klaslarımızı burada tutacağız.


2- Bağlantılar (References)
Soğan mimarisine göre bağlantılarda en merkezi katman olan "Domain Katmanı" (Object klasları) hiç bir katmana bağlı olmaması lazım.  yani "multilayer.domain" diğer hiç bir projeye bağlı değil ve herhangi bir reference eklemeniz gerekmiyor.

"multilayer.infrastructure"  projesinden "multilayer.domian" projesine referance ekleyiniz. ("multilayer.domian" aslında aynı zamanda "domain services" katmanının yerinede geçiyor, şöyle ki domain interface'lerimizi aynı proje içinde başka bir klasörde saklıyoruz, ve domain klaslarıyla iletişim sadece ve sadece interface'ler aracılığıyla yapılıyor.

"multilayer.services" projesinden "multilayer.domain" ve "multilayer.infrastructure" projesine reference ekleyiniz. bu proje "Application Services" Katmanı olarak kullanılıyor.
Burada "multilayer.infrastructure" projesine bağlantıya sadece "Dependency Injection" için ihtiyacımız var.

"mutlilayer.presentation" projesinden ise "multilayer.domain", "multilayer.services" projesine link veriniz. 



Dikkat: bu projede "Data Access" Katmanı için Entity Framework kullanılıyor.


Diğer Bağlantılar:
Dependency Injection için bu projede ninject kullanılmış. nuget kullanarak ninject'i indiriniz ve "multilayer.presentation" ve "multilayer.services" projelerine ekleyiniz.

Bu yazı Devam edecek.

Lütfen detaylar için projenin kaynak kodlarını Buradan indiriniz. sorularınızı yorumlar kısmından bana yöneltebilirsiniz.




Güncelleme: 23.10.2016
Devam:
Nininject Ayarlaması
Daha önce belirttiğimiz gibi, presentation katmanından hiç bir şekilde Infrastructure (DAL) katmanına referans eklemiyoruz ve presentation katmanından veri ekleme güncelleme ve genel olarak veritabanı ile ilgili olan tüm işlerimizi Business (BLL - Multilayer.Services) katmanı aracılığıyla yapacağız.
Proje içinde objelerin birbirine bağlılığını ortadan kaldırmak amacıyla ve SOLID'in son prensipi olan Dependency Inversion'a uymak için IoC kavramına göre kod geliştiriyoruz ve bunun içinde bağımlılığı dışarıdan enjekte etmek gerekmektedir. (Dependency Injection- DI).
bağımlılığı dışarıdan enjekte edebilmek için DI Container(Bağımlılık Kabı)  dediğimiz bir eklentiye ihtiyacimiz vardır. en ünlü hazır DI Container'lardan Autofac ve Ninject söylenebilir. ben projede Ninject kullanıyorum, özel bir sebebi yoktur sadece alışkanlık.

Ninject'i iki projemizde kullanacağız. birincisi presentation katmanı, ikincisi ise, Business katmanı. presentation katmanında business service'lerime ait objelerin presentation da kullanıldığı yerlerde enjekte etmek için kullanacağız. business katmanında ise, infrastructure objelerini business'de kullandığımız yerlere enjekte etmek için ihtiyacımız var.



Business katmanı projesine sadece Ninject kurmanız yeterlidir, Presentation katmanında ise Ninject.MVC5 kurunuz, diğer eklentilerini otomatik kuracaktır.


(DİKKAT: DI Container'ı kendimiz de geliştirebiliriz. ancak konudan uzak olduğu için burada yer vermiyorum).

Ninject'e hangi interface kullanıldığı zaman hangi objeyi oluşturup göndermesi gerektiğini söylemek işlemine Binding denilir. bu binding işlemlerini presentation da Ninjectwebcommon.cs dosyasında yapacağız. business'te ise yeni bir class oluşturup onun içinde tanımlayacağız.

presentation katmanında, ninject kurulduğunda, App_Start klasörü içine NinjectWebCommon.cs dosyasını oluşturmaktadır.


Binding'lerimizi  "RegisterServices" fonksiyonu içine yazacağız.
Örnek:

Bind<IBlogservice>().To<BlogSerive>();



yukarıdaki kodda, aslında şunu söylüyoruz, ne zaman ki  IBlogService türünden bir objeye ihtiyacım olsa, BlogService türünden bir obje oluşturup bana döndür.



Yukarıdaki resimde gördüğünüz üzere, binding işlemlerini yapmadan önce, business katmanındaki bindingleri ninject kerneline yüklemesini söylemişim. Services.DIModule aslında business katmanın ninject modul'udur.
business katmanında aşağıdaki resimde görüldüğü gibi bir class oluşturup NinjectModule klasından kalıtım alıyoruz, bu klas içinde ise NinjectModule'un Load fonksiyonunu override (geçersiz kılmak) ederek business tarafı binding'lerimizi içine yazıyoruz.



DIModule aşağıdaki gibidir:


using MultiLayer.Domain.Entities;
using MultiLayer.Domain.Interfaces;
using MultiLayer.Infrasturcture.Repositories;
using Ninject.Modules;

namespace MultiLayer.Services
{
    public class DIModule : NinjectModule
    {
        public override void Load()
        {
            Bind<IRepository<Blog>>().To<Repository<Blog>>();
        }
    }
}


Bağımlılıkların enjekte edilme işlemi de tamamlanmış oldu. geriye tek şey kaldı, bu bağımlılıklar nasıl kullanılcak.
bağımlılıklar presentation tarafında da, business tarafında da class'ların constructor fonksiyonlarına parametre olarak gönderiliyor. örneğin: BlogController'da BlogService ile iletişim kurabilmek için, BlogController class'ının Constructor fonksiyonuna IBlogService parametresini ekliyoruz.


Yukarıda gördüğünüz üzere, controller içinde kullanabilmek için IBlogService türünden bir private değişken tanımlanmıştır ve bu değişken Constructor vasıtasıyla gelen yine aynı türden olan bir değişken ile initialize ediliyor. Constructor'daki IBlogService'inin doldurulma görevi Ninject'e bırakılmıştır. ve tanımladığımız binding'ler ile Ninject, IBlogService gördüğü zaman hangi objeden oluşturup doldurması gerektiğini biliyor.
BlogService Class'ında ise Constructor IRepository<Blog>() türünden bir değişken beklemektedir. Ninject, business katmanındaki tanımladığımız DIModule classındaki binding'lere bakarak hangi obje ile doldurması gerektiğini bilmektedir.


Böylece Presentation katmanından Infrastructure (DAL) katmanına hiç bir şekilde referans eklemeden sadece Business katmanı aracılığıyla ulaşabilmekteyiz.

Bir sonraki makalemde, Soğan mimarisine göre oluşturulan bir projede Asp.Net Identity kullanımını göstereceğim.

Yorumlar

  1. MVC tek başına çok katmanlı bir mimari sayılır mı ? Yoksa soğan veya 3 katmanlı mimari gibi bir yazım şekli belirlemek mi gerekiyor.

    YanıtlaSil
    Yanıtlar
    1. Sayılmaz. MVC'de Seperation of Concerns (SOC) sağlanmaktadır, yani bir koddaki ilgili bölümleri birbirinden ayırmayı sağlanmaktadır, ancak SOC katmanlı mimari demek değildir. Özellikle büyük projelerde katmanlı mimari kullanmak daha doğrudur.

      Sil
  2. MVC tek başına çok katmanlı bir mimari sayılır mı ? Yoksa soğan veya 3 katmanlı mimari gibi bir yazım şekli belirlemek mi gerekiyor.

    YanıtlaSil
  3. hocam connection stringi nerede belirttiniz ?

    YanıtlaSil
    Yanıtlar
    1. Connection String, presentation'da web.config'de yer almaktadır.

      Sil
    2. Bir sorum daha olacak dbcontext i normalde direk kulanıyorduk ve verileri istediğimiz her katmanda çekebiliyorduk. Örnepin bir partial view de statik bir veriyi db den çekip listeleyebiliyorduk. Peki bu durumda ne yapmamız gerekiyor ? View içerisinde db den veri çağırma işlemini nasıl yapacağız ?

      Sil
    3. View için veritabanına ulaşmak doğrı bir yöntem değil, View'in görevi ona verilen veriyi kullanıcı dostu bir şekilde yorumlamak ve göstermektedir. MVC'de View'e gelen data Model'dir, Model'i için veriyle donatmak görevi ise controller'e aitdir, ancak Katmanlı mimaride Controller'in de doğrudan veritabanına ulaşması yasaktır, bu durumda Controller veriyi Business Katmanından ister, Business Katmanı ise DAL (Infrastructure) dan. en son veritabanına ulaşan ve sorguyu yapan katman DAL katmanıdır. kısaca istek akışı şu şekilde olmalı. PL ==> BLL'den, BLL ==> DAL'dan ve DAL ==> Veritabanından olması lazım.

      Sil
  4. Merhabalar,
    ""mutlilayer.presentation" projesinden ise "multilayer.domain", "multilayer.services" projesine link veriniz." diye söylemişsiniz. burada katettiğiniz referans mı vericez. dll mi ekliycez.

    YanıtlaSil
  5. Merhaba, Evet Link'ten kastım, "Referance" eklemektir.

    YanıtlaSil
  6. Hocam merhaba , bu anlattığınız pattern piyasada ne derecede kullanılıyor. Bununla ilgili çok fazla döküman bulamıyorum (türkçe & ingilizce kaynaklar) acaba yaygın değil mi ? Ve onion arch ile identity kullanımını yapacak mısınız ?

    YanıtlaSil
  7. Merhaba, Eski 3 katmanlı mimariye göre daha güncel bir mimaridir, Yeni olduğu için eskisi kadar yaygın değildir. İngilizce Kaynak bolca vardır diye biliyorum internette, bu linkten mimarin kendi dilinden anlatımını inceleyebilirsiniz. http://jeffreypalermo.com/blog/the-onion-architecture-part-1/
    Identity Kullanımını ilk fırsatta yazacağım.

    YanıtlaSil
  8. Hocam öncelikle merhabalar anlatımınız için çok teşekkürler. Yaptığınız uygulamayı bende adım adım tekrar ederek yaptım ve mimari konusunda baya katkı sağladı. Yalnız bir sorum olacak konuyla alakalı ama farklı bir bakış açısı MVC mimarisi dediğiniz gibi katmanlı mimari sayılmaz büyük projeler için elbette katmanlı mimari kullanımı çok gerekli özellikle repository ve unit of work yapısının sağlıklı çalışması için fakat MVC de bulunan Contoller ile BLL de gerçekleşek olan işlmeler entity freamwork sayesinde hızlı bir şekilde gerçekleşiyor Entity'lerin olduğu Model Katmanını anlıyorum tamam fakat CRUD işlemleri için MVC projesi için de Repository ve Unit of Work için klasör oluştursak ve BLL katmanına ihtiyac kalmasa mantıklı olmaz mı ? Yani Projede sadece entity'lerin olduğu ClassLibrary projesi olsa ve birde MVC Projesi olsa daha efektif olmaz mı?

    YanıtlaSil
    Yanıtlar
    1. Merhaba Bora, BLL katmanı Soğan mimarisinde Application Core kısmında kalan bir katmandır.ürünün tüm İş kuralları ve İşin tanımlayan katman aslında BLL katmanıdır. Presentation katmanından doğrudan DAL katmanına referans verilmesi doğru değildir. bunun dışında BLL aynı kalır ama Presentation değişir, örneğin Uygulamana bir mobil ara yüz eklemek istediğin zaman BLL'in değişmeyecektir. aynı BLL yeni presentation için de kullanılacaktır. yanı kısacası dediğiniz senatiyo katmanlı bir mimari olmaz.

      Sil
  9. Hocam Merhaba. Bu mimariyi Web API için kullanabiliriz demi. Presantation katmanını mvc değil de api projesi olarak oluşturursak diğer katmanlar aynı kalabilir mi ?

    YanıtlaSil
    Yanıtlar
    1. Merhaba Aziz, Bu Mimaride WebAPI UI katmanı olarak değil ancak Business Katmanında yeni bir proje olarak ekleyip business katmanının API'si olarak diğer kullanacak Client'lara (Mobil, Angular vs.) sunulabilir.

      Sil
    2. Teşekkürler hocam. Adamsınız

      Sil
  10. Merhaba , makale için teşekkürler. Faydalı bir yazı olmuş fakat kafama takılan bir nokta var. Makalenin başında "merkezi katmanlar ondan sonrakilere bağlı olamaz" demişsiniz. Verilen örnekte ise application service katmanı infrastrcuter katmanına bağlı. Bu sebeple kafam karıştı biraz.

    YanıtlaSil
    Yanıtlar
    1. Onion Architecture içinde, katmanlar iç içe geçmiş bir yapıya sahiptir ve genellikle dıştaki katmanlar içteki katmanlara bağlıdır, ancak içteki katmanlar dıştaki katmanlara bağımlı değildir. Ancak, iç katmanlardan biri olan uygulama katmanı, veritabanından veri almak veya veritabanına veri kaydetmek gibi işlemler için altyapı katmanına (dış katman) bağımlı olabilir.

      Bu genellikle iç katmanlardaki iş mantığını dış katmanlardan bağımsız tutmak amacıyla yapılır. İç katmanlar genellikle interface'leri veya contract'ları tanımlar ve dış katmanlar, örneğin altyapı katmanı, bu interface'leri uygular. Böylece iç katmanlar, iş mantığını korurken dış sistemlerle etkileşimde bulunabilir.

      Bu durumda, uygulama katmanı, veri erişimi gibi konular için interface'leri tanımlar ve altyapı katmanı bu interface'leri uygular. Bu, içteki iş mantığını dıştaki sistemlerden izole etmeye ve esnek bir yapı sağlamaya yardımcı olur.
      Bu örneği yıllar önce yapmıştım, hem karmaşık yapmamak için, hem application katmanı istisna olduğu için çok gerek duymamıştım, fakat tamamen izole edelim dersek yukarıda bahsettiğim gibi yapılabilir.

      Sil

Yorum Gönder

Bu blogdaki popüler yayınlar

C# ve Asp.net MVC'de Çok katmanlı Soğan mimarisinde (Onion Architecture) Asp.Net Identity Kullanımı

Bir önceki makelemde (Buraya Tıklayınız)   Soğan mimarisini oluşturduk ve Ninject kullarak Katmanlar arasında bağımsızlığı sağladık. Bu makalede ise ASP.NET Identity 2.x kullanarak Güvenlik ve Üye yönetimini katmanlı mimaride nasıl sağlayabileceğimizi göstereceğim. bu makalede amaç ASP.NET Identity hakkında bilgi vermek veya nasıl kullanıldığını anlatmaktan ziyade, bir katmanlı mimaride katmanlar arası ilişkiyi bozmadan ASP.NET Identity'yi kullanıma sunulmasıdır. Sorun Nedir? Yeni bir web uygulaması oluşturulduğunda ve üye yönetimi olarak ASP.NET Identity kullanıldığında varsayılan olarak veri tabanı erişimini doğrudan PL katmanından ayarlamaktadır, ancak katmanlı mimaride PL katmanın veri tabanına veyahut DAL katmanına doğrudan erişmesi yasaklanmıştır. ASP.NET Identity'nin çalışabilmesi için IdentityDbContext 'i geliştirmiş bir Context (Entity Framework) sınıfına ihtiyaç duyar. Önemli Not: ASP.NET Identity varsayılan olarak EF kullanarak veri tabanına erişim

Güncel İl, İlçe ve Okullar Listesi (excel ve sql)

Bu Yazımda, en son ve güncel iller, ilçeler ve okullar listesini yayınlıyorum. bu yayında hem excel ve hemde sql sorgularını yayınlanmıştır. NOT1: il ve ilçeler listesi iç işleri bakanlığının sitesinden alınmıştır. eğer değişiklik olursa bu linkten kendiniz de alabilirsiniz, ancak veritabanına kendiniz yazmanız gerekecektir. İÇ İŞLERİ BAKANLIĞI - İL ve İLÇELER LİSTESİ NOT2: Okullar listesi Milli Eğitim Bakalığı sitesinden alınarak Excele aktarılmıştır, daha sonra Excelden Veritabanında eşleşen il ve ilçeri bulunarak doğru bir şekilde kaydedilmiştir. Toplam 3250 Adet okul. Milli Eğitim Bakanlığı - Okullar Ful Listesi Tablo düzeni şu şekildedir. İl, İlçe ve Okul için SQL İlişki diagramı Yukarıdaki Resimde görüldüğü üzere; Her İl'in (City Tablosu) 0 veya birden fazla İlçesi var, ve her İlçenin 0 veya daha Çok Okulu vardır. Gördüğünüz üzere Okul ve İl arasında bağlantı eklenmemiştir, okul olduğu il zaten ilçe tablosu vasitasiyla belirlenebiliniyor, böylece veri taba