Onion Architecture

Merhaba arkadaşlar, bu yazımızda çok katlı mimaride kullanılan Onion Architecture yani Soğan Mimarisi ile ilgili demo yapacağız.

Öncelikle güzel ama ingilizce olan şu kaynağı baştan paylaşayım. Laf söz olmasın sonra 🙂 Ben de bu işin öncülerinden olmadığım için önce okumuş olurum daha sonra o makalelere uygun örnekler yaparım. Yoksa bir Bora Kaşmer bir Kadir Murat Başeren olmayı biz de isteriz 🙂

Konumuza gelecek olursak, yukarıda paylaştığım linkten de görüleceği üzere (Data-Repository-Service-UI olmak üzere) 4 katmanlı bir yapımız var. Daha fazla katmanlı bir yapınız olacaksa bile (birden fazla havuzunuz (repository) yada data katmanınız varsa)  işleyişi itibari ile yine 4 katmanlı bir yapı sunar. Siz UI’dan servis katmanına bir istekte bulunursunuz, servis katmanı gelen bu istek sizin işlemleri yaptırdığınız havuza düşer, o havuz data katmanına erişir ve size sonucu aynı yoldan geri döner ve bunu kullanırsınız. Dolayısıyla UI katmanı tek bir yerden aldığı örnekle uygulama içerisindeki diğer fonksiyonları yönetebilir.

4

Mimari yapı, uygulama çalışma şekli ile UI => Service => Repository => Data olarak erişimini sağlasa da, UI katmanı referans olarak Repo ve Data katmanlarını da görecektir.

cats.jpg

Örnek olarak ele alacağımız uygulamanın, şekil olarak görüntüsü yukarıdaki gibi olacaktır. Öncesinde paylaşmanın daha uygun olacağını düşündüm. Yoksa elimde yapılmışı var deyip bırakmayacağım 🙂

Uygulama örneği ile ilgili şu şekildeki bilgileri de versem iyi olabilir;

  • Uygulama .net 4.5.2 framework’ünde geliştirildi
  • Class Library’ler .net framework alt yapısında (core yada standart seçmiyoruz)
  • UI olarak bir console uygulaması açıldı
  • EntityFramework 6.0 versiyon orm kullanıldı
  • vs2017 ide’sinde proje oluşturuldu
  • son olarakta entity framework ün bize sunduğu 3 yaklaşımdan (model first,db first,code first) code first ü tercih ediyoruz.

Örnekte hangi katmanda ne yapacağımızdan bahsedecek olursak;

1-Data Katmanı

  • varlık(entity) dediğimiz sınıflarımıza ait field’lar (tablo =>kolon isimleri).
  • Veritabanında oluşturulacak olan varlıklara ait property’ler in olduğu Context sınıfımız(tablolar)
  • Ana işlemlerde ortak bir yapıda dönüş yapılması için kullanacağımız Mesaj sınıfı

Mesaj class ı hariç diğerleri ile package manager console’da enable-migrations ve update-database diyerek veritabanında tabloların oluşmasını sağlayacağız. Mapping işlemi ile buna da gerek kalmaz fakat o başka bir masanın konusu olsun 🙂 Sınıfların içerikleri;

BaseModel sınıfı;

       [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public Guid ID { get; set; }
        public bool IsActive { get; set; }

 

Category sınıfı;

        
      public Categories()
        {
            this.Products = new HashSet();
        }

        public string Name { get; set; }
        public string Desc { get; set; }

        public virtual ICollection<Products> Products { get; set; }

 

Product sınıfı;

        
        public string Name { get; set; }
        public double Quantity { get; set; }
        
        public Guid CategoryID { get; set; }
        [ForeignKey("CategoryID")]
        public virtual Categories Category { get; set; }

 

Mesaj sınıfı;

        
        public Guid ResultID { get; set; }
        public string Message { get; set; }

        public bool IsSuccess
        {
            get
            {
                if (ResultID != Guid.Empty)
                {
                    return true;
                }
                return false;
            }
        }

 

Context sınıfı;

        
    public OnionArchContext()
        {
            //home connection
            Database.Connection.ConnectionString 
   = "Server=EROLAKGUL\\BESIKTAS;Database=OnionArch;uid=sa;pwd=istanbul;";
        }

        public DbSet<Categories> Category { get; set; }
        public DbSet<Products> Product { get; set; }


        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
        }

 

2-Repository Katmanı

Bu katmanda, generic tipte bir varlık alacak olan soyut sınıf kullanacağız. Bu sınıfta tanımlanan abstract metotları,bu sınıfı derive eden class larda ortak olarak kullanmak için bunu yapıyoruz. Bu kısım, interface kullanılarak da yönetilebilirdi fakat aynı kodları, interface ten kalıtılmış bir sınıfa yazıp yönetmek istemedim. abstract’ta iyidir onu sevelim. Bu sınıfta yapıcı metot içerisinde data katmanına erişebilmek için context sınıfından bir nesne türetilecektir aynı zamanda. Context içindeki varlıkları temsil eden dbset class ı da abstract sınıfına gönderilecek olan entity ile set edilecek. Yani category gönderilecekse category, product gönderilecekse product gibi davranacak.

DataAccess olarak kullanacağımız bir de repository class larımız var. Burada her bir tablomuz için bir sınıf oluşturup, abstract metottan kalıtım alacağız.

Abstract Class;

    public abstract class ServiceBase<TEntity> where TEntity : BaseModel
    {
        protected OnionArchContext context;
        protected DbSet dbset;
        protected Messages result;

        public ServiceBase()
        {
            context = new OnionArchContext();
            result = new Messages();
            dbset = context.Set(<TEntity>);
        }
        
        public abstract Messages Insert(TEntity dto);

        public abstract Messages Update(TEntity dto);

        public TEntity FindByID(Guid ID)
        {
            return dbset.Where(x => x.ID == ID && x.IsActive).FirstOrDefault();
        }
    }

 

Repository class;

 
public class CategoryRepository : ServiceBase<Categories>
    {
        public override Messages Insert(Categories dto)
        {
            result.ResultID = Guid.Empty;

            if (context.Category.Any(x => x.IsActive && x.Name == dto.Name))
            {
                result.Message = "Benzer Kayıt Var..";
                return result;
            }

            bool isExist = (dto == null) ? false : true;

            if (isExist)
            {
                dto.IsActive = true;
                try
                {
                    dbset.Add(dto);
                    context.SaveChanges();
                    result.ResultID = dto.ID;
                    result.Message = "Kaydedildi..";
                    return result;
                }
                catch (Exception ex)
                {
                    result.Message = ex.Message;
                }
            }
            return result;
        }

        public override Messages Update(Categories dto)
        {
            result.ResultID = Guid.Empty;

            if (dbset.Any(x => x.ID == dto.ID))
            {
                Categories c = FindByID(dto.ID);
                c.IsActive = dto.IsActive;
                c.Name = (String.IsNullOrEmpty(dto.Name)) ? c.Name : dto.Name;
                c.Desc = (String.IsNullOrEmpty(dto.Desc)) ? c.Desc : dto.Desc;

                try
                {
                    result.Message = "Başarıyla güncellendi..";
                    context.SaveChanges();
                    result.ResultID = dto.ID;
                }
                catch (Exception ex)
                {
                    result.Message = ex.Message;
                }
                return result;
            }
            result.Message = "Böyle bir kayıt bulunamadı..";
            return result;
        }
    }

 

3-Service Katmanı

Bu katman bir router görevi görür. UI’dan gelen isteğe göre repository katmanındaki classlara erişim sağlar.

ServicePoint sınıfı;

        
private CategoryRepository categoryRepository;
        private ProductRepository productRepository;

        public CategoryRepository CategoryRepository { get { return categoryRepository ?? new CategoryRepository(); } }
        public ProductRepository ProductRepository { get { return productRepository ?? new ProductRepository(); } }

 

4- UI Katmanı

Bu katman yapılacak olan işleri diğer katmana bildirir.

Console uygulaması main metot =>

            ServicePoints points = new ServicePoints();
            try
            {
                Categories cm = new Categories
                {
                    Desc = "Ev Tekstili",
                    Name = "Tekstil",
                    IsActive = true
                };

                Messages m = points.CategoryRepository.Insert(cm);
                Console.WriteLine(m.ResultID + " Nolu Kayıt:" + m.Message);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Hata :" + ex.Message);
            }
            Console.ReadLine();

 

ServicePoints sınıfından aldığımız nesne ile category sınıfına ait repository e erişiyoruz,repository’de data içindeki context sınıfına erişerek select,insert,update,delete işlemlerini gerçekleştiriyor.

Anlatacaklarımız bu kadar.. Onion Architecture yapısını küçük bir örnek ile göstermek istedim. Çok kod yazıyormuş gibi olabilir ama yönetimsel anlamda hem sizin için hem de sizden sonra gelecekler için anlaşılması … tamam tamam babaannelik yapmayacağım, düzgün yazın ulen kodları 🙂

Daha detaylı bilgi için şu makaleyi de bırakıyorum.

İyi çalışmalar..

Bir Cevap Yazın

Aşağıya bilgilerinizi girin veya oturum açmak için bir simgeye tıklayın:

WordPress.com Logosu

WordPress.com hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Twitter resmi

Twitter hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Facebook fotoğrafı

Facebook hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Connecting to %s