N-tier architecture'da servis ve repository farki nedir?

Bu durumda business logic data accessteki fonksiyonlar kadar basit olduğu için kafanızın karışması normal. Yani servise yeni user ekle diyorsunuz, dbde yeni user yaratıyor ya da login ol diyorsunuz, dbden userı bulup getiriyor.

User ile ilgili crud işlemleri data access layerda kalacak gibi düşünün. Entity, dao, dto, query, config vs. ne varsa data access yapmakla sorumlu olan katmanda.

UserService'in mesela db hakkında bilgisi olmasına gerek var mı? Yok. Mysql mi, mssql mi, mongo mu vs. hangi dbye bağlandığını bilmesine gerek yok, o dbden nasıl data çekileceğini, querynin nasıl yazıldığını bilmesine gerek yok, serialization-deserialization bilmesine gerek yok vs. vs. User servisin bu data hakkında tek bildiği şey User nesnesi (dto).

Business logicdeki LoginUser mantığı daha karmaşık oldu diyelim, artık sadece user bulup dönmeyeceğiz

LoginUser
-User (userid, name vb. bilgileri içeren standart user nesnesi, uygulamaya girdiğimizde "Hoşgeldin Count" yazması için vs. lazım)
-kullanıcının avatarı (bunun büyüğü, thumbnail hali vs. bu dataya eklenmiş, arayüzde avatarımızı görebileceğiz)
-token/refresh token vs. (her seferinde user/pass göndermektense auth için token kullanacağız)
-kullanıcının uygulamadaki rolleri (mesela standart user ise standart arayüz gelecek, moderatör ise moderatör paneli arayüzde gösterilecek vs.)
...


Yani business logic artık şunları yapmalı
username/password ile User nesnesini ara
mevcut ise bir token generate et
UserImages arasında bu user'a ait avatarları bul
UserRoles'a git, bu userın rollerini bul

Şimdi bir de bu uygulamanın bir MessageService'e sahip olduğunu düşünelim, kullanıcıya atılan mesajları görebileceği, başka kullanıcıya mesaj atabileceği ayrı bir servis. Kullanıcı mesajlarıma tıkladığına ona gönderilmiş olan mesajları listeleyebilen bir method ekleyelim;

GetMessages
token kontrolü
gönderilen tokendan userId'nin alınması
bu userId ile User nesnesini ara (verification)
authorization işlemleri (mesela user banlanmış ise mesaj falan gösterilmeyecek)
Messages arasında bu userId'ye gönderilmiş mesajları bul, hatta bu işlemden önce BlockedUsers tablosuna git, bizim userId'nin blokladığı kullanıcıların userIdlerini bul, o userIdlerden gelen mesajlar listelenmesin
Mesajları gönderenlerin userIdlerini bul, Users arasında bu userIdler ile eşleşenlerin User bilgilerini getir (getUser değil getUsers isteği atılacak, elli kere her bir userId için tek tek data çekmemek için), her bir mesaj içine gönderenin user bilgisini ekle ki mesajı gönderen olarak userId değil userName görsün
aynı şekilde bu userIdler ile UserImages arasında mesaj gönderenlerin resimlerini bul, bunları da mesajlara iliştir

en sonunda da şuna benzer bir response dön.

[

{
messageId:...
sendDate:...
from: {userId:..., userName:..., image:...}
isRead:...
}
,
...
]


Bu senaryolardan bahsetmemin sebebi, data access ile business logic'i ayırdığımızda ne olduğunu biraz irdelemek. Yani User nesnesi ile sadece sizin createUser, getUser vb. yapan login servisiniz ilgilenmiyor, farklı servisler de User gibi datalara erişme ihtiyacı duyabiliyor. Bu durumda her serviste tek tek db'ye nasıl bağlanırım, User tablosundan nasıl data çekerim gibi şeyleri implemente etmek gerekirdi layerlara ayırmasa idik, yani örneğin UserService içinde bir db.connect kodu olurdu, methodların başında bu şekilde dbye bağlanırdık, her method içinde de select * from User... şeklinde query/ler olurdu, yani user çekmek için pek çok serviste, pek çok method içinde sorgular yazmamız gerekirdi

serviceA
db.connect (dburlsi, username, password)
-method1: select * from a where blabla, select * from b where qweqwe
-method2: select * from a where blabla
-method3: select * from a where blabla, select * from b where qweqwe, select * from c where zxczxc

serviceB
db.connect (dburlsi, username, password)
-method1: select * from a where blabla, select * from b where qweqwe
-method2: select * from b where zxczxc
-method3: select * from a where blabla, select * from c where qweqwe

bunlara update, insert, delete, join vs. işlemleri de dahil olacaktır, yani sorgu sayısı, sorguların complexityleri çok daha fazla olacaktır.

Şimdi diyelim ki dbye bağlanma mekanizmamız değişti, db.connect yapan her serviste bu mantığı teker teker değiştirmemiz gerekecek.
Diyelim ki a tablosunun adını değiştirdik ya da a tablosunda bir kolonun ismini değiştirdik, a tablosuna istek atan her servisteki her sorguyu tek tek kontrol etmemiz/güncellememiz gerekecek.

olması gereken ise kabaca şöyle bir şey

dbClass-db connect sorumluluğunun sahibi bir db sınıfı

daoA
-inject db sınıfı
getById
insert
update
getByBlaBla


daoB
-inject db sınıfı
getById
insert
update
getByHede
...

bu durumda serviceA içi şöyle olur

serviceA
-inject daoA, daoB, daoC
-method1: daoA.get, daoB.get
-method2: daoA.get
-method3: daoA.get, daoB.get, daoC.get

DB connectionı ile alakalı bir şeyler(server url mesela) değişti -> bir tek db sınıfında güncelleme yapılması yeterli (bunun da zaten burada tutulmaması, environmenttan okunması gerekir), daha üst seviyedeki yapılar bununla ilgilenmez, bu bilgi ile alakaları yoktur
diyelim ki standart db yerine localdeki test dbsine bağlanılarak debug yapılacak -> bir tek db sınıfındaki connection değiştirilir
A tablosunun adı AA oldu -> her servisteki A tablosu querylerine güncelleme yapılması gerekmez, daoA'daki queryler güncellenir
B tablosuna isActive diye bir kolon eklememiz gerekti diyelim, querylerin değişmesi gerekti, örneğin "...where isActive = true" eklenmesi gerekti -> yine sadece o daoB deki querylerin güncellenmesi yeterli, bu methodu çağıran her servisteki code artık sadece isActive olanları alacak şekilde güncellenmiş oluyor
yeni bir servis ekledim, A datasına ihtiyacı var -> queryleri copy paste etmek yerine daoA.get... methodlarını eklemem yeterli.

Dao ile Service arasına ekstra bir katman da koyuluyor hatta, şunun gibi
itemDao, priceDao, stockDao gibi tabloların daoları olsun, bunlar sadece kendi tablolarından data çekiyor
bir query service şunu yapsın, bu tabloları joinlesin,
[{kalem,20,100},{silgi, 10, 50}...] şeklinde datayı harmanlama görevi onun olsun yani birden fazla daoya bağlanıp yeni bir dto modelinde response dönsün
business logic katmanı ise bu datayı okusun, daha sonra daha karmaşık işlemleri yapmak onun görevi, örneğin stok 100den aşağı olan ürünlere "warning:"tükenmek üzere" şeklinde bir bilgi ekleyebilir, kullanıcının daha önceki alışverişlerini de tarayıp daha evvel aldığı ürünlere ekstra indirim tanımlayabilir, sistemde en çok satan ürünleri bulup onlara 4 al 3 öde gibi kampanyalar ekleyebilir, ör: tarih > 20 aralık ise yılbaşı indirimi yapabilir, kullanıcının daha önceki alışverişlerini tarayıp onun tercih edebileceği ürünleri hesaplayıp listede onları en başa alabilir, güncel dolar/euro kurunu bir yerden alıp x dolarlık bir ürünün fiyatını güncel olarak verebilir vs. vs.

Forumda proje yaparak öğrenme tavsiye ediliyor sürekli, onun öncesinde yazılım prensiplerini tekrar gözden geçirmenizi tavsiye ederim, teorik bilginizi tazeledikten sonra bunların artısını eksisini tartar, neden böyle yapılması gerektiğini daha kolay anlarsınız. Separation of concerns, single responsibility principle, code reusability vb.
Tesekkur ettim kafamda biraz daha oturdu.
 

Technopat Haberler

Yeni konular

Geri
Yukarı