Varlık Çerçeve Varlıklar - Web Hizmetinden Bazı Veriler - En İyi Mimari?


10

Şu anda Entity Framework'ü birkaç web uygulamasında ORM olarak kullanıyoruz ve şimdiye kadar tüm verilerimiz tek bir veritabanında saklandığı için bize çok uygun. Havuz desenini kullanıyoruz ve bunları kullanan hizmetlerimiz (etki alanı katmanı) var ve EF varlıklarını doğrudan ASP.NET MVC denetleyicilerine döndürüyoruz.

Bununla birlikte, veritabanımızdaki kullanıcıyla ilgili ek bilgi veren bir 3. taraf API (bir web servisi aracılığıyla) kullanmak için bir gereksinim ortaya çıkmıştır. Yerel Kullanıcı veritabanımızda, ek bilgi almak için to API'ya sağlayabileceğimiz harici bir kimlik saklayacağız. Biraz bilgi mevcut, ancak basitlik açısından, bunlardan biri kullanıcının şirketi (isim, yönetici, oda, iş unvanı, yer vb.) İle ilgilidir. Bu bilgiler, tek bir yerde kullanılmasının aksine, web uygulamalarımızdaki çeşitli yerlerde kullanılacaktır.

Öyleyse sorum şu, bu bilgiye ulaşmak ve bu bilgilere erişmek için en iyi yer neresi? Çeşitli yerlerde kullanıldığı için, web uygulamasında kullandığımız her yerde geçici olarak getirmek gerçekten mantıklı değil - bu nedenle bu ek verileri etki alanı katmanından döndürmek mantıklı.

İlk düşüncem sadece EF varlığını (EFUser) içerecek bir sarıcı model sınıfı ve yeni bilgileri içeren yeni bir 'ApiUser' sınıfı yaratmaktı ve bir kullanıcı aldığımızda EFUser'i alıp ek API'dan bilgi edinin ve ApiUser nesnesini doldurun. Ancak, bu tek kullanıcı elde etmek için iyi olsa da, birden fazla kullanıcı alırken düşer. Bir kullanıcı listesi alırken API'ya ulaşamayız.

İkinci düşüncem EFUser varlığına ApiUser döndüren tek bir yöntem eklemek ve sadece gerektiğinde doldurmaktı. Bu, yukarıdaki sorunu yalnızca ihtiyaç duyduğumuzda eriştiğimiz için çözer.

Veya son düşünce, veri tabanımızdaki verilerin yerel bir kopyasını tutmak ve kullanıcı oturum açtığında API ile senkronize etmekti. Bu sadece bir senkronizasyon işlemi olduğu için minimum bir iştir - ve isabet yükümüz yok kullanıcı bilgisini her istediğimizde DB ve API. Ancak, bu, verilerin iki yerde depolanması anlamına gelir ve ayrıca bir süredir oturum açmamış herhangi bir kullanıcı için verilerin güncel olmadığı anlamına gelir.

Bu tür bir senaryoyu en iyi nasıl ele alacağınız konusunda herhangi bir tavsiye veya öneriniz var mı?


it's not really sensible to fetch it on an ad-hoc basis-- Neden? Performans nedenleriyle?
Robert Harvey

API'yı geçici olarak vurmak istemiyorum - sadece mevcut varlık yapısını olduğu gibi tutmak ve daha sonra gerektiğinde web uygulamasında API ad-hoc'u çağırmak demek - sadece bunun olmayacağı anlamına geliyordu birçok yerde yapılması gerektiği için mantıklı.
stevehayter

Yanıtlar:


3

Senin durumun

Sizin durumunuzda her üç seçenek de geçerlidir. Ben en iyi seçenek muhtemelen asp.net uygulama farkında bile olmayan bir yerde veri kaynaklarını senkronize olduğunu düşünüyorum. Yani, her seferinde ön planda iki getiriden kaçının, API'yı sessizce db ile senkronize edin). Bu durumda sizin durumunuz için uygun bir seçenekse - diyorum.

Diğer yanıtın önerdiği gibi getirmeyi 'bir kez' yaptığınız bir çözüm çok uygun görünmüyor çünkü hiçbir yerde yanıtı devam ettirmiyor ve ASP.NET MVC her istek için tekrar tekrar getirecek.

Singleton'dan kaçınırdım, bunun olağan sebeplerden dolayı hiç iyi bir fikir olduğunu sanmıyorum.

Üçüncü seçenek uygun değilse - bir seçenek tembel yüklemektir. Olduğunu, bir sınıf varlık uzatmak var ve bu bir üzerinde API vurmak zorunda gerek bazında. Bu çok tehlikeli bir soyutlama olsa da, daha da büyülü ve açık olmayan bir durum.

Sanırım gerçekten birkaç soruya bağlı:

  • API çağrı verileri ne sıklıkta değişir? Sık değil? Üçüncü seçenek. Sıklıkla? Aniden üçüncü seçenek çok uygun değil. Sizin gibi geçici görüşmelere karşı olduğumdan emin değilim.
  • Bir API çağrısı ne kadar pahalıdır? Çağrı başına ödeme yapıyor musunuz? Hızlı mı? Bedava? Hızlıysa, her seferinde bir çağrı yapmak işe yarayabilir, eğer yavaşlarsa, bir tür tahmininiz olması ve çağrıları yapmanız gerekir. Paraya mal olurlarsa - bu önbellekleme için büyük bir teşviktir.
  • Tepki süresinin ne kadar hızlı olması gerekir? Açıkçası daha hızlı daha iyidir, ancak basitlik için hızdan ödün vermek bazı durumlarda, özellikle doğrudan bir kullanıcıya bakmıyorsa buna değebilir.
  • API verileri verilerinizden ne kadar farklı? Kavramsal olarak farklı iki şey mi? Öyleyse, API sonucunu doğrudan sonuçla döndürmek ve diğer tarafın ikinci çağrıyı yapmasına ve yönetimini yönetmesine izin vermek yerine API'yi dışarıda göstermek daha iyi olabilir.

Endişelerin ayrılması hakkında bir iki kelime

Burada Bobson'ın endişelerin ayrılması hakkında söylediklerine karşı çıkmama izin verin. Günün sonunda - bu mantığı bu gibi varlıklara koymak, endişelerin ayrılmasını da ihlal ediyor.

Böyle bir depoya sahip olmak , iş mantığı katmanına sunum merkezli mantığı koyarak endişelerin ayrılmasını da ihlal eder . Deponuz artık aniden asp.net mvc denetleyicilerinde kullanıcıyı nasıl görüntülediğiniz gibi sunu ile ilgili şeylerin farkındadır.

Gelen bu ilgili soruya ben bir denetleyici doğrudan varlıkları erişme hakkında sordunuz. Oradaki cevaplardan birini alıntılamama izin verin:

"Özel Pizza dükkanı BigPizza'ya hoş geldiniz, siparişinizi alabilir miyim?" "Peki, zeytinli bir pizza istiyorum, ama üstünde domates sosu ve altta peynir var ve siyah ve sert bir granit kayası gibi sert olana kadar 90 dakika fırında pişirin." "Tamam, efendim, özel Pizza bizim mesleğimiz, biz başaracağız."

Kasiyer mutfağa gider. "Tezgaha bir psiko var, onunla bir Pizza almak istiyor ... bu bir granit kayası ... bekle ... önce bir isme ihtiyacımız var" diyor aşçı.

"Hayır!" Diye bağırıyor aşçı, "bir daha değil! Bunu zaten denediğimizi biliyorsun." 400 sayfalık bir kağıt yığını alıyor, "burada 2005'ten beri granit kayamız var, ama ... zeytinleri yoktu, ama paprica ... ya da burada en iyi domates ... ama müşteri istedi sadece yarım dakika pişirdi. " "Belki de TopTomatoGraniteRockSpecial olarak adlandırmalıyız?" "Ama alttaki peyniri hesaba katmıyor ..." Kasiyer: "Special'ın ifade etmesi gereken budur." Aşçı, "Ama Pizza kayasının bir piramit gibi oluşması da özel olurdu" diyor. "Hmmm ... zor ..." diyor çaresiz kasiyer.

"PIZZA'm ZEMİNDE YERDE Mİ?", Aniden mutfak kapısından bağırıyor. "Bu tartışmayı durduralım, sadece bu pizzayı nasıl yapacağımı söyle, ikinci kez böyle bir pizzamız olmayacak" diyor aşçı. "Tamam, zeytinli bir Pizza, ama üstte domates sosu ve altta peynir ve düz bir granit kaya gibi siyah ve sert olana kadar 90 dakika fırında pişirin."

(Cevabın geri kalanını okuyun, gerçekten güzel imo).

Bir veritabanı olduğu gerçeğini görmezden gelmek naif - bir veritabanı var ve bunu soyutlamak ne kadar zor olursa olsun, hiçbir yere gitmiyor. Uygulamanız veri kaynağının farkında olacaktır . 'Çalışırken değiştirilemez'. ORM'ler faydalıdır, ancak çözdükleri sorunun ne kadar karmaşık olduğu ve birçok performans nedeni nedeniyle sızarlar (örneğin Select n + 1 gibi).


Çok kapsamlı cevabınız için teşekkürler @ Benjamin. Başlangıçta yukarıda Bobson'un çözümünü kullanarak (cevabını göndermeden önce bile) bir prototip oluşturmaya başladım, ancak bazı önemli noktaları öne sürdünüz. Sorularınızı cevaplamak için: - API çağrısı çok pahalı değil (ücretsiz ve aynı zamanda hızlı). - Verilerin bazı bölümleri düzenli olarak değişecektir (bazıları her iki saatte bir). - Hız oldukça önemlidir, ancak uygulamanın izleyicisi hızlı yüklemeyi hafifletmek mutlak bir gereklilik değildir.
stevehayter

@stevehayter Bu durumda büyük olasılıkla API tarafından yapılan çağrıları istemci tarafından gerçekleştiririm. Daha ucuz ve daha hızlıdır ve size daha hassas bir kontrol sağlar.
Benjamin Gruenbaum

1
Bu cevaplara dayanarak, verilerin yerel bir kopyasını tutmaya daha az eğiliyorum. Aslında API ayrı ayrı açığa ve ek veri bu şekilde işleme eğilimi. Bu, @ Bobson çözümünün basitliği arasında iyi bir uzlaşma olabilir, ancak aynı zamanda biraz daha rahat olduğum bir ayrılık derecesi ekliyor. Bu stratejiye prototipimde bakacağım ve bulgularımla rapor edeceğim (ve ödülü ödüllendireceğim!).
stevehayter

@BenjaminGruenbaum - Argümanınızı takip ettiğimden emin değilim. Önerim depoyu sunumdan nasıl haberdar eder? Elbette, API destekli bir alana erişildiğinin farkındadır, ancak görünümün bu bilgilerle ne yaptığıyla ilgisi yoktur.
Bobson

1
Her şeyi istemci tarafına taşımayı seçtim - ancak EFUser'da (sunum katmanında bulunan ayrı bir montajda) bir genişletme yöntemi olarak. Yöntem, API'dan verileri döndürür ve tek bir ton ayarlar, böylece tekrar tekrar vurulmaz. Nesnelerin ömrü o kadar kısadır ki, burada bir singleton kullanmayla ilgili bir sorunum yok. Bu şekilde bir dereceye kadar ayrılık var, ama yine de EFUser varlığıyla çalışmanın rahatlığını yaşıyorum. Tüm katılımcılara yardımları için teşekkürler. Kesinlikle ilginç bir tartışma :).
stevehayter

2

Endişelerin uygun şekilde ayrılmasıyla , Varlık Çerçevesi / API seviyesinin üzerinde hiçbir şey verinin nereden geldiğinin farkına varmamalıdır. API çağrısı pahalı olmadığı sürece (zaman veya işlem açısından), onu kullanan verilere erişmek veritabanından verilere erişmek kadar şeffaf olmalıdır.

Bunu uygulamanın en iyi yolu EFUser, gerektiğinde API verilerini tembel olarak yükleyen nesneye ekstra özellikler eklemek olacaktır . Bunun gibi bir şey:

partial class EFUser
{
    private APIUser _apiUser;
    private APIUser ApiUser
    {
       get { 
          if (_apiUser == null) _apiUser = API.LoadUser(this.ExternalID);
          return _apiUser;
       }
    }
    public string CompanyName { get { return ApiUser.UserCompanyName; } }
    public string JobTitle{ get { return ApiUser.UserJobTitle; } }
}

Dışarıdan, ilk kez ya CompanyNameda JobTitlekullanıldığında, tek bir API çağrısı (ve dolayısıyla küçük bir gecikme) olacaktır, ancak nesne yok edilene kadar sonraki tüm çağrılar veritabanı erişimi kadar hızlı ve kolay olacaktır.


Teşekkürler @Bobson ... Bu aslında aşağıya başladığım başlangıç ​​rotasıydı (kullanıcı listeleri için ayrıntıları toplu olarak yüklemek için bazı uzantı yöntemleri eklenmişti - örneğin, bir kullanıcı listesi için şirket adını görüntülüyor). Şimdiye kadar ihtiyaçlarımı karşılayacak gibi görünüyor - ama aşağıda Benjamin bazı önemli noktaları gündeme getiriyor, bu yüzden bu hafta değerlendirmeye devam edeceğim.
stevehayter

0

Bir fikir, ApiUser'ı her zaman fazladan bilgiye sahip olmayacak şekilde değiştirmektir. Bunun yerine, onu almak için ApiUser üzerinde bir yöntem koyun:

ApiUser apiUser = backend.load($id);
//Locally available data directly on the ApiUser like so:
String name = apiUser.getName();
//Expensive extra data available after extra call:
UserExtraData extraData = apiUser.fetchExtraData();
String managerName = extraData.getManagerName();

Ayrıca, ApiUser nesnesinden UserExtraData çıkarmak zorunda kalmamak için ekstra verilerin tembel yüklemesini kullanmak için bunu biraz değiştirebilirsiniz:

//Extra data fetched on first get:
String managerName = apiUser.lazyGetExtraData().getManagerName();

Bu şekilde, bir listeniz olduğunda, fazladan veriler varsayılan olarak getirilmez. Ancak listeyi gezerken yine de erişebilirsiniz!


Burada ne demek istediğinizden emin değilim - backend.load () 'de zaten bir yük yapıyoruz - bu yüzden API verilerini yükleyecek misiniz?
stevehayter

Açıkça talep edilene kadar ekstra yük yapmayı beklemelisiniz - api verilerini tembel yükleyin.
Alexander Torstling
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.