“İş mantığı modelde değil hizmette olmalı” ne kadar doğrudur?


397

Durum

Bu akşamın erken saatlerinde StackOverflow ile ilgili bir soruya cevap verdim .

Soru:

Mevcut bir nesnenin düzenlenmesi havuz katında mı yoksa hizmette mi yapılmalı?

Örneğin, borcu olan bir kullanıcı varsa. Borcunu değiştirmek istiyorum. Bunu UserRepository'de veya serviste yapmalı mıyım, örneğin BuyingService'i bir nesne alarak, düzenleyerek ve kaydederek mi yapmalıyım?

Cevabım:

Bir nesneyi aynı nesneye döndürme sorumluluğunu bırakmalı ve bu nesneyi almak için depoyu kullanmalısınız.

Örnek durum:

class User {
    private int debt; // debt in cents
    private string name;

    // getters

    public void makePayment(int cents){
        debt -= cents;
    }
}

class UserRepository {
    public User GetUserByName(string name){
        // Get appropriate user from database
    }
}

Aldığım bir yorum:

İş mantığı gerçekten bir hizmette olmalıdır. Manken değil.

İnternet ne diyor?

Bu yüzden beni aramaya soktu çünkü gerçekten (bilinçli olarak) bir servis katmanı kullanmadım. Servis Katmanı deseni ve Çalışma Birimi desenini okumaya başladım, ancak şu ana kadar bir servis katının kullanılması gerektiğine ikna olduğumu söyleyemem.

Örneğin , Anemic Domain Modelinin anti-paterni hakkındaki Martin Fowler'ın bu makalesini ele alalım :

Etki alanı alanındaki isimlerden sonra adlandırılan nesneler var ve bu nesneler, gerçek etki alanı modellerinin sahip olduğu zengin ilişkiler ve yapıyla bağlantılı. Davranış, davranışa baktığınız zaman gelir ve bu nesneler üzerinde neredeyse hiçbir davranış olmadığını fark edersiniz, bu da onları alıcıların ve ayarlayıcıların çantalarından daha az yapar. Gerçekten de bu modeller, etki alanı nesnelerine herhangi bir etki alanı mantığı koymadığınızı söyleyen tasarım kuralları ile birlikte gelir. Bunun yerine, tüm etki alanı mantığını yakalayan bir dizi hizmet nesnesi vardır. Bu hizmetler etki alanı modelinin tepesinde yaşar ve etki alanı modelini veriler için kullanır.

(...) Bir etki alanı nesnesinde olması gereken mantık, etki alanı mantığıdır - doğrulamalar, hesaplamalar, iş kuralları - ne şekilde istersen.

Bana göre durum tam olarak bununla ilgili gibiydi: Bir nesnenin verilerinin manipülasyonunu, o sınıfın içine tam da bunu yapan yöntemler getirerek savundum. Bununla birlikte, bunun her iki şekilde de verilmesi gerektiğinin farkındayım ve muhtemelen bu yöntemlerin nasıl çağrıldığını (bir havuz kullanarak) yapması gerekiyor.

Ayrıca, bu makalede (aşağıya bakınız) Hizmet Katmanının, çalışmayı asıl iş yoğun bir katmandan ziyade, temeldeki modele delege eden bir cephe olarak kabul edildiğini de hissettim .

Uygulama Katmanı [Servis Katmanı için adı]: Yazılımın yapması gereken işleri tanımlar ve etkileyici etki alanı nesnelerini sorunları çözmeleri için yönlendirir. Bu katmanın sorumlu olduğu işler, iş için anlamlıdır veya diğer sistemlerin uygulama katmanlarıyla etkileşim için gereklidir. Bu katman ince tutulur. İş kurallarını veya bilgisini içermez, ancak yalnızca görevleri koordine eder ve çalışmaları bir sonraki katmandaki etki alanı nesnelerinin işbirliğine aktarır. İşletmenin durumunu yansıtan bir durumu yoktur, ancak kullanıcı veya program için bir görevin ilerlemesini yansıtan bir durumu olabilir.

Hangisi burada güçlendirilmiş :

Servis arayüzleri Servisler, gelen tüm mesajların gönderildiği bir servis arayüzü sunar. Hizmet arayüzünü, uygulamada uygulanan iş mantığını (tipik olarak iş katmanındaki mantığı) potansiyel tüketicilere maruz bırakan bir cephe olarak düşünebilirsiniz.

Ve burada :

Hizmet katmanı herhangi bir uygulamadan veya iş mantığından yoksun olmalı ve öncelikle birkaç kaygıya odaklanmalıdır. İş Katmanı çağrılarını kapsamalı, Etki Alanınızı müşterilerinizin anlayabileceği ortak bir dile çevirin ve sunucu ile istemci arasında iletişim ortamını idare etmelidir.

Bu, Hizmet Katmanı hakkında konuşan diğer kaynaklara ciddi bir karşıtlıktır :

Hizmet katmanı, aynı işleme ait işlemlerin yapıldığı iş birimleri olan yöntemlerden oluşan sınıflardan oluşmalıdır.

Veya daha önce bağladığım bir sorunun ikinci cevabı :

Bir noktada, uygulamanız biraz iş mantığı isteyecektir. Ayrıca, kötü ya da uygun olmayan bir şeyin talep edilmediğinden emin olmak için girişi doğrulamak isteyebilirsiniz. Bu mantık servis katmanınıza aittir.

"Çözüm"?

Bu cevaptaki yönergelere uyarak, Servis Katmanı kullanan aşağıdaki yaklaşımı gördüm:

class UserController : Controller {
    private UserService _userService;

    public UserController(UserService userService){
        _userService = userService;
    } 

    public ActionResult MakeHimPay(string username, int amount) {
        _userService.MakeHimPay(username, amount);
        return RedirectToAction("ShowUserOverview");
    }

    public ActionResult ShowUserOverview() {
        return View();
    }
}

class UserService {
    private IUserRepository _userRepository;

    public UserService(IUserRepository userRepository) {
        _userRepository = userRepository;
    }

    public void MakeHimPay(username, amount) {
        _userRepository.GetUserByName(username).makePayment(amount);
    }
}

class UserRepository {
    public User GetUserByName(string name){
        // Get appropriate user from database
    }
}

class User {
    private int debt; // debt in cents
    private string name;

    // getters

    public void makePayment(int cents){
        debt -= cents;
    }
}

Sonuç

Hep birlikte burada pek bir şey değişmedi: kontrol cihazından gelen kod servis katmanına taşındı (bu iyi bir şey, bu yüzden bu yaklaşıma bir terslik var). Ancak bu benim orijinal cevabım ile ilgisi var gibi görünmüyor.

Tasarım desenlerinin, mümkün olduğunda uygulanacak taşa konulmuş kurallar değil, kurallar olduğunu fark ediyorum. Yine de, servis katmanının ve nasıl dikkate alınması gerektiğine dair kesin bir açıklama bulamadım.

  • Kontrol cihazından mantığı çıkarmak ve bunun yerine bir hizmetin içine koymak için bir araç mı?

  • Denetleyici ile etki alanı arasında bir sözleşme mi yapılması gerekiyor?

  • Etki alanı ve hizmet katmanı arasında bir katman olmalı mı?

Ve, son fakat en az değil: orijinal yorumu takip ederek

İş mantığı gerçekten bir hizmette olmalıdır. Manken değil.

  • Bu doğru mu?

    • İş mantığımı model yerine bir serviste nasıl tanıtırım?

6
Hizmet katmanına, işlem komut dosyasının kaçınılmaz kısmını toplam köklere etki eden bir yer olarak koyarım. Hizmet katmanım çok karmaşık hale gelirse, bu bana Anemik Model yönünde hareket ettiğimi gösterir ve etki alanı modelimin dikkat ve incelemeye ihtiyacı var. Ayrıca mantığını, doğası gereği kopyalanmayacak olan SL'ye koymaya çalışıyorum.
Pavel Voronin

Hizmetlerin Model katmanının bir parçası olduğunu düşündüm. Bunu düşünmekte yanlış mıyım?
Florian Margaine 13:13

Temel kural kullanıyorum: İhtiyacınız olmayan şeylere güvenmeyin; Aksi halde, ihtiyacım olmayan bölümdeki değişikliklerden etkilenebilir (genellikle olumsuz). Bunun bir sonucu olarak, net bir şekilde tanımlanmış rollerin birçoğu ile bitirdim. Veri nesnelerim, tüm istemciler kullandığı sürece davranış içerir. Aksi halde, davranışı gereken rolü yerine getiren sınıflara taşırım.
beluchin

1
Uygulama akış kontrol mantığı bir kontrol cihazına aittir. Veri erişim mantığı bir havuza aittir. Doğrulama mantığı bir servis katmanına aittir. Hizmet katmanı, bir denetleyici ve depo katmanı arasındaki iletişime aracılık eden bir ASP.NET MVC uygulamasında ek bir katmandır. Servis katmanı işletme doğrulama mantığını içerir. deposu. asp.net/mvc/overview/older-versions-1/models-data/…
Kbdavis07

3
IMHO, doğru OOP tarzı etki alanı modeli gerçekten "işletme hizmetlerinden" kaçınmalı ve iş mantığını modelde tutmalıdır. Ancak uygulama, açıkça adlandırılmış yöntemlerle büyük bir iş katmanı yaratmanın ve tüm yöntemin çevresine bir işlem (veya iş birimi) koymanın çok cazip olduğunu gösteriyor. Bir işletme hizmeti yönteminde birden fazla model sınıfını işlemek, bu model sınıfları arasındaki ilişkileri planlamaktan çok daha kolaydır, çünkü o zaman cevaplamak için zor sorularınız olur: toplam kök nerede? Bir veritabanı işlemini nereden başlatmalı / işlemeliyim? Vb
JustAMartin

Yanıtlar:


368

Bir hizmetin sorumluluklarının ne olduğunu tanımlamak için önce bir hizmetin ne olduğunu tanımlamanız gerekir .

Hizmet kanonik veya genel bir yazılım terimi değildir. Aslında, Servicebir sınıf ismindeki sonek , kötü huylu Yönetici'ye çok benziyor : Size, nesnenin gerçekte yaptığı şey hakkında neredeyse hiçbir şey söylemez .

Gerçekte, bir servisin yapması gereken şey mimariye özgüdür:

  1. Geleneksel katmanlı bir mimaride, hizmet tam anlamıyla iş mantığı katmanıyla eş anlamlıdır . UI ve Data arasındaki katmandır. Bu nedenle, tüm iş kuralları hizmetlere giriyor. Veri katmanı sadece temel CRUD işlemlerini anlamalı ve UI katmanı sadece iş DTO'larının iş nesnelerine ve iş nesnelerinin haritalandırılmasıyla ilgilenmelidir.

  2. RPC tarzı dağıtılmış bir mimaride (SOAP, UDDI, BPEL, vb.) Hizmet , fiziksel bir bitiş noktasının mantıksal sürümüdür . Esasen, bakımcının genel bir API olarak sağlamak istediği bir faaliyetler topluluğudur. Çeşitli en iyi uygulama kılavuzları, bir hizmet işleminin aslında bir CRUD değil, işletme düzeyinde bir işlem olması gerektiğini açıklar ve ben de aynı fikirdeyim.

    Yönlendirme Ancak, her şeyi gerçek bir uzaktan hizmet aracılığıyla ciddiye performansını olumsuz etkileyebileceğini, normalde en iyisi değil aslında iş mantığı kendilerini uygulamak bu hizmetleri sahip olmak; bunun yerine, bir "iç" iş nesnesi kümesini sarmaları gerekir. Tek bir servis bir veya birkaç iş nesnesini içerebilir.

  3. Bir MVP / MVC / MVVM / MV * mimarisinde, hizmetler hiç mevcut değildir. Veya bunu yaparlarsa, terim bir denetleyiciye veya görünüm modeline enjekte edilebilecek herhangi bir jenerik nesneye atıfta bulunmak için kullanılır. İş mantığı modelinizde . Karmaşık işlemleri düzenlemek için "hizmet nesneleri" oluşturmak istiyorsanız, uygulama detayı olarak görülür. Ne yazık ki, birçok insan MVC'yi bu şekilde uygular, ancak modelin kendisi hiçbir şey yapmadığı için bir anti-patern ( Anemik Alan Modeli ) olarak kabul edilir, çünkü bu UI için sadece bir grup özelliktir.

    Bazı insanlar yanlışlıkla 100 hatlı bir kontrol yöntemi almanın ve hepsini bir hizmete sokmanın bir şekilde daha iyi bir mimari için yapıldığını düşünüyor. Gerçekten de değil; Tek yaptığı, başka bir, muhtemelen gereksiz bir dolaysızlık katmanı eklemek. Pratik olarak konuşursak, kontrolör hala işi yapıyor, sadece kötü bir şekilde adlandırılan "yardımcı" nesnesi üzerinden yapıyor. Anemik bir etki alanı modelinin nasıl faydalı bir model haline getirileceğinin açık bir örneği için Jimmy Bogard'ın Kötü Etki Alanı Modelleri sunumunu şiddetle tavsiye ediyorum . Bu, ortaya koyduğunuz modellerin ve bağlamında hangi işlemlerin gerçekten geçerli olduğunu dikkatlice incelemeyi içerir .

    Örneğin, veritabanınız Emir içeriyorsa ve Toplam Tutar için bir sütununuz varsa, uygulamanızın muhtemelen bu alanı isteğe bağlı bir değerle değiştirmesine izin verilmemelidir, çünkü (a) geçmiş ve (b) olması gerektiği gibi ne göre belirlenir içinde sırayla yanı sıra belki başka bir zaman duyarlı veri / kurallar. Emirleri yönetmek için bir servis oluşturmak bu sorunu mutlaka çözmez, çünkü kullanıcı kodu hala gerçek Emir nesnesini alabilir ve üzerindeki miktarı değiştirebilir. Bunun yerine, emrin kendisi yalnızca güvenli ve tutarlı yollarla değiştirilebilmesini sağlamaktan sorumlu olmalıdır.

  4. DDD'de, hizmetler, herhangi bir toplam köke uygun olmayan bir işlem yaptığınız durum için özel olarak tasarlanmıştır . Burada dikkatli olmalısınız, çünkü genellikle bir hizmete duyulan ihtiyaç doğru kökleri kullanmadığınız anlamına gelebilir. Ancak yaptığınızı varsayalım, bir hizmet birden çok kökten işlemleri koordine etmek veya bazen etki alanı modelini içermeyen endişeleri (belki de bir BI / OLAP veritabanına bilgi yazmak gibi) ele almak için kullanılır.

    DDD hizmetinin kayda değer bir yönü, işlem komut dosyalarının kullanılmasına izin verilmesidir . Büyük uygulamalarda çalışırken, sonunda bir T-SQL veya PL / SQL prosedürüyle bir şeyi gerçekleştirmenin, etki alanı modeliyle karıştırmaktan çok daha kolay olduğu durumlarla karşılaşmanız çok olasıdır. Bu tamam ve bir servise ait.

    Bu, hizmetlerin katmanlı mimari tanımından radikal bir ayrılmadır. Hizmet katmanı, etki alanı nesnelerini içine alır; Bir DDD hizmeti ne olursa olsun kapsüller değil etki alanı nesneleri ve duyu olmak yapmaz.

  5. Servis Odaklı Mimari'de, hizmetin bir işletme yeteneği için teknik otorite olduğu kabul edilir. Bu , işletme verilerinin belirli bir alt kümesinin münhasır sahibi olduğu anlamına gelir ve bu verilere dokunmak için hiçbir şeye izin verilmez - sadece okumak için bile değil.

    Gereklilik gereği, hizmetler aslında bir SOA'da baştan sona bir öneridir. Yani, bir hizmet bir yığının tamamı kadar belirli bir bileşen değildir ve uygulamanızın (veya tüm işinizin), mesajlaşma ve UI katmanları dışında kesişmeden yan yana çalışan bu hizmetler kümesidir. Her hizmetin kendi verileri, kendi iş kuralları ve kendi kullanıcı arayüzü vardır. İşle uyumlu olmaları gerektiği için birbirleriyle birlikte düzenlemeye gerek duymazlar - ve işin kendisi gibi, her bir hizmetin kendi sorumlulukları vardır ve diğerlerinden daha fazla veya daha az bağımsız olarak çalışır.

    Bu yüzden, SOA tanımıyla, hizmetin herhangi bir yerindeki her iş mantığı parçası hizmetin içinde yer alır, ancak yine de tüm sistem böyledir . Bir SOA’daki servislerin bileşenleri olabilir ve uç noktalarına sahip olabilirler , ancak orijinal "S" nin ne anlama geldiği ile çakıştığı için herhangi bir kod parçasını hizmet olarak adlandırmak oldukça tehlikelidir .

    SOA genellikle mesajlaşma konusunda oldukça istekli olduğundan, daha önce bir hizmette paketlemiş olabileceğiniz işlemler genellikle işleyicilerde saklanır , ancak çokluk farklıdır. Her işleyici, bir ileti türünü, bir işlemi işler . Tek Sorumluluk İlkesi'nin kesin bir yorumudur , ancak her olası işlem kendi sınıfında olduğu için büyük bir sürdürülebilirlik sağlar. Yani merkezi bir iş mantığına gerçekten ihtiyacınız yok , çünkü komutlar teknik olanlardan ziyade ticari operasyonları temsil ediyor.

Sonuçta, seçtiğiniz herhangi bir mimaride, iş mantığının çoğuna sahip bir bileşen veya katman olacak. Ne de olsa, iş mantığı her yere dağılmışsa, spagetti kodunuz vardır. Ancak, bu bileşene bir hizmet dediğiniz veya bu işlemin sayısı veya büyüklüğü gibi şeyler açısından nasıl tasarlandığı, mimari hedeflerinize bağlıdır.

Doğru ya da yanlış cevap yok, sadece sizin durumunuz için geçerli olan.


12
Çok ayrıntılı cevap için teşekkür ederim, aklıma gelen her şeyi açıklığa kavuşturdunuz. Diğer cevaplar da mükemmel kalitede olmasına rağmen, bu cevabın hepsinin başında olduğuna inanıyorum. Buraya diğer cevaplar için ekleyeceğim: mükemmel kalite ve bilgi, fakat ne yazık ki sadece size bir öneri verebileceğim.
Jeroen Vannevel 14:13

2
Geleneksel katmanlı bir mimaride, hizmetin iş mantığı katmanıyla eş anlamlı olduğu gerçeğine katılmıyorum.
CodeART

1
@CodeART: 3 katmanlı bir mimaride. Ben var aynı zamanda bir "hizmet" katmanı olarak adlandırılan bazen, ama dürüst olmak gerekirse, sadece yerleri Ben hiç bu uygulamaya gördüğüm sunum ve iş katmanları arasında bir "uygulama katmanı", olduğu yerde 4-katmanlı mimariler görülen başarıyla büyük uzanmakta SAP ya da Oracle'ın beğenisine göre sizin için tüm işinizi sizin için sınırsızca düzenleyebilen ürünler, ve burada gerçekten bahsetmeye değeceğini düşünmedim. İsterseniz bir açıklama ekleyebilirim.
Aaron

1
Ancak, 100'den fazla hat denetleyicisi kullanıyorsak (örneğin denetleyici mesajı kabul ediyorsa - JSON nesnesini seri hale getirin, doğrulama yapın, iş kuralları uygulayın, db'ye kaydedin, sonuç nesnesini döndürün) ve bazı mantığı servis yönteminin adı verilen bir tanesine taşıyın. Bu, her bir parçasını ayrı ayrı acısız test etmemize yardımcı oldu mu?
artjom

2
@Aaronaught ORM üzerinden db ile eşlenen etki alanı nesnelerimiz varsa ve bunlarda iş mantığı yoksa bu anemik etki alanı modeli midir?
artjom

40

Başlığın gelince , sorunun mantıklı olduğunu sanmıyorum. MVC Modeli veri ve iş mantığından oluşur. Mantığın Hizmette olması gerektiğini ve Modelin “Yolcu arabada değil koltukta oturması gerektiğini” söylememesi gerektiğini söylemelidir.

Sonra tekrar, "Model" terimi aşırı yüklenmiş bir terimdir. Belki de MVC Modelini kastetmediniz ama Veri Aktarım Nesnesi (DTO) anlamında bir model demek istediniz. AKA bir varlıktır. Martin Fowler’ın bahsettiği şey bu.

Gördüğüm gibi, Martin Fowler ideal bir dünyadaki şeylerden bahsediyor. Hazırda Beklet ve JPA'nın gerçek dünyasında (Java ülkesinde) DTO'lar süper sızdıran bir soyutlamadır. İş mantığımı varlığımın içine koymak isterim. İşleri daha temiz yapar. Sorun, bu varlıkların anlaşılması çok zor olan ve çabalarınızı sürekli önleyen, yönetilen / önbelleğe alınmış bir durumda bulunabilmesidir. Fikrimi özetlemek gerekirse: Martin Fowler doğru yolu öneriyor, ancak ORM'ler bunu yapmanıza engel oluyor.

Bob Martin'in daha gerçekçi bir önerisi olduğunu düşünüyorum ve bu videoda ücretsiz olmayan bir video verdi . DTO'larınızı mantıktan uzak tutmaktan bahsediyor. Sadece verileri tutarlar ve daha çok nesne yönelimli ve DTO'ları doğrudan kullanmayan başka bir katmana aktarırlar. Bu, sızan soyutlamanın sizi ısırmasını önler. DTO'lar ve DTO'ların kendileri OO değildir. Fakat bu katmandan çıktıktan sonra, Martin Fowler'ın savunduğu gibi OO olursunuz.

Bu ayrılmanın yararı, kalıcılık katmanını soyutlamasıdır. JPA'dan JDBC'ye geçebilir (veya tersi) ve iş mantığının hiçbiri değişmek zorunda kalmayacaktı. Sadece DTO'lara bağlı, bu DTO'ların nasıl doldurulacağı önemli değil.

İçin hafifçe konular değiştirmek, SQL veritabanları nesne yönelimli değildir gerçeğini dikkate almak gerekir. Ancak ORM'lerin genellikle tablo başına bir nesne olan bir varlık vardır. Bu yüzden en başından beri zaten bir savaşı kaybettiniz. Tecrübelerime göre, hiçbir zaman Nesne yönelimli bir şekilde istediğiniz şekilde Varlığı tam olarak temsil edemezsiniz.

" Bir hizmet" gelince , Bob Martin adlı bir sınıfa karşı olacaktır FooBarService. Bu nesne yönelimli değil. Bir servis ne yapar? İle ilgili bir şeyFooBars . Etiketlenmiş olabilir FooBarUtils. Bir servis katmanını savunacağını düşünüyorum (daha iyi bir isim iş mantığı katmanı olacaktır), ancak bu katmandaki her sınıfın anlamlı bir adı olur.


2
ORM'lerdeki amacınıza katılıyorum; Varlığınızı doğrudan db'ye eşlediğiniz yalanı yayar; gerçekte bir varlık birkaç tabloda saklanabilir.
Andy,

@Daniel Kaplan Bob Martin videosu için güncellenmiş bağlantının ne olduğunu biliyor musunuz?
Brian Morearty

25

Şu an greenfield projesi üzerinde çalışıyorum ve dün birkaç mimari karar vermek zorunda kaldık. Yeterince komik, birkaç 'Kurumsal Uygulama Mimarisi Örüntüsü' bölümünü tekrar gözden geçirmek zorunda kaldım.

İşte biz geldik:

  • Veri katmanı Veritabanını sorgular ve günceller. Kat, enjekte edilebilir depolar vasıtasıyla açığa çıkar.
  • Etki alanı katmanı. İş mantığının yaşadığı yer burasıdır. Bu katman, enjekte edilebilir depolardan yararlanır ve iş mantığının çoğundan sorumludur. Bu, kapsamlı bir şekilde test edeceğimiz uygulamanın özü.
  • Servis katmanı Bu katman etki alanı katmanıyla konuşur ve müşteri isteklerine hizmet eder. Bizim durumumuzda servis katmanı oldukça basittir - talepleri etki alanı katmanına iletir, güvenliği ele alır ve diğer birkaç çapraz kesme kaygısı vardır. Bu, MVC uygulamasındaki bir denetleyiciden çok farklı değildir - denetleyiciler küçük ve basittir.
  • Müşteri katmanı. SOAP üzerinden servis katmanıyla konuşur.

Aşağıdakilerle sonuçlanır:

Müşteri -> Servis -> Domain -> Data

Müşteri, hizmet veya veri katmanını makul miktarda işle değiştirebiliriz. Etki alanı mantığınız hizmette yaşıyorsa ve hizmet katmanınızı değiştirmek veya hatta kaldırmak istediğinize karar verdiyseniz, tüm işletme mantığını başka bir yere taşımak zorunda kalırsınız. Böyle bir gereksinim nadirdir, ancak olabilir.

Bütün bunları söyledikten sonra, bunun Martin Fowler’ın ne demek istediğine oldukça yakın olduğunu düşünüyorum.

Bu hizmetler etki alanı modelinin tepesinde yaşar ve etki alanı modelini veriler için kullanır.

Aşağıdaki diyagram bunu oldukça iyi göstermektedir:

http://martinfowler.com/eaaCatalog/serviceLayer.html

http://martinfowler.com/eaaCatalog/ServiceLayerSketch.gif


2
Dün sadece SOAP'a karar verdin mi? Bu bir gereklilik mi yoksa daha iyi bir fikrin yok mu?
JensG

1
REST gereksinimlerimiz için kesmeyecek. SABUN veya REST, bu cevapta herhangi bir fark yaratmaz. Anladığım kadarıyla hizmet, etki alanı mantığına açılan bir kapıdır.
CodeART

Ağ Geçidine kesinlikle katılıyorum. SOAP (standartlaştırılmış) bir yazılım yazılımı olduğundan sormak zorunda kaldım. Ve evet, soru / cevap üzerinde de bir etkisi yoktur.
JensG

6
Kendine bir iyilik yap ve servis katmanını öldür. Kullanıcı arayüzünüz etki alanınızı doğrudan kullanmalıdır. Bunu daha önce görmüştüm ve alanınız her zaman zengin modeller değil, bir sürü anemik dtos haline geldi.
Andy

Bu slaytlar, servis katmanına ilişkin açıklamayı ve bu grafiğin oldukça düzenli olmasını sağlar: slideshare.net/ShwetaGhate2/…
Marc Juchli

9

Bu kullanım durumuna gerçekten bağlı olan şeylerden biri. Bir hizmet katmanının genel amacı, iş mantığını birlikte birleştirmek. Bu, bazı kontrol cihazlarının aynı UserService.MakeHimPay () yöntemini, ödemenin nasıl yapıldığına bakmadan çağırabildiği anlamına gelir. Hizmette devam eden bir nesne özelliğini değiştirmek kadar basit olabilir veya diğer hizmetlerle ilgili karmaşık bir mantık yapıyor olabilir (örneğin, üçüncü taraf hizmetlere çağrı yapmak, doğrulama mantığını aramak veya hatta yalnızca bir şeyi veritabanına kaydetmek). )

Bu, ALL mantığını etki alanı nesnelerinden çıkarmanız gerektiği anlamına gelmez. Bazen etki alanı nesnesindeki bir yöntemin kendi üzerinde bazı hesaplamaları yapması daha mantıklı olur. Son örneğinizde servis, depo / etki alanı nesnesi üzerinde fazlalık bir katmandır. Gereksinim değişikliklerine karşı güzel bir tampon sağlar, ancak gerçekten gerekli değildir. Bir hizmete ihtiyacınız olduğunu düşünüyorsanız, bunun yerine etki alanı nesnesi yerine basit "Y nesnesinde X özelliğini değiştirme" mantığını gerçekleştirmeyi deneyin. Etki alanı sınıfları üzerindeki mantık, tüm alanları alıcılar / ayarlayıcılar aracılığıyla göstermek yerine "bu değeri alanlardan hesapla" alanına girme eğilimindedir.


2
İş mantığına sahip bir hizmet katmanı lehine tavrınız çok mantıklı geliyor, ancak bu yine de bazı sorular bırakıyor. Benim görevimde, hizmet katmanından herhangi bir iş mantığının boşluğunun cephesi olarak bahseden saygın kaynaklardan alıntı yaptım. Bu, cevabınızla doğrudan çelişmektedir, bu farkı belki açıklığa kavuşturabilir misiniz?
Jeroen Vannevel 11:13

5
İş mantığının TÜRÜ'ne ve kullanılan dil gibi diğer faktörlere bağlı olduğunu düşünüyorum. Bazı iş mantığı etki alanı nesnelerine pek uymuyor. Bir örnek, sonuçları veritabanından geri çektikten sonra filtrelemek / sıralamaktır. İş mantığı IS, ancak etki alanı nesnesine bir şey ifade etmiyor. Hizmetlerin basit mantık için veya sonuçları dönüştürmek için en iyi şekilde kullanıldığını ve etki alanındaki mantığın, veri kaydetme veya nesneden veri hesaplama ile uğraşırken en faydalı olduğunu biliyorum.
İtfaiyeci

8

Programcıların neden etki alanı mantığını etki alanı nesnelerine koymaktan kaçındıklarını göstermenin en kolay yolu, genellikle "doğrulama mantığını nereye koyacağım?" Durumuyla karşı karşıya olmalarıdır. Örneğin bu etki alanı nesnesini alın:

public class MyEntity
{
    private int someProperty = 0;

    public int SomeProperty
    {
        get { return this.someProperty; }
        set
        {
            if(value < 0) throw new ArgumentOutOfRangeException("value");
            this.someProperty = value;
        }
    }
}

Dolayısıyla, ayarlayıcıda bazı temel doğrulama mantığımız var (negatif olamaz). Sorun şu ki, bu mantığı tekrar kullanamazsınız. Bir yerde , aslında bir etki alanı nesnesine değişiklik yapmadan önce doğrulama yapması gereken bir ekran veya ViewModel ya da bir Denetleyici var , çünkü kullanıcıya, önce ya da ne zaman yapamayacaklarını yapamayacaklarını bildirmesi gerekiyor. ve neden . Belirleyiciyi aradığınızda istisna olup olmadığını test etmek çirkin bir kesektir, çünkü işlemi başlatmadan önce tüm doğrulamaları yapmanız gerekirdi.

Bu yüzden insanlar doğrulama mantığını bir tür hizmet gibi hareket ettirir MyEntityValidator. Daha sonra varlık ve çağıran mantık hem doğrulama servisine referans alabilir hem de yeniden kullanabilir.

Bunu yapmazsanız ve doğrulama mantığını yeniden kullanmak istiyorsanız, varlık sınıfının statik yöntemlerine yerleştirmek zorunda kalırsınız:

public class MyEntity
{
    private int someProperty = 0;

    public int SomeProperty
    {
        get { return this.someProperty; }
        set
        {
            string message;
            if(!TryValidateSomeProperty(value, out message)) 
            {
                throw new ArgumentOutOfRangeException("value", message);
            }
            this.someProperty = value;
        }
    }

    public static bool TryValidateSomeProperty(int value, out string message)
    {
        if(value < 0)
        {
            message = "Some Property cannot be negative.";
            return false;
        }
        message = string.Empty;
        return true;
    }
}

Bu, etki alanı modelinizi daha az "anemik" yapar ve özelliğin yanında doğrulama mantığını korur, bu harika, ancak kimsenin statik yöntemleri gerçekten sevdiğini sanmıyorum.


1
Peki, sizce geçerliliği için en iyi çözüm nedir?
Flashrunner

3
@Flashrunner - benim doğrulama mantığım kesinlikle işletme mantığı katmanında, ancak bazı durumlarda varlık ve veritabanı katmanlarında da çoğaltılıyor. İş katmanı, kullanıcıyı vb.
Scott Whitlock

6

Martin Fowler'in Anemik Etki Alanı Modeli makalesini okuduysanız cevabın açık olduğunu düşünüyorum .

Etki alanı olan iş mantığını etki alanı modelinden kaldırmak, temel olarak nesne yönelimli tasarımı bozuyor.

En temel nesne yönelimli kavramı gözden geçirelim: Bir nesne veriyi ve işlemleri kapsıyor. Örneğin, bir hesabı kapatmak, bir hesap nesnesinin kendisi üzerinde gerçekleştirmesi gereken bir işlemdir; bu nedenle, bir servis katmanına sahip olmak bu işlemi bir nesne yönelimli çözüm değildir. Prosedüreldir ve Martin Fowler'ın anemik bir etki alanı modeli hakkında konuşurken bahsettiği şeydir.

Bir servis katmanınız varsa, hesap nesnesinin kendisini kapatmasını sağlamak yerine hesabı kapatın, gerçek bir hesap nesnesiniz yoktur. Hesabınız "nesne" sadece bir veri yapısıdır. Martin Fowler'ın önerdiği gibi, sonuçta elde edeceğiniz şey alıcı ve ayarlayıcılardan oluşan bir demet çanta.


1
Düzenlenen. Aslında bu oldukça yararlı bir açıklama buldum ve bunun indirgeme hak etmediğini sanmıyorum.
BadHorsie

1
Zengin modellerin aşağı tarafında büyük oranda denetlenen bir tane var. Ve geliştiriciler, modele biraz ilgili bir şey çekiyorlar. Açık / kapalı durumu hesabın bir özelliği midir? Peki ya sahibi? Ya banka? Hepsine hesap tarafından mı başvurulmalı? Bir hesabı bir bankayla konuşarak mı yoksa doğrudan hesap yoluyla mı kapatırım? Anemik modellerde, bu bağlantılar modellerin doğal bir parçası değildir, ancak diğer modellerde bu modellerle çalışırken (onlara servis veya yönetici olarak adlandırılır) oluşturulur.
Hubert Grzeskowiak

4

İş mantığınızı hizmet katmanında nasıl uygularsınız? Bir kullanıcıdan ödeme yaparken, sadece bir mülkün değerinden düşülmekle kalmaz, bir ödeme yaratırsınız.

Ödeme yapma yönteminizin bir ödeme kaydı oluşturması, bu kullanıcının borcuna eklemesi ve tüm bunları depolarınızda sürdürmesi gerekir. Bunu bir servis yönteminde yapmak inanılmaz derecede basittir ve tüm işlemi bir işleme sarabilirsiniz. Aynı şeyi birleştirilmiş etki alanı modelinde yapmak çok daha problemlidir.


2

Tl; dr sürümü:
Tecrübelerim ve görüşlerim, iş mantığı olan herhangi bir nesnenin etki alanı modelinin bir parçası olması gerektiğini söylüyor. Veri modeli muhtemelen herhangi bir mantığa sahip olmamalıdır. Servisler muhtemelen bu ikisini birbirine bağlamalı ve çapraz kaygılı durumlarla (veritabanları, kayıt vb.) İlgilenmelidir. Ancak, kabul edilen cevap en pratik olanıdır.

Başkaları tarafından ima edilen uzun versiyon, "model" kelimesi üzerinde bir denklem bulunması. Gönderi, veri modeli ile etki alanı modeli arasında aynımış gibi değişir, bu çok yaygın bir hatadır. "Hizmet" kelimesi üzerinde de hafif bir denklem olabilir.

Pratik açıdan, herhangi bir etki alanı nesnesinde değişiklik yapan bir hizmetiniz olmamalıdır; Bunun nedeni, hizmetinizin, bu özelliğin değerini değiştirmek için nesnenizdeki her özellik için bazı yöntemlere sahip olacağıdır. Bu bir problem çünkü o zaman, eğer nesneniz için bir arayüze sahipseniz (veya olmasa bile), servis artık Açık-Kapalı Prensibini izlemiyor; bunun yerine, modelinize ne zaman daha fazla veri eklerseniz (etki alanı vs verilerinden bağımsız olarak), hizmetinize daha fazla işlev eklemek zorunda kalırsınız. Etrafında belirli yollar var, ama bu "kurumsal" uygulamaların başarısız olduğunu görmemdeki en yaygın neden, özellikle de "kuruluşların" sistemdeki her nesne için bir arayüze sahip olduğunu "düşündüğü zaman. Arayüze yeni yöntemler eklemeyi hayal edebiliyor musunuz, sonra iki veya üç farklı uygulamaya (uygulama içi uygulama, sahte uygulama ve hata ayıklama, hafıza içi uygulama?), sadece modelinizdeki tek bir özellik için? Bana korkunç bir fikir gibi geliyor.

Burada girmeyeceğim daha uzun bir sorun var, ama esas olan şudur: Hardcore nesne yönelimli programlama, ilgili nesnenin dışında kimsenin nesnenin içindeki bir özelliğin değerini değiştirememesi gerektiğini, hatta " bakınız "nesnenin içindeki özelliğin değeri. Bu, salt okunur veriler yapılarak hafifletilebilir. Birçok insanın verileri salt okunur olarak bile kullandığı ve bu verilerin türünü değiştirmeniz gerektiği gibi sorunlar ile karşılaşabilirsiniz. Tüm tüketicilerin buna uyum sağlamak için değişmesi gerekebilir. Bu yüzden, API'leri herhangi biri ve herkes tarafından tüketilmesini sağladığınızda, herkese açık veya korunan mülkler / veriler kullanmamanız tavsiye edilir; Sonuçta OOP'un icat edilmesinin nedeni budur.

Bence buradaki cevapların çoğunluğu, kabul edilenler dışında, konuyu bulanıklaştırıyor. Kabul edilen olarak işaretlenen kişi iyi, ama yine de 4 numaralı merminin genel olarak gideceği yolun cevaplanması ve aynı fikirdeyim olması gerektiğini hissettim.

DDD'de, hizmetler, herhangi bir toplam köke uygun olmayan bir işlem yaptığınız durum için özel olarak tasarlanmıştır. Burada dikkatli olmalısınız, çünkü genellikle bir hizmete duyulan ihtiyaç doğru kökleri kullanmadığınız anlamına gelebilir. Ama yaptığını varsayarsak, hizmet birden fazla kökten koordine etmek için kullanılır, ya da bazen etki alanı modelini içermeyen endişeleri gidermek için kullanılır ...


1

Cevap, kullanım durumuna bağlı olmasıdır. Ancak çoğu genel senaryoda, hizmet katmanında yer alan iş mantığına bağlı kalacağım. Sağladığınız örnek gerçekten basit. Ancak bir kez ayrıştırılmış sistemleri veya hizmetleri düşünmeye ve üzerine işlemsel davranış eklemeye başladığınızda, bunun gerçekten servis katmanının bir parçası olmasını istersiniz.

Hizmet katmanı için herhangi bir iş mantığından yoksun olarak alıntı yaptığınız kaynaklar, iş katmanı olan başka bir katmanı tanıtır. Birçok senaryoda, servis katmanı ve iş katmanı bir olarak sıkıştırılır. Bu gerçekten sisteminizi nasıl tasarlamak istediğinize bağlı. İşi üç kat halinde halledebilir ve süslemeye devam edip gürültü ekleyebilirsiniz.

İdeal olarak yapabileceğiniz şey, durumlarını korumak için etki alanı modelleri üzerinde çalışmak üzere iş mantığını içeren model hizmetlerdir . Servisleri mümkün olduğunca ayırmaya çalışmalısınız.


0

MVC Modelinde iş mantığı olarak tanımlanmaktadır. MVC kullanmadığı sürece başka bir yerde olması gerektiğini iddia etmek yanlış. Servis katmanlarını bir modül sistemine benzer şekilde görüyorum. İlgili bir işlevsellik kümesini güzel bir pakette birleştirmenizi sağlar. Bu servis katmanının içindekiler, sizinle aynı işi yapan bir modele sahip olacak.

Model, uygulama verileri, iş kuralları, mantık ve işlevlerden oluşur. http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller


0

Servis katmanı kavramı DDD perspektifinden görülebilir. Aaronaught cevabından bahsetti, ben sadece biraz detaylandırdım.

Buradaki ortak yaklaşım, bir tür istemciye özgü bir denetleyiciye sahip olmaktır. Diyelim ki, bir web tarayıcısı olabilir, başka bir uygulama olabilir, işlevsel bir test olabilir. İstek ve yanıt biçimleri değişebilir. Bu yüzden uygulama hizmetini bir Altıgen mimari kullanmak için bir araç olarak kullanıyorum . Somut bir talebe özgü altyapı sınıfları enjekte ediyorum. Örneğin, web tarayıcısı istekleri sunan denetleyicim şöyle görünebilir:

class WebBroserController
{
    public function purchaseOrder()
    {
        $data = $_POST;

        $responseData =
            new PurchaseOrderApplicationService(
                new PayPalClient(),
                new OrderRepository()
            )
        ;

        $response = new HtmlView($responseData);
    }
}

İşlevsel bir test yazıyorsam sahte bir ödeme istemcisi kullanmak istiyorum ve muhtemelen bir html yanıtına ihtiyacım olmaz. Böylece kontrol cihazım şöyle görünebilir:

class FunctionalTestController
{
    public function purchaseOrder()
    {
        $data = $_POST;

        $responseData =
            new PurchaseOrderApplicationService(
                new FakePayPalClient(),
                new OrderRepository(),
                $data
            )
        ;

        return new JsonData($responseData);
    }
}

Bu yüzden uygulama hizmeti, iş mantığını yürütmek için kurduğum bir ortam. Model sınıflarının isimlendirildiği yer - yani bir altyapı uygulamasının.

Yani, sorularınızı bu açıdan cevaplamak:

Kontrol cihazından mantığı çıkarmak ve bunun yerine bir hizmetin içine koymak için bir araç mı?

Hayır.

Denetleyici ile etki alanı arasında bir sözleşme mi yapılması gerekiyor?

Biri öyle diyebilir.

Etki alanı ve hizmet katmanı arasında bir katman olmalı mı?

Hayır!


Her türlü hizmetin kullanımını tamamen inkar etmesine rağmen, radikal olarak farklı bir yaklaşım var. Örneğin, Nesne Düşünme adlı kitabında David West , herhangi bir nesnenin işini yapmak için gerekli tüm kaynaklara sahip olması gerektiğini iddia eder. Bu yaklaşım, örneğin, herhangi bir ORM'nin atılmasına neden olur .


-2

Kayıt için.

SRP:

  1. Model = Data, ayarlayıcı ve alıcılar için burada.
  2. Mantık / Hizmetler = kararlar burada.
  3. Depo / DAO = burada kalıcı olarak bilgiyi saklıyor veya alıyoruz.

Bu durumda, sonraki adımları yapmanız tamamdır:

Borç bir hesaplama gerektirmiyorsa:

userObject.Debt = 9999;

Bununla birlikte, biraz hesaplama gerektiriyorsa:

userObject.Debt= UserService.CalculateDebt(userObject)

veya ayrıca

UserService.UpdateDebt(userObject)

Ama aynı zamanda, hesaplama kalıcılık katmanında yapılırsa, böyle bir mağaza prosedürü

UserRepository.UpdateDebt(userObject)

Bu durumda, kullanıcıyı veri tabanından almak ve borcunu güncellemek istiyoruz, daha sonra birkaç adımda yapmalıyız (aslında iki) ve hizmetin işlevine sarılması / kapsüllenmesi gerekmiyor.

User userObject=UserRepository.GetUserByName(somename);
UserService.UpdateDebt(userObject)

Ve eğer depolamak istiyorsa, üçüncü bir adım daha ekleyebiliriz.

User userObject=UserRepository.GetUserByName(somename);
UserService.UpdateDebt(userObject)
UserRepository.Save(userobject);

Önerilen çözüm hakkında

a) Son geliştiriciyi, bir işlev içinde saklamak yerine bir çift yazmak için bırakmaktan korkmamalıyız.

b) Arayüz ile ilgili olarak, bazı geliştiriciler ara yüzünü severler ve gayet iyidirler, ancak bazı durumlarda ihtiyaç duymazlar.

c) Bir hizmetin amacı, özniteliksiz bir tane oluşturmaktır, çünkü temelde Paylaşılan / Statik işlevleri kullanabiliriz. Ünite testi de kolaydır.


sorusu soruyu nasıl cevaplıyor: “İş mantığı bir modelde değil bir hizmette olmalı” mı?
gnat

3
Ne tür bir cümle "We shouldn't be afraid to left the end-developer to write a couple of instead of encapsulate it in a function."? Sadece Lewis Black'i alıntılayabilirim" eğer atım olmasaydı o yıl üniversitede
geçiremezdim
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.