Getting Started with Clean Architecture using ASP.NET Core – 01

Biliyoruz ki maintenance işlemi, uygulamanın kendisini yazmaktan her zaman daha maliyetli bir operasyondur. Her ne yaparsak yapalım, değişmeyen tek gerçek maintenance maliyetidir, değil mi?

Bu maliyeti etkileyen en önemli etkenlerden birisi de, uygulamanın architecture’ının seçimidir. Edindiğim geçmiş tecrübelerim doğrultusunda iki farklı makale serisi olarak, “iyi bir architecture design edebilmek için nelere dikkat etmeliyiz” ve “clean architecture nedir” konularına değinmeye çalışacağım.

Peki,

Robert C. Martin, iyi bir architecture’ı Clean Architecture kitabında, aşağıdaki gibi tanımlamaktadır;

Good architecture makes the system easy to understand, easy to develop, easy to maintain, and easy to deploy. The ultimate goal is to minimize the lifetime cost of the system and to maximize programmer productivity.

Zaten bir architecture design ederken temel gayelerimiz de bunlardır, değil mi? Sanırım clean architecture’ın önemi, bu gayeler ile başlıyor. Bana göre iyi bir architecture design edebilmek için dikkat etmemiz gereken başlıca iki temel kavram bulunmaktadır. “Coupling” ve “Separation of Concerns” kavramları.

Haydi hatırlayalım…

Coupling

Kısaca coupling’in tanımını hatırlayalım. Coupling için, modüller arasındaki bağımlılıkların derecesinin bir ölçütüdür diyebiliriz.

Bildiğimiz gibi genelde bunu ise, loosely coupling ve tightly coupling olarak ele alırız. İyi bir architecture’a sahip uygulama geliştirmek istiyorsak, uygulamamızı/modüllerimizi loosely coupled olarak geliştirmemiz gerekmektedir.

Özetle, modüllerimiz birbirlerine “sıkı sıkıya” bağlı olmamalıdırlar. “Genişletilebilir” ve kolaylıkla “değiştirilebilir” olmalıdırlar.

Separation of Concerns (SoC)

Bir diğer önemli kavram ise separation of concerns(SoC). Buradaki temel hedef, design veya kod tarafındaki farklı concern’lerin aynı yerde handle edilmeye çalışılmasını engellemektir. Bu konu aslında, SOLID prensiplerinden birisi olan “Single Responsibility” prensibi ile oldukça ilgilidir.

Örneğin, business logic içerisinde data access işlemlerini de handle etmek veya UI representation işlemlerini de gerçekleştirmek gibi düşünebiliriz. Bir süre sonra ortaya binlerce satırlık god class’lar ve spagetti kodlar çıkmaya başlıyor.

Sanırım spagetti sadece bir besin öğesi iken güzel.

Bu tarz işlemler, SoC‘ü ihlal etmektedir. Hatırlayalım, iyi bir architecture, kolay “maintain” edilebilmeli, daha az “coupled” ve kolayca “extend” edilebiliyor olmalıdır.

SoC, özellikle layered bir architecture design edebilmek için oldukça önemli bir kavramdır. Modüllerimizin olabildiğince loosely coupled ve high cohesion olmasına dikkat etmeliyiz. İçlerindeki sorumluluk ilişkisi olabildiğince yüksek ve alakalı olmalıdır, farklı concern’ler ile ilgilenmemelidirler.

Layered Architecture

Layered architecture, biz yazılımcıların hayatında büyük bir yere sahiptir sanırım. En azından benim için öyle.

Layered architecture arkasındaki ana prensip’lerden birisi, SoC‘dür. Hedef ise database, domain veya UI code’unun birbirlerine karışmasını engellemek ve sorumluluklarını ayırmaktır.

Layered architecture genelde, yukarıdaki gibi bir form’da karşımıza gelmektedir. Buradaki dependency akışı ise, presentation’dan, data access layer’a doğrudur. Aslında bakarsanız, her ne kadar SoC‘ü ve loosely coupling’i sağlamaya çalışıyor olsak da, layer’lar arasında bir hiyerarşi, dependency söz konusudur.

Clean architecture’ın arkasındaki ana prensip’lerden birisi ise, Dependency Inversion prensibidir. Bu prensip ile mimari içerisindeki modüllerimizi birbirinden ve farklı teknolojilerden daha decoupled bir hale getirebilmek mümkündür.

Görsele bir de bu şekilde bakalım.

Uncle Bob‘un “Clean Architecture” olarak adlandırdığı concept veya Alistair Cockburn‘un “Hexagonal Architecture” olarak adlandırdığı design yukarıdaki gibidir. Dependency inversion uygulanmış, domain-centric bir design.

Yukarıdan aşağıya doğru hiyerarşik bir akış yerine, application domain’i, yani business logic ve domain modellerin içerisinde barındığı bölümü core olarak ortada konumlandırmaktadır. Ayrıca diğer tüm modüllerin bağımlılıklarını da tersine çevirmektedir. Dependency akışı ise her zaman ortaya, yani core kısma doğru gerçekleşmektedir. Plug-in yapısı gibi düşünebiliriz. Tak, çıkart.

Bu yaklaşım ise bize, herhangi bir parçayı istediğimiz gibi kolaylıkla extend edebilme, test edebilme ve değiştirebilme yeteneklerini kazandırmaktadır.

Neden Clean Architecture?

  1. Değiştirilmesi zor: Business ihtiyaçları genelde bitmez. Her zaman uygulamamıza yeni özellikler eklemeye ihtiyaç duyarız. Eğer düzgün bir architecture inşa etmez isek, bu değişiklikleri implemente etmemiz hem zorlaşacak, hem de projeyi daha karmaşık bir hale getirecektir. Ayrıca projeyi maintain edilebilmesi zor ve kırılganlığı fazla bir yapıya doğru itmiş olabiliriz.
  2. Test etmesi zor: İyi inşa edilmemiş bir architecture’da, test edilebilirlik bi o kadar zordur. Ya business logic her tarafa duplicate bir şekilde yayılmıştır, ya UI ile iç içe geçmiştir, yada component’ler birbirlerine sıkı sıkıya bağlıdır. Kırılganlık çok fazla olunca, genişletilebilirlik de zorlaşıyor ve yaptığımız bir değişikliğin nereleri etkilediğini görebilme ve test edebilme işlemlerimiz de zorlaşıyor.
  3. Database’lerden, framework’lerden ve external library’lerden bağımsızlık: Bir diğer önemli konu ise, core application domain layer’ın database’lerden, framework’lerden veya external librirary’lerden olabildiğince soyut tutabilmektir. Buda bize, istediğimiz bir zaman farklı bir yöntemle, farklı bir framework’le ilerleyebilme, yani flexibility yeteneğini kazandıracaktır.

Yukarıdaki makas’ı, bir bıçak ile yer değiştirmeye çalıştığımızı düşünebiliyor musunuz?

İyi Bir Architecture’a Sahip Uygulama Nedir?

Genelde iyi bir architecture’a sahip uygulamadan, aşağıdaki concern’leri cover edebiliyor olmasını bekleriz.

  1. Testability: Geliştirdiğimiz kod/component parçalarının, bireysel olarak her birinin kolayca test edilebilir olması gerekmektedir.
  2. Maintainability: Bildiğimiz gibi her şeyden önce uygulamanın sürdürülebilir olması büyük bir önem taşımaktadır. Kolay bir şekilde maintenance işlemlerini gerçekleştirebiliyor olmalıyız.
  3. Extendability: Mevcut modülleri, component’leri yıkmadan/değiştirmeden, onları kolaylıkla genişletebiliyor olmalıyız.
  4. Reusability: Geliştirmiş olduğumuz modüller, tekrar kullanılabilir olmalıdır.
  5. Readability: Ekibe yeni bir developer katıldığında, kolayca projeye adapte olabilmesi de, diğer maddeler kadar büyük bir önem taşımaktadır.

Yukarıdaki görsele tekrar baktığımızda diğer aletleri etkilemeden makas’ı, bıçak ile  değiştirebilmenin ne kadar da daha kolay olacağını görebiliriz.

Clean Architecture Konsepti

Peki, şimdi clean architecture konsept’inin detaylarına bir bakalım.

Öncelikle aşağıda görüyor olduğumuz Hexagonal architecture, clean architecture konsept’inin uygulanmış bir örneğidir.

“Application Domain” layer, en iç core layer’dır. Architecture’ın kalbi. Herhangi bir dependency’si bulunmamaktadır. Database, UI vb. framework’lerden isolated bir şekilde ortada konumlanmaktadır. İçerisinde başlıca domain entity’lerini, use-case’leri ve external interface’leri barındırmaktadır.

Etrafında ise, “input” ve “output” port’ları bulunmaktadır. Etrafındaki implementasyon’lar ise, “adapter” olarak adlandırılmaktadır. Bu adaptör’ler ise, port’ları implemente etmektedir.

Entities

  • Uygulamamızın business objeleridir.
  • Bu objeler, hiç bir değişiklikten etkilenmemelidirler.

Use-Cases

  • Her bir use-case, business action’larımızı temsil etmektedir.
  • Sistemdeki tüm business rule’larını implemente ve encapsulate ederler.
  • Her biri, single responsibility prensibine uymaktadır. Yani, tek bir concern ile ilgilenmektedir.
  • Bir use-case içerisinde herhangi bir data’ya ihtiyaç var ise, “input” port’ları aracılığı ile alınmaktadır. Use-case data’nın nereden geldiği ile ilgilenmez.
  • Data’yı persist etmek veya bir yerlere göndermek için ise, “output” port’ları kullanılmaktadır.

Örneğin yukarıdaki project yapısına bakarsak, “Services” klasörü altında sadece “MovieService” class’ı bulunmakta. Sizce bu service’in, ne iş yaptığı yeterince açık mı?

Birde use-case yaklaşımının uygulandığı, single responsible olan service’lere bir bakalım.

MovieUseCases” klasörü altında, “CreateMovieHandler” ve “GetBestMoviesForKidsHandler” use-case’leri bulunmakta. Şimdi business use-cases’lerinin ne olduğu ve sorumlulukları daha açık değil mi? God classes vs single responsible classes.

Interfaces/Adapters

  • Core layer’da tanımlanan interface’lerin implementasyonlarıdır.
  • Data’yı persist veya retrive edecek olan bir kısım olabilir. “Domain” ile “Infrastructure” arasındaki bir translator gibi düşünebiliriz.

Yukarıdaki görsele tekrar baktığımızda, dependency’i invert edebilmek için yani bağımlılığı ters çevirebilmek adına port’ların, domain layer’da tanımlanmış olan interface’ler olduğunu görebiliriz.

Etrafındakiler ise port’ların implementasyonları’dır. Yani adapter’ler. Bu bir “Infrastructure” kısmında SQL Server olabilir, veya bir NoSQL implementasyonu olabilir. “Presentation” kısmında bir Web UI veya bir REST API olabilir.

Gördüğümüz gibi konsept olarak core domain layer, tamamen etrafındaki modüller’den, teknolojilerden isolated ve decoupled bir şekilde ortada konumlanmaktadır. Tüm dependency flow’u ise, içeriye doğru gerçekleşmektedir.

Son sözler…

Bu noktaya kadar “neden clean architecture” konusu ve genel konseptten bahsetmeye çalıştım. Biliyorum bu makale biraz soyut gelebilir, bu sebeple makalenin bir sonraki bölümünde ise clean architecture’ın .NET Core ile implementasyonundan bahsediyor olacağım.

Referanslar

https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html
https://docs.microsoft.com/en-us/dotnet/architecture/modern-web-apps-azure/architectural-principles?WT.mc_id=DT-MVP-5003382

https://pusher.com/tutorials/clean-architecture-introduction
https://www.freecodecamp.org/news/a-quick-introduction-to-clean-architecture-990c014448d2/
https://slides.com/gokgokalp/aspnet-core-clean-architecture#/

Gökhan Gökalp

View Comments

  • Harika bir yazı olmuş. Uzun zamandır bu konuda bilgi ihtiyacı olan bir kişi olarak yazının tamamını bir solukta su içer gibi okudum. En sonunda en heyecanlı kısmında bitti :( Bir sonraki yazıyı sabırsızlıkla bekliyorum elinize sağlık. Ayrıca böyle bir konuda Türkçe kaynak kazandırdığınız için ayrıca tebrik ederim.

  • Türkçe kaynak bu konuda sıkıntılıydı, uzun zamandır yeni başlayacağımız bir proje için bu mimariyi inceliyorum. Sizin anlatımınızla bakmak da bloğunuzu takip eden ben açısından güzel oldu. Github'da Steve Smith'e sorduğum soruyu size de sormak isterim. Bu mimari db bağımsız diyoruz, okuyoruz ancak gerçek bir kullanım veya örneği yok. Yani hem redis, hem mssql hem de mongo kullanacaksak ne yapacağız? Teoride bir çok şey yerine oturmasına rağmen, pratikte herşeyi ayırmak adına birbirinin aynısı olmayan ancak birbirine aşırı benzer kodlar çıkmıyor mu?

    • Merhaba, öncelikle güzel yorumunuz için teşekkür ederim. Evet, eğer farklı ihtiyaçlar karşısında farklı service'ler kullanacaksak, onları da bir bir implemente etmek gerekiyor. En azından ben öyle yapıyorum. Dediğiniz gibi benzer kodlar/kod tekrarları ortaya çıkabiliyor, oradaki o ince ayrımı iyi yapmak gerekiyor bence. Hiç kod tekrarına düşmemek adına abstract yapa yapa bu seferde mimariyi over engineering yoluna doğru da itebiliyoruz açıkcası. Yer yer bu tradeoff'u seçmek gerekiyor sanırım. Independency vs Reusability? Kesin bir cevabı yok bence. :) Sizce?

  • Thank you very much for the nice and clean article. It's a good entrance to a very important subject, "clean architecture". We're waiting for the next part of this series. Don't make us wait too long :)

  • Çok yararlı bir kaynak kazandırmışsınız, tek solukta okudum. Çok teşekkürler. İkincisini beklemedeyim.

  • Paylaşım için teşekkürler, 2. makaleyi okumadan önce bunu yeniden okumak iyi oldu.
    Emeğine sağlık

Recent Posts

Containerized Uygulamaların Supply Chain’ini Güvence Altına Alarak Güvenlik Risklerini Azaltma (Güvenlik Taraması, SBOM’lar, Artifact’lerin İmzalanması ve Doğrulanması) – Bölüm 1

{:tr}Bildiğimiz gibi modern yazılım geliştirme ortamında containerization'ın benimsenmesi, uygulamaların oluşturulma ve dağıtılma şekillerini oldukça değiştirdi.…

8 ay ago

Identity & Access Management İşlemlerini Azure AD B2C ile .NET Ortamında Gerçekleştirmek

{:tr}Bildiğimiz gibi bir ürün geliştirirken olabildiğince farklı cloud çözümlerinden faydalanmak, harcanacak zaman ve karmaşıklığın yanı…

1 yıl ago

Azure Service Bus Kullanarak Microservice’lerde Event’ler Nasıl Sıralanır (FIFO Consumers)

{:tr}Bazen bazı senaryolar vardır karmaşıklığını veya eksi yanlarını bildiğimiz halde implemente etmekten kaçamadığımız veya implemente…

2 yıl ago

.NET Microservice’lerinde Outbox Pattern’ı ile Eventual Consistency için Atomicity Sağlama

{:tr}Bildiğimiz gibi microservice architecture'ına adapte olmanın bir çok artı noktası olduğu gibi, maalesef getirdiği bazı…

2 yıl ago

Dapr ve .NET Kullanarak Minimum Efor ile Microservice’ler Geliştirmek – 02 (Azure Container Apps)

{:tr}Bir önceki makale serisinde Dapr projesinden ve faydalarından bahsedip, local ortamda self-hosted mode olarak .NET…

2 yıl ago