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…
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.
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, 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.
Yukarıdaki makas’ı, bir bıçak ile yer değiştirmeye çalıştığımızı düşünebiliyor musunuz?
Genelde iyi bir architecture’a sahip uygulamadan, aşağıdaki concern’leri cover edebiliyor olmasını bekleriz.
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.
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.
Ö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.
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.
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.
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#/
{:tr} Makalenin ilk bölümünde, Software Supply Chain güvenliğinin öneminden ve containerized uygulamaların güvenlik risklerini azaltabilmek…
{: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.…
{:tr}Bildiğimiz gibi bir ürün geliştirirken olabildiğince farklı cloud çözümlerinden faydalanmak, harcanacak zaman ve karmaşıklığın yanı…
{:tr}Bazen bazı senaryolar vardır karmaşıklığını veya eksi yanlarını bildiğimiz halde implemente etmekten kaçamadığımız veya implemente…
{:tr}Bildiğimiz gibi microservice architecture'ına adapte olmanın bir çok artı noktası olduğu gibi, maalesef getirdiği bazı…
{:tr}Bir önceki makale serisinde Dapr projesinden ve faydalarından bahsedip, local ortamda self-hosted mode olarak .NET…
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.
Çok teşekkür ederim güzel yorumunuz için. Şuan 2. kısmını yazmaktayım. :)
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 :)
Thanks mate :) I'm working on it. :)
Thanks for sharing
Çok yararlı bir kaynak kazandırmışsınız, tek solukta okudum. Çok teşekkürler. İkincisini beklemedeyim.
Çok teşekkür ederim değerli yorumunuz için.
Paylaşım için teşekkürler, 2. makaleyi okumadan önce bunu yeniden okumak iyi oldu.
Emeğine sağlık
Ben teşekkür ederim. :)