Veritabanının kötü tasarlandığı ilişkisel bir veritabanı tabanlı uygulamada daha iyi OO kodu nasıl oluşturulur


19

Ben esas olarak her sayfada birkaç tablo ve bu tablolar için geçerli bir filtre olan benzer sayfaları bir demet oluşan bir Java web uygulaması yazıyorum. Bu tablolardaki veriler bir SQL veritabanından gelir.

Veritabanı kötü tasarlanmış ve mybatis daha veritabanı odaklı bir araç olduğundan myBatis benim durumumda en iyi seçim olmayabilir ORM olarak kullanıyorum.

Ben veritabanının kötü tasarımı nedeniyle, bu sorgular çok farklı olabilir benzer şeyler için farklı sorgular yazmak zorunda çünkü yinelenen kod çok yazıyorum buluyorum. Yani sorguları kolayca parametreleyemiyorum. Bu benim koduma yayılır ve basit bir döngü ile benim tablonun sütunları üzerinde satırları doldurmak yerine gibi kod var:

olsun A Verileri (p1, ..., pi);

olsun B Verileri (p1, ..., pi);

olsun C Verileri (p1, ..., pi);

olsun D Verileri (p1, ..., pi); ...

Ve bu, yakında farklı sütunlara sahip farklı tablolarımız olduğunda patlar.

Ayrıca karmaşıklığa "küçük kapı" kullandığım gerçeğini de ekler, bu da nesnelerin sayfadaki html öğelerine eşlenmesini sağlar. Java kodum, veritabanı ve ön uç arasında bir adaptör haline geliyor, bu da bana içinde bir miktar mantıkla birlikte çok sayıda kablolama, ortak kod kodu oluşturmamı sağlıyor.

Doğru çözüm ORM haritacıları db için daha homojen bir arayüz sunan bir ekstra katman ile sarma mı yoksa yazdığım bu spagetti kodu ile başa çıkmak için daha iyi bir yolu var mı?

EDIT: Veritabanı hakkında daha fazla bilgi

Veritabanı esas olarak telefon araması bilgilerini içerir. Kötü tasarım şunlardan oluşur:

Etki alanı bilgisi ile ilgisi olmayan, birincil anahtar olarak yapay kimliğe sahip tablolar.

Benzersiz, tetikleyici, çek veya yabancı anahtar yok.

Farklı kayıtlar için farklı kavramlarla eşleşen genel bir ada sahip alanlar.

Farklı koşullara sahip diğer tablolarla kesilerek kategorilere ayrılabilen kayıtlar.

Dize olarak depolanan sayı veya tarih olması gereken sütunlar.

Özetle, her yerde dağınık / tembel bir tasarım.


7
Veritabanı tasarımını düzeltmek bir seçenek midir?
RMalke

1
Lütfen veritabanının nasıl kötü tasarlandığını açıklayınız.
Tulains Córdova

@Renan Malke Stigliani Ne yazık ki, ona bağlı eski bir yazılım olduğu için, ancak bazı tabloları biraz farklı bir tasarıma yansıttım ve kodları basitleştiren tabloları doldurdum. Ancak bununla gurur duymuyorum ve tabloları gelişigüzel çoğaltmak istemiyorum
DPM

1
Bu kitap, datbase problesmini düzeltmeye ve eski kodun çalışmaya devam etmesine nasıl başlayabileceğinize dair bir şeyler verebilir: amazon.com/…
HLGEM

4
Listelediğiniz sorunların çoğu. . . değildir. Doğal anahtarlardan ziyade yedek anahtarların kullanımı aslında günümüzde oldukça standart bir öneridir; hiç "kötü tasarım" değil. Kısıtlamaların olmaması ve uygun olmayan sütun türlerinin kullanılması, "kötü tasarım" söz konusu olduğunda daha iyi bir örnektir, ancak aslında uygulama kodunuzu etkilememelidir (bu sorunları kötüye kullanmayı planlamıyorsanız?).
ruakh

Yanıtlar:


53

Nesne yönelimi özellikle önemlidir, çünkü bu tür senaryolar ortaya çıkar ve karmaşıklığı kapsamanıza izin veren soyutlamaları makul bir şekilde tasarlamanız için araçlar sağlar.

Buradaki asıl soru şu, bu karmaşıklığı nereye ekliyorsunuz?

Bir dakika geriye çekilip burada bahsettiğim 'karmaşıklık' ile konuşmama izin verin. Sorununuz (anladığım kadarıyla; yanlışsam beni düzelt), verilerle tamamlamanız gereken görevler için etkili bir şekilde kullanılabilir bir model olmayan bir kalıcılık modelidir. Bu etkili ve diğer görevler için kullanılabilir, ancak olabilir sizin görevleri.

Peki, araçlarımız için iyi bir model sunmayan verilerimiz olduğunda ne yaparız?

Çevirmek. Yapabileceğiniz tek şey bu . Bu çeviri, yukarıda bahsettiğim 'karmaşıklık'. Şimdi modeli çevireceğimizi kabul ettiğimize göre, birkaç faktöre karar vermeliyiz.

Her iki yönü de çevirmemiz gerekiyor mu? Her iki yön de şu şekilde tercüme edilecek mi:

(Tbl A, Tbl B) -> Obj X (okuma)

Obj X -> (Tbl A, Tbl B) (yazma)

veya ekleme / güncelleme / silme etkinlikleri Obj X olarak veri okuyacak şekilde farklı bir nesne türünü temsil ediyor, ancak veriler Obj Y'den ekleniyor / güncelleniyor mu? Bu iki yoldan hangisine gitmek istediğiniz veya güncelleme / ekleme / silme mümkün değilse çeviriyi nereye koymak istediğinizde önemli faktörlerdir .


Nerede çeviri yapıyorsun?

Bu cevapta yaptığım ilk ifadeye geri dönersek; OO kapsülü karmaşıklığı ve geri verir ne burada atıfta sen, ama sen gerektiğini sadece bu gerçektir gerekir Eğer tüm kod içine sızan ve seep gelmez bunu sağlamak istiyorsanız o karmaşıklığı saklanması. Aynı zamanda, mükemmel bir soyutlamaya sahip olamayacağınızı tanımak önemlidir, bu yüzden çok etkili ve kullanılabilir bir taneye sahip olmaktan daha az endişe edin.

Yine şimdi; senin sorunun: Bu karmaşıklığı nereye koyarsın? Seçimleriniz var.

Bunu yapabilecek içinde saklı prosedürleri kullanarak veritabanı. Bunun ORM'lerle çok iyi oynamamasının dezavantajı vardır, ancak bu her zaman doğru değildir. Saklı yordamlar, performans da dahil olmak üzere bazı faydalar sağlar. Ancak saklı yordamlar çok fazla bakım gerektirebilir, ancak özel senaryoyu analiz etmek ve bakımın diğer seçeneklerden daha fazla veya daha az olup olmayacağını söylemek size kalmıştır. Kişisel olarak saklı prosedürler konusunda çok yetenekliyim ve bu nedenle mevcut yetenek gerçeği yükü azaltır; Ne dayalı kararlar değerini hafife asla yok biliyorum. Bazen en uygun çözüm doğru çözümden daha uygun olabilir, çünkü siz veya ekibiniz bunu en iyi çözümden daha iyi nasıl oluşturacağınızı ve koruyacağınızı bilirsiniz.

Başka bir veritabanı içi seçenek de görünümlerdir. Veritabanı sunucunuza bağlı olarak, bunlar son derece optimal veya alt optimal olabilir veya hiç etkili olmayabilir, dezavantajlardan biri, veritabanınızda hangi indeksleme seçeneklerinin mevcut olduğuna bağlı olarak sorgu süreleri olabilir. Hiçbir zaman veri değişikliği (ekleme / güncelleme / silme) yapmanız gerekmiyorsa, görünümler daha da iyi bir seçim haline gelir.

Veritabanının ötesine geçerek depo desenini kullanmak için eski bir bekleme moduna sahipsiniz. Bu çok etkili olabilen zamanla test edilmiş bir yaklaşımdır. Dezavantajlar kazan plakasını içerir, ancak iyi faktörlü depolar bunun bir miktarını önleyebilir ve bunlar talihsiz miktarlarda kazan plakasıyla sonuçlansa bile, havuzun iyi bir API sunmanın yanı sıra anlaşılması ve bakımı kolay basit bir kod olma eğilimindedir. /soyutlama. Ayrıca depolar, veritabanı içi seçeneklerle kaybettiğiniz birim test edilebilirlikleri için iyi olabilir.

Orada otomatik-haritacı gibi bir ORM kullanmayı akla getirebilecek araçlar vardır, burada veritabanı modeli arasında orm'dan kullanılabilir modellere çeviri yapabilirler, ancak bu araçların bazıları sihir gibi davranmayı sürdürmek / anlamak için zor olabilir; iyi anlaşıldıklarında daha az bakım yükü ile sonuçlanan minimum ek yük kodu oluştururlar.

Daha sonra veritabanından daha da ileriye adım atıyorsunuz , yani çevrilmemiş kalıcılık modeli ile başa çıkacak daha fazla miktarda kod olacak ve bu gerçekten tatsız olacak. Bu senaryolarda, çeviri katmanını şimdi yaptığınız gibi göründüğü kullanıcı arayüzünüze yerleştirmekten bahsediyorsunuz. Bu genellikle çok kötü bir fikirdir ve zamanla çok çürür.


Şimdi çılgın konuşmaya başlayalım .

Bu Object, var olan her şeyden önce soyutlama değildir. Uzun yıllar boyunca bilgisayar biliminin incelendiği ve hatta daha önce matematik çalışmalarından elde edilen bir soyutlama derinliği olmuştur. Yaratıcı olmaya başlayacaksak, üzerinde çalışılmış olan bilinen soyutlamalar hakkında konuşmaya başlayalım.

Aktör modeli var.Bu ilginç bir yaklaşımdır, çünkü yaptığınız tek şey, tüm çalışmaları etkili bir şekilde bu diğer koda delege eden diğer koda mesaj göndermek olduğunu ve bu da tüm kodunuzdan karmaşıklığı kapsüllemede çok etkili olduğunu söylüyor. Bu, bir oyuncuya "Y'ye gönderilen Obj X'e ihtiyacım var" yazan bir mesaj gönderdiğiniz ve Y konumunda Obj X'i işleyen bir yanıt bekleyen bir kabınız olduğu sürece işe yarayabilir. Hatta talimat veren bir mesaj bile gönderebilirsiniz. "Obj X'e ve Y, Z hesaplamasına ihtiyacım var" ve sonra beklemenize bile gerek yok; çeviri, bu ileti geçişinin diğer tarafında gerçekleşir ve sonucunun okunmasına gerek duymuyorsanız devam edebilirsiniz. Bu, aktör modelinin amaçlarınız için hafifçe kötüye kullanılması olabilir, ancak hepsi bağlıdır;

Diğer bir kapsülleme sınırı süreç sınırlarıdır. Bunlar karmaşıklığı çok etkili bir şekilde ayırmak için kullanılabilir. Çeviri kodunu, SOAP, REST kullanarak veya gerçekten kendi protokolünüzü (önerilmiyor) kullanarak iletişimin basit HTTP olduğu bir web hizmeti olarak oluşturabilirsiniz. STOMP tamamen kötü bir yeni protokol değildir. Veya hangi protokolü seçerseniz kullanarak çok hızlı bir şekilde iletişim kurmak için sistem yerel bir halka açık bellek borusu ile normal bir daemon servisi kullanın. Bunun aslında bazı iyi faydaları var:

  • Daha eski ve daha yeni sürüm desteği için çeviri yapan birden çok işleminiz olabilir ve aynı zamanda bir V2 nesnesini tanıtmak için çeviri hizmetini güncellemenize ve daha sonra ayrı bir noktada yeni nesneyle çalışacak şekilde tüketim kodunu güncellemenize olanak tanır. modeli.
  • Süreci performans için bir çekirdeğe sabitlemek gibi ilginç şeyler yapabilirsiniz, aynı zamanda bu yaklaşıma bu verilere dokunmak için güvenlik ayrıcalıklarıyla çalışan tek işlem yaparak bir miktar güvenlik güvenliği elde edersiniz.
  • Uzun bir süre için soyutlamanızın minimum sızıntısını garanti eden sabit kalacak süreç sınırları hakkında konuşurken çok güçlü bir sınır elde edersiniz, çünkü çeviri alanına kod yazmak, çeviri alanının dışında çağrılmayacağı için işlem kapsamını paylaşmayacak, sözleşme ile sabit bir kullanım senaryoları seti sağlayacaktır.
  • Eşzamansız / engellemeyen güncellemeler için daha basit özellik.

Dezavantajları, genel olarak gerekenden daha fazla bakımdır, performansı ve bakımı etkileyen iletişim yükü.


Karmaşıklığı kapsüllemek için bu karmaşıklığın sisteminizde daha garip ve meraklı yerlere yerleştirilmesine izin verebilecek çok çeşitli yollar vardır. Üst düzey fonksiyonların formlarını kullanarak (genellikle strateji deseni veya nesne desenlerinin çeşitli diğer formları kullanılarak sahte zamanlar), çok ilginç şeyler yapabilirsiniz.

Bu doğru, bir monad hakkında konuşmaya başlayalım. Bu çeviri katmanını, gerekli bağımsız çevirileri yapan küçük özel işlevlerden çok bağımsız bir şekilde oluşturabilirsiniz, ancak tüm bu çeviri işlevlerini görünmeyecek şekilde gizleyin, böylece dış koda erişilemezler. Bu, onlara çok fazla dış kod etkilemeden kolayca değişmelerine izin veren güvenlerini azaltma avantajına sahiptir. Daha sonra, güzel OO model türü nesnelerden herhangi birinde çalışan daha üst düzey işlevleri (anonim işlevler, lambda işlevleri, strateji nesneleri, ancak bunları yapılandırmanız gerekir) kabul eden bir sınıf oluşturursunuz. Daha sonra, bu işlevleri kabul eden temel kodun uygun çeviri yöntemlerini kullanarak gerçek yürütmeyi yapmasına izin vermiş olursunuz.

Bu, tüm çevirinin tüm kodunuzdan yalnızca sınırın diğer tarafında bulunmadığı bir sınır oluşturur ; yalnızca bu tarafta , kodunuzun geri kalanının, söz konusu sınırın giriş noktasının dışında herhangi bir şey bilmemesine izin vermek için kullanılır .

Tamam, evet bu gerçekten çılgınca konuşuyor, ama kim bilir; sadece deli olabilirsiniz (ciddi olarak,% 88'in altında bir delilik derecesi olan monadları üstlenmeyin, gerçek yaralanma riski vardır).


4
Vay be, olağanüstü kapsamlı bir cevap. Sadece SE bana izin verirse, bunu bir kereden fazla iptal ederim.
Marjan Venema

11
Film versiyonu ne zaman çıkıyor?
yannis

3
@JimmyHoffa Bravo efendim !!! Bu yanıtı işaretleyeceğim ve kızım yaşlandığında göstereceğim.
Tombatron

4

Benim önerim:

Aşağıdakileri yapan veritabanı görünümleri oluşturun:

  1. Sütunlara anlamlı adlar verin
  2. Bu karmaşıklığı ortadan kaldırabilmeniz için "farklı koşullara sahip diğer tablolarla çaprazlama" yapın.
  3. Dizgi olarak saklanan sayıları veya tarihleri ​​sırasıyla sayılara ve tarihlere dönüştürün.
  4. Bazı kriterlere göre, bulunmayan yerlerde benzersizlik yaratın.

Fikir, kötü olanın üstünde daha iyi bir tasarım taklit eden bir cephe oluşturmaktır.

Ardından ORM'yi gerçek tablolar yerine o cepheyle ilişkilendirin.

Bu, eklemeleri basitleştirmez.


Veritabanı görünümlerini kullanmak harika bir fikir ve çirkinliği en alt düzeyde soyutlayan en zarif eylemler gibi görünüyor, nedense bunu düşünmemiştim. Teşekkür ederim.
DPM

3

Mevcut veritabanı şemanızın, daha iyi tasarlanmış bir şema ile soyutlanabilecek görevler için nasıl daha özel kod ve sorgular yazmanıza neden olduğunu görebiliyorum, ancak iyi nesne tabanlı kod yazma yeteneğinizi engellememelidir.

  • SOLID ilkelerini hatırlayın .
  • Kolayca birim testine tabi tutulabilen kodlar yazın (bu genellikle SOLID ilkelerini izler).
  • İş mantığınızı görüntü mantığınızdan ayrı tutun.
  • Apache Wicket belgelerini ve örneklerini okuyun - bu çerçeve muhtemelen düşündüğünüzden daha fazla kazan plakası tasarrufu sağlayabilir, bu nedenle etkili bir şekilde nasıl kullanacağınızı öğrenin.
  • Veritabanıyla ilgilenmesi gereken mantığı, iş mantığınızın çalışabileceği temiz bir arabirim sağlayan ayrı bir katmanda tutun. Bu şekilde, siz (veya gelecekteki bir bakıcı) şemayı geliştirme şansınız olursa, iş mantığında çok fazla değişiklik yapmadan bunu yapabilirler.

Kendinizi mükemmel olmayan bir veritabanı şeması ile çalışırken bulduğunuzda, işinizi zorlaştıran tüm yolları ele almak kolaydır, ancak bir noktada bu şikayetleri bir kenara bırakıp en iyisini yapmanız gerekir.

Kusurlu şemaya rağmen temiz, yeniden kullanılabilir, kolayca bakımı kolay kod yazmak için yaratıcılığınızı kullanma fırsatı olarak düşünün.


1

Daha iyi nesne yönelimli kod hakkında ilk sorunuzu yanıtlayarak, SQL konuşan nesneleri kullanmanızı öneririm . ORM, bir nesne üzerinde çalıştığı ve OOP'taki nesne, amacına ulaşmak için tüm kaynaklara sahip olan kendi kendine yeterli bir varlık olduğu için özünde nesne yönelimli ilkelere aykırıdır. Eminim bu yaklaşım kodunuzu basitleştirebilir.

Sorunlu alandan, yani alan adınızdan bahsetmişken, toplu kökleri belirlemeye çalışacağım . Bunlar, alan adınızın tutarlılık sınırlarıdır. Kesinlikle tutarlılığını her zaman sürdürmesi gereken sınırlar. Toplamalar etki alanı olayları aracılığıyla iletişim kurar. Yeterince büyük bir sisteminiz varsa, muhtemelen onu alt sistemlere ayırmaya başlamalısınız (buna SOA, Microservice, Bağımsız sistemler vb. Deyin)

Ayrıca CQRS kullanmayı düşünürüm - hem yazma hem de okuma tarafınızı büyük ölçüde basitleştirebilir. Udi Dahan'ın bu konuyla ilgili makalesini mutlaka okuyun .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.