paint-brush
Bir Ön Uç Uygulamasında Temiz Bir Mimariyi Test Etmek - Mantıklı mı?ile@playerony
10,637 okumalar
10,637 okumalar

Bir Ön Uç Uygulamasında Temiz Bir Mimariyi Test Etmek - Mantıklı mı?

ile Paweł Wojtasiński21m2023/05/01
Read on Terminal Reader
Read this story w/o Javascript

Çok uzun; Okumak

Ön uç geliştiricileri ölçeklenebilir, bakımı yapılabilir mimariler oluşturma zorluğuyla karşı karşıyadır. Önerilen mimari fikirlerin birçoğu gerçek dünyadaki üretim ortamlarında hiçbir zaman uygulanmamış olabilir. Bu makale, ön uç geliştiricilere, sürekli gelişen web sitesi geliştirme dünyasında gezinmek için ihtiyaç duydukları araçları sağlamayı amaçlamaktadır.
featured image - Bir Ön Uç Uygulamasında Temiz Bir Mimariyi Test Etmek - Mantıklı mı?
Paweł Wojtasiński HackerNoon profile picture

Dijital ortam geliştikçe modern web sitelerinin karmaşıklığı da artıyor. Daha iyi kullanıcı deneyimi ve gelişmiş özelliklere olan talebin artmasıyla birlikte ön uç geliştiriciler ölçeklenebilir, bakımı yapılabilir ve verimli mimariler oluşturma zorluğuyla karşı karşıya kalıyor.


Ön uç mimarisiyle ilgili mevcut çok sayıda makale ve kaynak arasında önemli bir kısmı Temiz Mimari ve onun uyarlanmasına odaklanmıştır. Aslında, ankete katılan yaklaşık 70 makalenin %50'sinden fazlası, ön uç geliştirme bağlamında Temiz Mimariyi tartışıyor.


Bilgi zenginliğine rağmen göze çarpan bir sorun varlığını sürdürüyor: Önerilen mimari fikirlerin birçoğu gerçek dünyadaki üretim ortamlarında hiçbir zaman uygulanmamış olabilir. Bu durum bunların etkililiği ve pratik senaryolarda uygulanabilirliği konusunda şüphelere yol açmaktadır.


Bu endişeden yola çıkarak, Temiz Mimariyi ön uçta uygulamak için altı aylık bir yolculuğa çıktım; bu, bu fikirlerin gerçekleriyle yüzleşmemi ve buğdayı samandan ayırmamı sağladı.


Bu makalede, Temiz Mimarinin ön uçta nasıl başarılı bir şekilde uygulanacağına dair kapsamlı bir rehber sunarak bu yolculuktan edindiğim deneyimleri ve içgörüleri paylaşacağım.


Zorluklara, en iyi uygulamalara ve gerçek dünya çözümlerine ışık tutan bu makale, ön uç geliştiricilere, sürekli gelişen web sitesi geliştirme dünyasında gezinmek için ihtiyaç duydukları araçları sağlamayı amaçlamaktadır.

Çerçeveler

Günümüzün hızla gelişen dijital ekosisteminde geliştiriciler, ön uç çerçeveler konusunda seçim yapmakta zorlanıyor. Bu seçenek bolluğu birçok sorunu giderir ve geliştirme sürecini basitleştirir.


Ancak bu aynı zamanda geliştiriciler arasında bitmek bilmeyen tartışmalara da yol açıyor; her biri kendi tercih ettiği çerçevenin diğerlerinden daha üstün olduğunu iddia ediyor. Gerçek şu ki, hızlı tempolu dünyamızda her gün yeni JavaScript kitaplıkları ortaya çıkıyor ve çerçeveler neredeyse her ay tanıtılıyor.


Böylesine dinamik bir ortamda esnekliği ve uyarlanabilirliği korumak için belirli çerçeveleri ve teknolojileri aşan bir mimariye ihtiyacımız var.


Bu, değişen trendlere ve teknolojik gelişmelere uyum sağlanması gereken ürün şirketleri veya bakım içeren uzun vadeli sözleşmeler için özellikle önemlidir.


Çerçeveler gibi detaylardan bağımsız olmak, üzerinde çalıştığımız ürüne odaklanmamızı ve ürünün yaşam döngüsü boyunca ortaya çıkabilecek değişikliklere hazırlıklı olmamızı sağlar.


Korkma; Bu makale bu ikileme cevap vermeyi amaçlamaktadır.

Fullstack Ekip İşbirliği

Temiz Mimariyi ön uçta uygulama arayışımda, mimarinin minimum ön uç deneyimine sahip olanlar için bile anlaşılır ve sürdürülebilir olmasını sağlamak amacıyla birkaç fullstack ve arka uç geliştiriciyle yakın işbirliği içinde çalıştım.


Dolayısıyla, mimarimizin temel gereksinimlerinden biri, ön uç karmaşıklıkları konusunda bilgili olmayan arka uç geliştiricilerin yanı sıra, kapsamlı ön uç uzmanlığına sahip olmayan tam yığın geliştiriciler için erişilebilir olmasıdır.


Mimari, ön uç ve arka uç ekipleri arasında kusursuz işbirliğini teşvik ederek aradaki boşluğu doldurmayı ve birleşik bir geliştirme deneyimi yaratmayı amaçlıyor.

Teorik Temeller

Ne yazık ki harika şeyler inşa etmek için biraz arka plan bilgisi edinmemiz gerekiyor. Temel ilkelerin net bir şekilde anlaşılması yalnızca uygulama sürecini kolaylaştırmakla kalmayacak, aynı zamanda mimarinin yazılım geliştirmedeki en iyi uygulamalara bağlı kalmasını da sağlayacaktır.


Bu bölümde mimari yaklaşımımızın temelini oluşturan üç temel kavramı tanıtacağız: SOLID ilkeleri , Temiz Mimari (aslında SOLID ilkelerinden gelir) ve Atomik Tasarım . Eğer bu alanlar hakkında güçlü hisleriniz varsa bu bölümü atlayabilirsiniz.

KATI İlkeler

SOLID, geliştiricilere ölçeklenebilir, bakımı yapılabilir ve modüler yazılım oluşturma konusunda rehberlik eden beş tasarım ilkesini temsil eden bir kısaltmadır:


  • Tek Sorumluluk İlkesi (SRP) : Bu ilke, bir sınıfın değişmek için tek bir nedeni olması gerektiğini, yani tek bir sorumluluğa sahip olması gerektiğini belirtir. Geliştiriciler SRP'ye bağlı kalarak daha odaklı, bakımı yapılabilir ve test edilebilir kodlar oluşturabilirler.


  • Açık/Kapalı Prensibi (OCP) : OCP'ye göre yazılım varlıkları genişlemeye açık, değişikliğe kapalı olmalıdır. Bu, geliştiricilerin mevcut kodu değiştirmeden yeni işlevsellik ekleyebilmesi ve böylece hata oluşma riskini azaltabilmesi gerektiği anlamına gelir.


  • Liskov Değiştirme Prensibi (LSP) : LSP, türetilmiş bir sınıfın nesnelerinin, programın doğruluğunu etkilemeden temel sınıfın nesnelerinin yerini alabilmesi gerektiğini ileri sürer. Bu prensip kalıtımın ve polimorfizmin doğru kullanımını teşvik eder.


  • Arayüz Ayrıştırma Prensibi (ISP) : ISP, istemcilerin kullanmadıkları arayüzlere bağımlı olmaya zorlanmaması gerektiğini vurgular. Geliştiriciler daha küçük, daha odaklanmış arayüzler oluşturarak daha iyi kod organizasyonu ve sürdürülebilirliği sağlayabilirler.


  • Bağımlılığı Tersine Çevirme İlkesi (DIP) : DIP, geliştiricileri somut uygulamalar yerine soyutlamalara güvenmeye teşvik eder. Bu prensip daha modüler, test edilebilir ve esnek bir kod tabanını teşvik eder.


Bu konuyu daha derinlemesine araştırmak istiyorsanız, ki bunu yapmanızı şiddetle tavsiye ediyorum, o zaman sorun değil. Ancak şimdilik sunduklarım daha ileri gitmek için yeterli.


Peki SOLID bu makale açısından bize ne veriyor?

Temiz Mimari

Robert C. Martin, SOLID ilkelerine ve çeşitli uygulamalar geliştirmedeki kapsamlı deneyimine dayanarak Temiz Mimari konseptini önerdi. Bu kavramı tartışırken, yapısını görsel olarak temsil etmek için genellikle aşağıdaki şemaya başvurulur:



Yani Temiz Mimarlık yeni bir kavram değil; işlevsel programlama ve arka uç geliştirme dahil olmak üzere çeşitli programlama paradigmalarında yaygın olarak kullanılmaktadır.


Lodash gibi kütüphaneler ve çok sayıda arka uç çerçevesi, SOLID ilkelerine dayanan bu mimari yaklaşımı benimsemiştir.


Temiz Mimari, sistemin anlaşılmasını, bakımını ve değiştirilmesini kolaylaştırmak amacıyla kaygıların ayrılmasını ve bir uygulama içinde bağımsız, test edilebilir katmanların oluşturulmasını vurgular.


Mimari, eşmerkezli daireler veya katmanlar halinde düzenlenmiştir; her birinin net sınırları, bağımlılıkları ve sorumlulukları vardır:


  • Varlıklar : Bunlar uygulama içindeki temel iş nesneleri ve kurallarıdır. Varlıklar genellikle kullanıcılar, ürünler veya siparişler gibi etki alanındaki temel kavramları veya veri yapılarını temsil eden düz nesnelerdir.


  • Kullanım Durumları : Etkileşimciler olarak da bilinen Kullanım Durumları, uygulamaya özel iş kurallarını tanımlar ve Varlıklar ile harici sistemler arasındaki etkileşimi düzenler. Kullanım Durumları, uygulamanın temel işlevlerinin uygulanmasından sorumludur ve dış katmanlardan bağımsız olmalıdır.


  • Arayüz Adaptörleri : Bu bileşenler, iç ve dış katmanlar arasında bir köprü görevi görerek verileri kullanım durumu ve harici sistem formatları arasında dönüştürür. Arayüz Bağdaştırıcıları, uygulamanın veritabanları, harici API'ler ve kullanıcı arayüzü çerçeveleriyle etkileşime girmesini sağlayan depoları, sunumcuları ve denetleyicileri içerir.


  • Çerçeveler ve Sürücüler : Bu en dış katman, veritabanları, kullanıcı arayüzü çerçeveleri ve üçüncü taraf kitaplıkları gibi harici sistemleri içerir. Çerçeveler ve Sürücüler, uygulamanın çalıştırılması ve iç katmanlarda tanımlanan arayüzlerin uygulanması için gerekli altyapının sağlanmasından sorumludur.


Temiz Mimari, bağımlılıkların dış katmanlardan iç katmanlara akışını teşvik ederek temel iş mantığının, kullanılan belirli teknolojilerden veya çerçevelerden bağımsız kalmasını sağlar.


Bu, değişen gereksinimlere veya teknoloji yığınlarına kolayca uyum sağlayabilen esnek, bakımı yapılabilir ve test edilebilir bir kod tabanıyla sonuçlanır.

Atomik Tasarım

Atomik Tasarım, arayüzleri en temel öğelerine ayırıp daha sonra bunları daha karmaşık yapılar halinde yeniden birleştirerek kullanıcı arayüzü bileşenlerini düzenleyen bir metodolojidir. Brad Frost, bu konsepti ilk kez 2008 yılında "Atomik Tasarım Metodolojisi" başlıklı makalesinde tanıttı.


İşte Atomik Tasarım kavramını gösteren bir grafik:



Beş farklı seviyeden oluşur:


  • Atomlar : Düğmeler, girişler ve etiketler gibi arayüzün en küçük, bölünemez birimleri.


  • Moleküller : Birlikte çalışan, formlar veya gezinme çubukları gibi daha karmaşık kullanıcı arayüzü bileşenleri oluşturan atom grupları.


  • Organizmalar : Üstbilgiler veya altbilgiler gibi arayüzün farklı bölümlerini oluşturan molekül ve atomların kombinasyonları.


  • Şablonlar : Organizmaların, moleküllerin ve atomların yerleştirilmesi için bir iskelet sağlayarak bir sayfanın düzenini ve yapısını temsil eder.


  • Sayfalar : Gerçek içerikle dolu, son arayüzü gösteren şablon örnekleri.


Geliştiriciler Atomik Tasarımı benimseyerek modülerlik, yeniden kullanılabilirlik ve kullanıcı arayüzü bileşenleri için net bir yapı gibi çeşitli avantajlardan yararlanabilirler çünkü bu, Tasarım Sistemi yaklaşımını takip etmemizi gerektirir, ancak bu makalenin konusu değil, o yüzden devam edin.

Örnek Olay: NotionLingo

Ön uç geliştirme için Temiz Mimari konusunda bilgili bir bakış açısı geliştirmek amacıyla bir uygulama oluşturma yolculuğuna çıktım. Altı aylık bir süre boyunca bu proje üzerinde çalışırken değerli bilgiler ve deneyimler kazandım.


Sonuç olarak, bu makale boyunca verilen örnekler uygulamayla ilgili uygulamalı deneyimimden alınmıştır. Şeffaflığı korumak için tüm örnekler kamuya açık kodlardan alınmıştır.


Nihai sonucu şu adresteki depoyu ziyaret ederek keşfedebilirsiniz: https://github.com/Levofron/NotionLingo .

Temiz Mimari Uygulaması

Daha önce de belirtildiği gibi, çevrimiçi ortamda Temiz Mimarinin çok sayıda uygulaması mevcuttur. Ancak bu uygulamalarda birkaç ortak unsur tespit edilebilir:


  • Etki alanı katmanı : İşle ilgili modelleri, kullanım senaryolarını ve operasyonları kapsayan uygulamamızın özü.


  • API katmanı : Tarayıcı API'leriyle etkileşimden sorumludur.


  • Depo katmanı : Etki alanı ve API katmanları arasında bir köprü görevi görerek API türlerini etki alanı türlerimizle eşlemek için bir alan sağlar.


  • UI katmanı : Kullanıcı arayüzünü oluşturan bileşenlerimizi barındırır.


Bu ortak noktaları anlayarak Temiz Mimarinin temel yapısını takdir edebilir ve onu özel ihtiyaçlarımıza uyarlayabiliriz.

İhtisas

Uygulamamızın temel kısmı şunları içerir:


  • Kullanım senaryoları : Kullanım senaryoları, verileri kaydetme, güncelleme ve getirme gibi çeşitli işlemlere yönelik iş kurallarını tanımlar. Örneğin, bir kullanım durumu, Notion'dan bir kelime listesi almayı veya kullanıcının öğrenilen kelimeler için günlük serisini artırmayı içerebilir.


    Temel olarak kullanım senaryoları, uygulamanın görev ve süreçlerini iş perspektifinden ele alarak sistemin istenen hedeflere uygun çalışmasını sağlar.


  • Modeller : Modeller, uygulama içindeki ticari varlıkları temsil eder. Bunlar TypeScript arayüzleri kullanılarak tanımlanabilir ve ihtiyaçlara ve iş gerekliliklerine uygun olmaları sağlanır.


    Örneğin, bir kullanım senaryosu Notion'dan bir kelime listesi almayı içeriyorsa, uygun iş kurallarına ve kısıtlamalara bağlı kalarak bu listenin veri yapısını doğru bir şekilde tanımlayacak bir modele ihtiyacınız olacaktır.


  • Operasyonlar : Bazen belirli görevleri kullanım senaryoları olarak tanımlamak mümkün olmayabilir veya alanınızın birden fazla bölümünde kullanılabilecek yeniden kullanılabilir işlevler oluşturmak isteyebilirsiniz. Örneğin, bir Notion sözcüğünü ada göre aramak için bir işlev yazmanız gerekiyorsa, bu tür işlemlerin bulunması gereken yer burasıdır.


    İşlemler, uygulama içindeki çeşitli bağlamlarda paylaşılabilen ve kullanılabilen, alana özgü mantığın kapsüllenmesi için kullanışlıdır.


  • Depo arayüzleri : Kullanım senaryoları verilere erişim için bir araç gerektirir. Bağımlılığı Tersine Çevirme Prensibi uyarınca, etki alanı katmanının başka bir katmana bağımlı olmaması gerekir (diğer katmanlar ona bağlıyken); bu nedenle bu katman depolar için arayüzleri tanımlar.


    Uygulama ayrıntılarını değil, arayüzleri belirttiğine dikkat etmek önemlidir. Depoların kendisi, gerçek veri kaynağından bağımsız olan ve bu kaynaklardan veri alma veya bu kaynaklardan veri gönderme mantığını vurgulayan Depo Desenini kullanır.


    Tek bir havuzun birden fazla API uygulayabileceğini ve tek bir Kullanım Senaryosunun birden fazla havuzu kullanabileceğini belirtmek çok önemlidir.

API'si

Bu katman veri erişiminden sorumludur ve gerektiğinde çeşitli kaynaklarla iletişim kurabilir. Bir frontend uygulaması geliştirdiğimizi düşünürsek bu katman öncelikle tarayıcı API’leri için sarmalayıcı görevi görecek.


Buna REST, yerel depolama, IndexedDB, konuşma sentezi ve daha fazlası için API'ler dahildir.


OpenAPI türleri ve HTTP istemcileri oluşturmak istiyorsanız API katmanının bunları yerleştirmek için ideal yer olduğunu unutmamak önemlidir. Bu katmanın içinde şunlara sahibiz:


  • API bağdaştırıcısı : API Bağdaştırıcısı, uygulamamızda kullanılan tarayıcı API'leri için özel bir bağdaştırıcıdır. Bu bileşen, REST çağrılarını ve uygulamanın belleğiyle veya kullanmak istediğiniz diğer herhangi bir veri kaynağıyla iletişimi yönetir.


    İsterseniz kendi nesne depolama sisteminizi bile oluşturabilir ve uygulayabilirsiniz. Özel bir API Bağdaştırıcısına sahip olarak, çeşitli veri kaynaklarıyla etkileşimde bulunmak için tutarlı bir arayüz sağlayabilir ve gerektiğinde bunları güncellemeyi veya değiştirmeyi kolaylaştırabilirsiniz.


  • Türler : Burası API'nizle ilgili tüm türlerin bulunduğu yerdir. Bu türler doğrudan alanla ilgili değildir ancak API'den alınan ham yanıtların açıklamaları olarak hizmet eder. Bir sonraki katmanda bu türler, uygun haritalama ve işleme için gerekli olacaktır.

Depo

Depo katmanı, birden fazla API'nin entegrasyonunu yöneterek, API'ye özgü türleri etki alanı türleriyle eşleyerek ve verileri dönüştürmeye yönelik işlemleri birleştirerek uygulamanın mimarisinde önemli bir rol oynar.


Örneğin, konuşma sentezi API'sini yerel depolamayla birleştirmek istiyorsanız burası bunu yapmak için mükemmel bir yerdir. Bu katman şunları içerir:


  • Depo uygulaması : Bunlar etki alanı katmanında bildirilen arayüzlerin somut uygulamalarıdır. Uygulama içinde esneklik ve uyarlanabilirlik sağlayarak birden fazla veri kaynağıyla çalışma yeteneğine sahiptirler.


  • Operasyonlar : Bunlara haritalayıcılar, transformatörler veya yardımcılar denilebilir. Bu bağlamda operasyonlar uygun bir terimdir. Bu dizin, ham API yanıtlarını karşılık gelen etki alanı türleriyle eşlemekten sorumlu tüm işlevleri içerir ve verilerin uygulama içinde kullanım için uygun şekilde yapılandırılmasını sağlar.

Adaptör


Bağdaştırıcı katman, bu katmanlar arasındaki etkileşimleri düzenlemekten ve bunları birbirine bağlamaktan sorumludur. Bu katman yalnızca aşağıdakilerden sorumlu modülleri içerir:


  • Bağımlılık Enjeksiyonu : Bağdaştırıcı katmanı, API, Depo ve Etki Alanı katmanları arasındaki bağımlılıkları yönetir. Bağımlılık ekleme işlemini gerçekleştiren Bağdaştırıcı katmanı, endişelerin temiz bir şekilde ayrılmasını sağlar ve kodun verimli bir şekilde yeniden kullanılabilirliğini destekler.


  • Modül Organizasyonu : Bağdaştırıcı katmanı, uygulamayı işlevlerine (örneğin, yerel depolama, REST, konuşma sentezi, Supabase) göre modüller halinde düzenler. Her modül belirli bir işlevselliği kapsayarak uygulama için temiz ve modüler bir yapı sağlar.


  • Eylemler Oluşturma : Bağdaştırıcı katmanı, Etki Alanı katmanındaki kullanım örneklerini uygun depolarla birleştirerek eylemler oluşturur. Bu eylemler, uygulamanın alttaki katmanlarla etkileşime girmesi için giriş noktaları görevi görür.

Sunum

Sunum katmanı, kullanıcı arayüzünü (UI) oluşturmaktan ve uygulamayla kullanıcı etkileşimlerini yönetmekten sorumludur. İşlevsel ve etkileşimli bir kullanıcı arayüzü oluşturmak için bağdaştırıcıdan, etki alanından ve paylaşılan katmanlardan yararlanır.


Sunum katmanı, bileşenlerini düzenlemek için Atomik Tasarım metodolojisini kullanır ve sonuçta ölçeklenebilir ve bakımı yapılabilir bir uygulama ortaya çıkar. Ancak bu katman, Temiz Mimari uygulaması açısından ana konu olmadığından bu makalenin ana odağı olmayacaktır.

Paylaşıldı

Merkezi yardımcı programlar, konfigürasyonlar ve paylaşılan mantık gibi tüm ortak öğeler için belirlenmiş bir yer gereklidir. Ancak bu yazıda bu katmana çok fazla değinmeyeceğiz.


Uygulama genelinde ortak bileşenlerin nasıl yönetildiğini ve paylaşıldığını anlamak için bahsetmeye değer.

Her Katman İçin Test Stratejileri

Şimdi, kodlamaya dalmadan önce testi tartışmak önemlidir. Uygulamanızın güvenilirliğini ve doğruluğunu sağlamak hayati öneme sahiptir ve mimarinin her katmanı için sağlam bir test stratejisi uygulamak çok önemlidir.


  • Etki Alanı Katmanı : Birim testi, etki alanı katmanını test etmenin birincil yöntemidir. Etki alanı modellerini, doğrulama kurallarını ve iş mantığını test etmeye odaklanın ve bunların çeşitli koşullar altında doğru şekilde çalışmasını sağlayın. Etki alanı modellerinizin tasarımını desteklemek ve iş mantığınızın sağlam olduğunu doğrulamak için test odaklı geliştirmeyi (TDD) benimseyin.


  • API Katmanı : Entegrasyon testlerini kullanarak API katmanını test edin. Bu testler, API'nin harici hizmetlerle doğru şekilde etkileşimde bulunmasını ve yanıtların doğru şekilde biçimlendirilmesini sağlamaya odaklanmalıdır. API çağrılarını simüle etmek ve yanıtları doğrulamak için Jest gibi otomatik test çerçeveleri gibi araçları kullanın.


  • Depo Katmanı : Depo katmanı için birim ve entegrasyon testlerinin bir kombinasyonunu kullanabilirsiniz. Birim testleri, bireysel depo yöntemlerini test etmek için kullanılabilir; entegrasyon testleri ise depoların API'leriyle doğru şekilde etkileşimde bulunduğunu doğrulamaya odaklanmalıdır.


  • Bağdaştırıcı Katmanı : Birim testleri bağdaştırıcı katmanını test etmek için uygundur. Bu testler, bağdaştırıcıların bağımlılıkları doğru şekilde enjekte etmesini ve katmanlar arasındaki veri dönüşümlerini yönetmesini sağlamalıdır. API veya depo katmanları gibi bağımlılıklarla alay etmek, test sırasında bağdaştırıcı katmanının yalıtılmasına yardımcı olabilir.


Mimarinin her katmanı için kapsamlı bir test stratejisi uygulayarak uygulamanızın güvenilirliğini, doğruluğunu ve sürdürülebilirliğini sağlarken geliştirme sırasında hata oluşma olasılığını da azaltabilirsiniz.


Ancak küçük bir uygulama geliştiriyorsanız adaptör katmanındaki entegrasyon testleri yeterli olacaktır.

Hadi bir şeyleri kodlayalım

Tamam, artık Temiz Mimari konusunda sağlam bir anlayışa sahip olduğunuza ve hatta bu konuda kendi fikrinizi oluşturduğunuza göre, biraz daha derine inelim ve bazı gerçek kodları inceleyelim.


Burada sadece basit bir örnek sunacağımı unutmayın; ancak daha ayrıntılı örneklerle ilgileniyorsanız, bu makalenin başında bahsedilen GitHub depomu incelemekten çekinmeyin.


"Gerçek hayatta" Temiz Mimari, büyük, kurumsal düzeydeki uygulamalarda gerçekten parlıyor, oysa daha küçük projeler için aşırıya kaçabilir. Bunu söyledikten sonra asıl meseleye geçelim.


Örnek olarak uygulamamı kullanarak, belirli bir kelime için sözlük önerilerini getirmek üzere bir API çağrısının nasıl gerçekleştirileceğini göstereceğim. Bu özel API uç noktası, iki web sitesini web kazıyarak anlamların ve örneklerin bir listesini alır.


İş açısından bakıldığında bu uç nokta, kullanıcıların belirli bir kelimeyi aramasına olanak tanıyan "Kelime Bul" görünümü için çok önemlidir. Kullanıcı sözcüğü bulup oturum açtığında, web'den alınan bilgileri Notion Veritabanına ekleyebilir.

Klasör Yapısı

Başlamak için daha önce tartıştığımız katmanları doğru şekilde yansıtan bir klasör yapısı oluşturmalıyız. Yapı aşağıdakine benzemelidir:


 client ├── adapter ├── api ├── domain ├── presentation ├── repository └── shared


İstemci dizini birçok projedeki "src" klasörüne benzer bir amaca hizmet eder. Bu özel Next.js projesinde, ön uç klasörünü "istemci" ve arka uç klasörünü "sunucu" olarak adlandırma kuralını benimsedim.


Bu yaklaşım, uygulamanın iki ana bileşeni arasında net bir ayrım yapılmasını sağlar.

Alt dizinler

Projeniz için doğru klasör yapısını seçmek gerçekten de geliştirme sürecinin başlarında verilmesi gereken çok önemli bir karardır. Kaynakların düzenlenmesi söz konusu olduğunda farklı geliştiricilerin kendi tercihleri ve yaklaşımları vardır.


Bazıları kaynakları sayfa adlarına göre gruplandırabilir, diğerleri OpenAPI tarafından oluşturulan alt dizin adlandırma kurallarını takip edebilir ve yine de diğerleri uygulamalarının bu çözümlerden herhangi birini garanti edemeyecek kadar küçük olduğuna inanabilir.


Önemli olan, kaynakların açık ve sürdürülebilir bir organizasyonunu korurken, projenizin özel ihtiyaçlarına ve ölçeğine en iyi uyan yapıyı seçmektir.


Ben üçüncü gruptayım, dolayısıyla yapım şöyle görünüyor:


 client ├── adapter │ ├── local-storage │ ├── rest │ ├── speech-synthesis │ └── supabase ├── api │ ├── local-storage │ ├── rest │ ├── speech-synthesis │ └── supabase ├── domain │ ├── local-storage │ ├── rest │ ├── speech-synthesis │ ├── supabase └── repository ├── local-storage ├── rest ├── speech-synthesis └── supabase


Daha derine inmek isteyenlerin daha fazla bilgi için arşivime başvurabileceğine inandığım için bu makaledeki paylaşım ve sunum katmanlarını atlamaya karar verdim. Şimdi Temiz Mimarinin bir ön uç uygulamada nasıl uygulanabileceğini göstermek için bazı kod örnekleriyle ilerleyelim.

Etki Alanı Tanımı

İhtiyaçlarımızı göz önünde bulunduralım. Bir kullanıcı olarak anlamları ve örnekleriyle birlikte bir öneri listesi almak istiyorum. Bu nedenle tek bir sözlük önerisi şu şekilde modellenebilir:


 interface DictionarySuggestion { example: string; meaning: string; }


Artık tek bir sözlük önerisini tanımladığımıza göre, bazen web kazıma yoluyla elde edilen kelimenin kullanıcının yazdığı kelimeyle karşılaştırıldığında farklı olduğunu veya düzeltildiğini belirtmek önemlidir. Buna uyum sağlamak için düzeltilmiş sürümü daha sonra uygulamamızda kullanacağız.


Sonuç olarak sözlük önerileri ve kelime düzeltmelerinin listesini içeren bir arayüz tanımlamamız gerekiyor. Son arayüz şuna benzer:


 export interface DictionarySuggestions { suggestions: DictionarySuggestion[]; word: string; }


Bu arayüzü dışa aktarıyoruz, bu nedenle export anahtar sözcüğü eklenmiştir.

Depo Arayüzü

Modelimiz elimizde, şimdi onu kullanma zamanı.


 import { DictionarySuggestions } from './rest.models'; export interface RestRepository { getDictionarySuggestions: (word: string) => Promise<DictionarySuggestions | null>; }


Bu noktada her şeyin açık olması gerekiyor. Burada API'yi hiç tartışmadığımızı unutmamak önemlidir! Deponun yapısı oldukça basittir: her yöntemin belirli bir türdeki verileri eşzamansız olarak döndürdüğü bazı yöntemleri içeren bir nesne.


Lütfen havuzun verileri her zaman etki alanı modeli biçiminde döndürdüğünü unutmayın.

Kullanım Örneği

Şimdi iş kuralımızı bir kullanım durumu olarak tanımlayalım. Kod şuna benzer:


 export type GetDictionarySuggestionsUseCaseUseCase = UseCaseWithSingleParamAndPromiseResult< string, DictionarySuggestions | null >; export const getDictionarySuggestionsUseCase = ( restRepository: RestRepository, ): GetDictionarySuggestionsUseCaseUseCase => ({ execute: (word) => restRepository.getDictionarySuggestions(word), });


Dikkat edilmesi gereken ilk şey, kullanım durumlarını tanımlamak için kullanılan yaygın türlerin listesidir. Bunu başarmak için etki alanı dizininde bir use-cases.types.ts dosyası oluşturdum:


 domain ├── local-storage ├── rest ├── speech-synthesis ├── supabase └── use-cases.types.ts


Bu, alt dizinlerim arasında kullanım senaryolarına yönelik türleri kolayca paylaşmamı sağlıyor. UseCaseWithSingleParamAndPromiseResult tanımı şuna benzer:


 export interface UseCaseWithSingleParamAndPromiseResult<TParam, TResult> { execute: (param: TParam) => Promise<TResult>; }


Bu yaklaşım, etki alanı katmanı genelinde kullanım senaryosu türlerinin tutarlılığının ve yeniden kullanılabilirliğinin korunmasına yardımcı olur.


execute işlevine neden ihtiyacımız olduğunu merak ediyor olabilirsiniz. Burada gerçek kullanım durumunu döndüren bir fabrikamız var.


Bu tasarım seçimi, kullanım senaryosu kodunda depo uygulamasına doğrudan referans vermek istemememizden veya deponun bir içe aktarma tarafından kullanılmasını istemememizden kaynaklanmaktadır. Bu yaklaşım daha sonra bağımlılık enjeksiyonunu kolayca uygulamamıza olanak tanır.


Fabrika modelini ve execute işlevini kullanarak, havuzun uygulama ayrıntılarını kullanım senaryosu kodundan ayrı tutabiliriz, bu da uygulamanın modülerliğini ve sürdürülebilirliğini artırır.


Bu yaklaşım, etki alanı katmanının başka herhangi bir katmana bağlı olmadığı Bağımlılığı Tersine Çevirme İlkesini takip eder ve farklı depo uygulamalarının değiştirilmesi veya uygulamanın mimarisinin değiştirilmesi söz konusu olduğunda daha fazla esneklik sağlar.

API Tanımı

Öncelikle arayüzümüzü tanımlayalım:


 export interface RestApi { getDictionarySuggestions: (word: string) => Promise<AxiosResponse<DictionarySuggestions>>; }


Gördüğünüz gibi, bu fonksiyonun arayüzdeki tanımı depodakine çok benzer. Etki alanı türü yanıtı zaten tanımladığından aynı türü yeniden oluşturmaya gerek yoktur.


API'mızın ham verileri döndürdüğünü unutmamak önemlidir; bu nedenle AxiosResponse<DictionarySuggestions> öğesinin tamamını döndürüyoruz. Bunu yaparak, API ve etki alanı katmanları arasında net bir ayrım sağlayarak veri işleme ve dönüştürmede daha fazla esneklik sağlıyoruz.


Bu API'nin uygulanması şuna benzer:


 export const getRestApi = (axiosInstance: AxiosInstance): RestApi => ({ getDictionarySuggestions: async (word: string) => { const encodedCurrentDate = encodeURIComponent(word); const response = await axiosInstance.get( `${RestEndpoints.GET_DICTIONARY_SUGGESTIONS}?word=${encodedCurrentDate}`, ); return response; } });


Bu noktada işler daha da ilginçleşiyor. Tartışılacak ilk önemli husus, axiosInstance enjeksiyonudur. Bu, kodumuzu çok esnek hale getirir ve kolayca sağlam testler oluşturmamızı sağlar. Burası aynı zamanda sorgu parametrelerinin kodlanmasını veya ayrıştırılmasını da gerçekleştirdiğimiz yerdir.


Ancak burada giriş dizesini kırpmak gibi başka eylemleri de gerçekleştirebilirsiniz. axiosInstance enjekte ederek endişelerin net bir şekilde ayrılmasını sağlıyoruz ve API uygulamasının farklı senaryolara veya harici hizmetlerdeki değişikliklere uyarlanabilir olmasını sağlıyoruz.

Depo Uygulaması

Arayüzümüz zaten alan adı tarafından tanımlandığından tek yapmamız gereken depomuzu uygulamaktır. Yani son uygulama şöyle görünür:

 export const getRestRepository = (restApi: RestApi): RestRepository => ({ getDictionarySuggestions: async (word) => { const { data } = await restApi.getDictionarySuggestions(word); if (!data?.suggestions?.length) { return null; } return formatDictionarySuggestions(data); } });


Bahsedilmesi gereken önemli bir husus API'lerle ilgilidir. getRestRepository önceden tanımlanmış bir restApi aktarmamıza olanak tanır. Bu avantajlıdır çünkü daha önce de belirtildiği gibi testlerin daha kolay yapılmasını sağlar. formatDictionarySuggestions kısaca inceleyebiliriz:


 export const formatDictionarySuggestions = ({ suggestions, word, }: DictionarySuggestions): DictionarySuggestions => { const cleanedWord = cleanUpString(word); const cleanedSuggestions = suggestions.map((_suggestion) => { const cleanedMeaning = cleanUpString(_suggestion.meaning); const cleanedExample = cleanUpString(_suggestion.example); return { meaning: cleanedMeaning, example: cleanedExample, }; }); return { word: cleanedWord, suggestions: cleanedSuggestions, }; };


Bu işlem, etki alanı DictionarySuggestions modelimizi bağımsız değişken olarak alır ve bir dize temizliği gerçekleştirir; bu, gereksiz boşlukların, satır sonlarının, sekmelerin ve büyük harflerin kaldırılması anlamına gelir. Hiçbir gizli karmaşıklık olmadan oldukça basittir.


Unutulmaması gereken önemli bir nokta, bu noktada API uygulamanız konusunda endişelenmenize gerek olmamasıdır. Bir hatırlatma olarak, depo her zaman etki alanı modelindeki verileri döndürür! Aksi olamaz çünkü bunu yapmak bağımlılığın tersine çevrilmesi ilkesini ihlal eder.


Ve şimdilik etki alanı katmanımız onun dışında tanımlanan hiçbir şeye bağlı değil.

Adaptör - Hepsini Bir Araya Getirelim

Bu noktada her şeyin uygulanması ve bağımlılık enjeksiyonuna hazır olması gerekir. İşte dinlenme modülünün son uygulaması:


 import { getRestRepository } from '@repository/rest/rest.repository'; import { getRestApi } from '@api/rest/rest.api'; import { getDictionarySuggestionsUseCase } from '@domain/rest/rest.use-cases'; import { axiosInstance } from '@shared/axios.instance'; const restApi = getRestApi(axiosInstance); const restRepository = getRestRepository(restApi); export const restModule = { getDictionarySuggestions: getDictionarySuggestionsUseCase(restRepository).execute, };


Bu doğru! Temiz Mimari ilkelerini belirli bir çerçeveye bağlı kalmadan hayata geçirme sürecini yaşadık. Bu yaklaşım, kodumuzun uyarlanabilir olmasını sağlar ve gerektiğinde çerçeveler veya kitaplıklar arasında geçiş yapmayı kolaylaştırır.


Test söz konusu olduğunda, depoyu kontrol etmek, testlerin bu mimaride nasıl uygulandığını ve organize edildiğini anlamanın harika bir yoludur.


Temiz Mimari'de sağlam bir temele sahip olduğunuzda, çeşitli senaryoları kapsayan kapsamlı testler yazabilir, uygulamanızı daha sağlam ve güvenilir hale getirebilirsiniz.


Gösterildiği gibi, Temiz Mimari ilkelerini takip etmek ve endişeleri ayırmak, sürdürülebilir, ölçeklenebilir ve test edilebilir bir uygulama yapısına yol açar.


Bu yaklaşım sonuçta yeni özellikler eklemeyi, kodu yeniden düzenlemeyi ve bir proje üzerinde bir ekiple çalışmayı kolaylaştırarak uygulamanızın uzun vadeli başarısını garanti eder.

Sunum

Örnek uygulamada sunum katmanı için React kullanılmıştır. Bağdaştırıcı dizininde dinlenme modülüyle etkileşimi yöneten hooks.ts adında ek bir dosya vardır. Bu dosyanın içeriği aşağıdaki gibidir:


 import { restModule } from '@adapter/rest/rest.module'; import { useAxios } from '@shared/hooks'; export const useDictionarySuggestions = () => { const { data, error, isLoading, mutate } = useAxios(restModule.getDictionarySuggestions); return { dictionarySuggestions: data, getDictionarySuggestions: mutate, dictionarySuggestionsError: error, isDictionarySuggestionsLoading: isLoading, }; };


Bu uygulama sunum katmanıyla çalışmayı inanılmaz derecede kolaylaştırır. useDictionarySuggestions kancasını kullanarak sunum katmanının, veri eşlemelerini veya birincil işleviyle ilgisi olmayan diğer sorumlulukları yönetme konusunda endişelenmesine gerek kalmaz.


Bu endişelerin ayrılması, Temiz Mimari ilkelerinin korunmasına yardımcı olarak daha yönetilebilir ve bakımı kolay kodlara yol açar.

Sıradaki ne?

Öncelikle ve en önemlisi, sağlanan GitHub deposundaki koda dalmanızı ve yapısını keşfetmenizi öneririm.


Başka ne yapabilirim? Sınır gökyüzü! Her şey özel tasarım ihtiyaçlarınıza bağlıdır. Örneğin, bir veri deposu (Redux, MobX veya hatta özel bir şey - fark etmez) ekleyerek veri katmanını uygulamayı düşünebilirsiniz.


Alternatif olarak, yoklama, anlık bildirimler veya soketleri (esasen herhangi bir veri kaynağı için hazırlanmayı) içerebilen arka uçla eşzamansız iletişimi yönetmek için RxJS kullanmak gibi katmanlar arasında farklı iletişim yöntemlerini deneyebilirsiniz.


Temel olarak, katmanlı mimariyi koruduğunuz ve ters bağımlılık ilkesine bağlı kaldığınız sürece dilediğiniz gibi keşfetmekten ve denemekten çekinmeyin. Her zaman alan adının tasarımınızın merkezinde olduğundan emin olun.


Bunu yaparak, çeşitli senaryolara ve gereksinimlere uyum sağlayabilecek esnek ve sürdürülebilir bir uygulama yapısı oluşturacaksınız.

Özet

Bu makalede, React kullanılarak oluşturulan bir dil öğrenme uygulaması bağlamında Temiz Mimari kavramını derinlemesine inceledik.


Katmanlı bir mimariyi korumanın ve ters bağımlılık ilkesine bağlı kalmanın öneminin yanı sıra endişeleri ayırmanın faydalarını vurguladık.


Temiz Mimarinin önemli bir avantajı, belirli bir çerçeveye bağlı kalmadan uygulamanızın mühendislik yönüne odaklanmanıza izin vermesidir. Bu esneklik, uygulamanızı çeşitli senaryolara ve gereksinimlere uyarlamanıza olanak tanır.


Ancak bu yaklaşımın bazı dezavantajları vardır. Bazı durumlarda katı bir mimari modeli takip etmek, standart kodun artmasına veya proje yapısında ilave karmaşıklığa yol açabilir.


Ek olarak, belgelere daha az güvenmek hem olumlu hem de olumsuz olabilir; daha fazla özgürlük ve yaratıcılığa izin verirken aynı zamanda ekip üyeleri arasında kafa karışıklığına veya yanlış iletişimle de sonuçlanabilir.


Bu potansiyel zorluklara rağmen Temiz Mimarinin uygulanması, özellikle evrensel olarak kabul edilmiş bir mimari modelin bulunmadığı React bağlamında son derece faydalı olabilir.


Mimarinizi, yıllarca uğraştıktan sonra ele almak yerine, bir projenin başlangıcında düşünmek önemlidir.


Temiz Mimarinin gerçek hayattaki bir örneğini uygulamalı olarak keşfetmek için şu adresteki depoma göz atmaktan çekinmeyin: https://github.com/Levofron/NotionLingo . Profilimde yer alan linklerden sosyal medya üzerinden de benimle iletişime geçebilirsiniz.


Vay, bu muhtemelen şimdiye kadar yazdığım en uzun makale. İnanılmaz hissettiriyor!