Kitaplar / Yöntem Birimler ve Araçlar Kılavuzu / 9. Üç katmanlı bir uygulama
9. Üç katmanlı bir uygulama
Sunucu ve Veritabanı ile klasik üç katmanlı yapı Yöntem’in birimleriyle doğrudan kurulur: her katman ayrı bir dosyadır ve yalnızca bir altındakini kullan ile görür.
| Katman | Dosya | Bildiği |
|---|---|---|
| Sunum | 10_uc_katman.y (program) |
HTTP: yollar, JSON, durum kodları |
| İş | UrunIsleri.y (birim) |
Kurallar: ad, fiyat ve stok denetimi |
| Depolama | UrunDeposu.y (birim) |
SQL: tablo ve sorgular |
Depolama katmanı yalnızca SQL içerir. arayüz bölümü üst katmana ne sunduğunu söyler; vt değişkeni uygulama bölümünde olduğu için dışarıdan görünmez:
birim ÜrünDeposu
{ Depolama katmanı: yalnızca SQL bilir. Kural da ekran da içermez.
Başka bir veritabanına geçmek için Aç'a verilen adresi değiştirmek yeter. }
kullan Veritabanı
arayüz
prosedür Aç(adres: metin)
fonksiyon Hepsi: liste
fonksiyon Bul(no: tamsayı): herhangi
fonksiyon AdVarMı(ad: metin): mantıksal
fonksiyon Ekle(ad: metin; fiyat: ondalık; adet: tamsayı): tamsayı
prosedür AdetDeğiştir(no, fark: tamsayı)
uygulama
değişken
vt: herhangi
prosedür Aç(adres: metin)
başla
vt := Bağlan(adres)
vt.Çalıştır('CREATE TABLE IF NOT EXISTS urun (no INTEGER PRIMARY KEY, ad TEXT NOT NULL UNIQUE, fiyat REAL NOT NULL, adet INTEGER NOT NULL)')
bitir
fonksiyon Hepsi: liste
başla
dön vt.Sorgula('SELECT no, ad, fiyat, adet FROM urun ORDER BY no')
bitir
fonksiyon Bul(no: tamsayı): herhangi
başla
dön vt.TekSatır('SELECT no, ad, fiyat, adet FROM urun WHERE no = ?', [no])
bitir
fonksiyon AdVarMı(ad: metin): mantıksal
başla
dön vt.TekDeğer('SELECT COUNT(*) FROM urun WHERE ad = ?', [ad]) > 0
bitir
fonksiyon Ekle(ad: metin; fiyat: ondalık; adet: tamsayı): tamsayı
başla
vt.Çalıştır('INSERT INTO urun (ad, fiyat, adet) VALUES (?, ?, ?)', [ad, fiyat, adet])
dön vt.SonKimlik
bitir
prosedür AdetDeğiştir(no, fark: tamsayı)
başla
vt.Çalıştır('UPDATE urun SET adet = adet + ? WHERE no = ?', [fark, no])
bitir
bitir.İş katmanı kuralları uygular. HTTP durum kodu da SQL de bilmez; kural çiğnenince türü belli bir hata fırlatır:
birim Ürünİşleri
{ İş katmanı: kurallar burada. HTTP'yi de SQL'i de bilmez; depolama katmanını çağırır,
kural çiğnenirse açıklayıcı bir hata fırlatır. }
kullan ÜrünDeposu
arayüz
fonksiyon ÜrünListesi: liste
fonksiyon ÜrünEkle(ad: metin; fiyat: ondalık; adet: tamsayı): sözlük
fonksiyon Sat(no, adet: tamsayı): sözlük
fonksiyon StokDeğeri: ondalık
uygulama
fonksiyon ÜrünListesi: liste
başla
dön Hepsi
bitir
fonksiyon ÜrünEkle(ad: metin; fiyat: ondalık; adet: tamsayı): sözlük
değişken temiz: metin
başla
temiz := Kırp(ad)
eğer Uzunluk(temiz) < 2 ise fırlat Hata('Ürün adı en az 2 harf olmalı', 'KuralHatası')
eğer fiyat <= 0 ise fırlat Hata('Fiyat sıfırdan büyük olmalı', 'KuralHatası')
eğer adet < 0 ise fırlat Hata('Adet eksi olamaz', 'KuralHatası')
eğer AdVarMı(temiz) ise fırlat Hata('Bu adda bir ürün zaten var: ' + temiz, 'KuralHatası')
dön Bul(Ekle(temiz, fiyat, adet))
bitir
fonksiyon Sat(no, adet: tamsayı): sözlük
değişken ürün: herhangi
başla
ürün := Bul(no)
eğer ürün = boş ise fırlat Hata('Böyle bir ürün yok', 'BulunamadıHatası')
eğer adet < 1 ise fırlat Hata('Satış adedi en az 1 olmalı', 'KuralHatası')
eğer ürün['adet'] < adet ise fırlat Hata('Stok yetersiz: ' + ürün['adet'] + ' adet var', 'KuralHatası')
AdetDeğiştir(no, -adet)
dön ['ürün': ürün['ad'], 'adet': adet, 'tutar': ürün['fiyat'] * adet, 'kalan': ürün['adet'] - adet]
bitir
fonksiyon StokDeğeri: ondalık
değişken
toplam: ondalık
ü: sözlük
başla
toplam := 0
Hepsi içindeki her ü için toplam := toplam + ü['fiyat'] * ü['adet']
dön toplam
bitir
bitir.Sunum katmanı isteği iş katmanının çağrısına, iş katmanının hatasını da durum koduna çevirir:
program ÜçKatman
{ Sunum katmanı: HTTP isteklerini iş katmanına çevirir, sonucu JSON olarak döndürür.
Kural bilmez, SQL bilmez; yalnızca Ürünİşleri birimini kullanır.
Sunucu olarak çalıştırmak için: yontem 10_uc_katman.y dinle }
kullan Sunucu, Veri, Sistem, Ürünİşleri, ÜrünDeposu
değişken
y: sözlük
{ İş katmanının hatalarını HTTP durum kodlarına çevirir }
fonksiyon HataYanıtı(h: herhangi): herhangi
başla
eğer h.tür = 'KuralHatası' ise dön JsonYanıt(['hata': h.mesaj], 400)
eğer h.tür = 'BulunamadıHatası' ise dön JsonYanıt(['hata': h.mesaj], 404)
fırlat h
bitir
fonksiyon Listele(istek: sözlük): herhangi
başla
dön JsonYanıt(['ürünler': ÜrünListesi, 'stokDeğeri': StokDeğeri])
bitir
fonksiyon YeniÜrün(istek: sözlük): herhangi
değişken v: sözlük
başla
dene
v := JSONÇöz(istek['gövde'])
dön JsonYanıt(ÜrünEkle(v['ad'], v['fiyat'], v['adet']), 201)
yakala (h)
dön HataYanıtı(h)
bitir
bitir
fonksiyon Satış(istek: sözlük): herhangi
başla
dene
dön JsonYanıt(Sat(Tamsayı(istek['parametreler']['no']), Tamsayı(istek['sorgu'].Al('adet', '1'))))
yakala (h)
dön HataYanıtı(h)
bitir
bitir
prosedür Göster(başlık: metin; y: sözlük)
başla
yaz(başlık + ' → ' + y['durum'] + ' ' + y['gövde'], 1)
bitir
başla
Yol('GET', '/urunler', Listele)
Yol('POST', '/urunler', YeniÜrün)
Yol('POST', '/urunler/:no/sat', Satış)
eğer Uzunluk(Argümanlar) > 0 ise
başla
Aç('sqlite:magaza.db')
Dinle(8080)
dön
bitir
{ Argümansız çalıştırınca: bellekteki veritabanıyla, ağ olmadan bir tur }
Aç('sqlite::bellek:')
Göster('ekle', İstekİşle('POST', '/urunler', '{"ad": "defter", "fiyat": 12.5, "adet": 40}'))
Göster('ekle', İstekİşle('POST', '/urunler', '{"ad": " kalem ", "fiyat": 4.0, "adet": 3}'))
Göster('aynı ad', İstekİşle('POST', '/urunler', '{"ad": "defter", "fiyat": 1.0, "adet": 1}'))
Göster('kötü fiyat', İstekİşle('POST', '/urunler', '{"ad": "silgi", "fiyat": 0.0, "adet": 1}'))
Göster('sat', İstekİşle('POST', '/urunler/1/sat?adet=4'))
Göster('stok yetersiz', İstekİşle('POST', '/urunler/2/sat?adet=5'))
Göster('ürün yok', İstekİşle('POST', '/urunler/9/sat'))
Göster('liste', İstekİşle('GET', '/urunler'))
bitir.ekle → 201 {"no": 1, "ad": "defter", "fiyat": 12.5, "adet": 40}
ekle → 201 {"no": 2, "ad": "kalem", "fiyat": 4.0, "adet": 3}
aynı ad → 400 {"hata": "Bu adda bir ürün zaten var: defter"}
kötü fiyat → 400 {"hata": "Fiyat sıfırdan büyük olmalı"}
sat → 200 {"ürün": "defter", "adet": 4, "tutar": 50.0, "kalan": 36}
stok yetersiz → 400 {"hata": "Stok yetersiz: 3 adet var"}
ürün yok → 404 {"hata": "Böyle bir ürün yok"}
liste → 200 {"ürünler": [{"no": 1, "ad": "defter", "fiyat": 12.5, "adet": 36}, {"no": 2, "ad": "kalem", "fiyat": 4.0, "adet": 3}], "stokDeğeri": 462.0}Bu ayrımın kazandırdıkları şunlardır:
- Veritabanını değiştirmek tek satırdır:
Aç('sqlite:magaza.db')yerineAç('postgres://…')yazmak yeterlidir. İş ve sunum katmanları değişmez. - Arayüzü değiştirmek iş kurallarına dokunmaz: aynı
Ürünİşleribirimini birPencereprogramı ya da komut satırı aracı da kullanabilir. - Test etmek kolaydır: bellekteki veritabanı ve
İstekİşleile bütün uygulama ağ ve dosya olmadan,yontem testiçinde sınanır. Yukarıdaki çıktı böyle üretilmiştir.
Katman sınırını dil zorlamaz: sunum katmanının kullan ÜrünDeposu yazıp doğrudan sorgu çalıştırmasını engelleyen bir şey yoktur (örnekte yalnızca veritabanını açmak için kullanılıyor). Sınırı korumak, her birimin arayüz bölümünü küçük tutmakla başlar.