Article View: alt.pl.comp.lang.csharp
Article #22359Re: ServiceLayer - rozważanie, best practice
From: MarlonB
Date: Wed, 22 Feb 2012 17:02
Date: Wed, 22 Feb 2012 17:02
173 lines
7919 bytes
7919 bytes
Użytkownik "Łukasz 'Maly' Ostrowski" <maly@USUNnemo.pl> napisał w wiadomości
news:12b400yh5lb3r.mg3vhkurgug9.dlg@40tude.net...
> On Wed, 22 Feb 2012 14:08:45 +0100, MarlonB wrote:
>> Jestem w trakcie pisania warstwy ServiceLayer w moim projekcie.
>> Pośredniczy ona między wartwą logiki biznesowej (businessLayer) a wartwą
>> prezentacji, czyli jest ona fabryką różnych api, tak jak ja to rozumiem.
>> Co
>> mnie zastanawiało: <snip>
>
> Czyli service layer to po prostu kontener IoC/service-locator? ;-)
O! to, właśnie to! :)
>> Warstwy businessLayer, dataAccessLayer, serviceLayer dzielą się w spólnie
>> głupimi modelami danych - POCO, które odzwierciedlają jeden do jeden, to
>> co
>> jest w bazie danych. To znaczy, klasa User i jej właściwości
>> odzwierciedlają
>> tabelę User i jej kolumny w bazie danych.
>
> Mhm, Domain Model aka DDD:
> http://en.wikipedia.org/wiki/Domain-driven_design
Dokładnie :)
>> Wprowadzenie serviceLayer ma m.in. za zadanie zerwanie zależności po
>> między
>> tymi typami danych, które są dość skomplikowane a wartwą prezentacji.
>> Warstwa serviceLayer może mieć kilka api i każde api musi być dość jasne,
>> przejrzyste, dać się łatwo modyfikować i testować.
>
> To się z reguły robi poprzez dependency injection, tzn. pytasz
> kontener IoC o instancję kontrolera(mvc), a ten kontener wstrzeliwuje
> zależności bez aktywnego odpytywania explicite kontenera o dane
> serwisy.
>
> http://en.wikipedia.org/wiki/Dependency_injection#Automatically_injected_dependency
> http://en.wikipedia.org/wiki/Service_locator_pattern
> http://en.wikipedia.org/wiki/Inversion_of_control
>
>> interface AdminPanelApi
>> {
>> IDictionary<string, object> GetUsers(GetUsersRequired,
>> GetUsersOptional);
>> IDictionary<string, object> GetSomeObjects(GetSomeObjectsRequired,
>> GetSomeObjectsOptional);
>> }
>
> To wygląda bardziej jak Repository pattern w tym wydaniu. Serwisy
> z reguły "coś robią", wykonują procesy biznesowe etc. Z kolei
> repozytoria(DAO) są właśnie do GetUsers(string filter) ;-).
>
> /Podział ideologiczno-nomenklaturyowy jest czysto syntetyczny/
>
>> Co na tym widać:
>> - Wszystkie metody zwracają dokładnie i zawsze to samo, czyli drzewo
>> IDictionary<string, object>, gdzie object może być typem prostym lub
>> następnym IDictionary<string, object>.
>
> Czemu Dictionary<>? Dlaczego nie Set/List lub Customowy View Model?
> To powinno zależeć od realnie wykonywanej akcji i oczekiwanego
> rezultatu. Późniejsze kombinacje
>
> var res = GetUsers(...);
> var registeredUsers = res["Registered"] as IList<User>;
>
> to bezpośrednie proszenie o kłopoty. ;-). Nie działa do tego
> IntelliSense...
Wydawało mi się to dobrym pomysłem... ale właśnie przestało.
>> - Każda metoda na wejściu ma dedykowane 2 obiekty: <nazwa metody>Required
>> i
>> <nazwa metody>Optional. Po co? W pierwszym są dane, które muszą być, aby
>> wywołanie miało sens. Drugie są opcjonalne, gdzie każda właściwość może
>> być
>> nullem. Jeśli dojdzie jakaś własność, to łatwo to będzie zmodyfikować,
>> udokumentować, zwalidować i serializować. Typy wejściowe nie są
>> drzewiaste:
>> mogą zawierać proste typy i proste kolekcje.
>
> Jeżeli do XxxRequired dojdzie property, to build nie pójdzie w krzaki,
> i.e. przejdzie do implementacji "API" niezainicjowane prop, moim zdaniem
> to źle. Gdy są explicite argumenty dodane, wciskasz Build i od razu masz
> potencjalne miejsca które "zepsułeś", w postaci listy błędów kompilacji.
> I to compile-time, nie runtime, nie test-run-time.
>
> Reasumując, moim zdaniem nie warto nadbudowywać na argumenty, warstwę
> abstrakcji w postaci kontenera argumentów, tylko użyć tego co jest:
>
> IEnumerable<Users> GetSomeUsers(bool includeDeleted,
> Expression<Func<User, bool>> filterBy = null);
>
>> - Dodanie każdej metody, to: wpisanie nagłówka do interfacu,
>> zaimplementowanie metody, napisanie 2 klas i udokumentowanie jakie klucze
>> będą zwracane.
>
> Najlepsze jest taki kod który się dokumentuje sam. :).
>
>> Zalety:
>> - łatwo modyfikowalne,
>> - łatwo zrozumiałe,
>> - IDictionary<string, object> można łatwo przerobić na Jsona, xmla lub
>> cokolwiek w przypadku napisania web servicu korzystającego z
>> serviceLayer,
>
> Takowoż IEnumerable<>, View Modele lub cokolwiek innego.
>
>> - w zależności od poziomu dostępu wywołującego metodę i danych
>> wejściowych
>> IDictionary<string, object> może zawierać czasem mniej lub więcej
>> kluczy -
>> to musi być w dokumentacji co od czego zależy.
>
> Nie nazwałbym wszystkich czterech zalet, zaletami. To raczej rzeźbienie na
> void* albo $php. IMO ofc ;-).
hmm... no może w sumie racja...
>> Wady:
>> - Typ IDictionary<string, object> nie jest jasno opisany przez kod -
>> potrzeba pracy z dokumentacją.
>
> - Nie funkcjonuje IntelliSense na Dictionary<string,object>.
>
>> I teraz główne pytania:
>> - Czy to ma sens?
>
> Pi razy drzwi. Idea wydaje się być ok, ale wykonanie rodem z PHP.
> Nic osobistego ;-).
>
>> - Czy ktoś już pisał coś podobnego i natrafił na lepsze rozwiązanie?
>
> Cośtam ktoś pisał i maintainuje aktywnie. "Większa" aplikacja
> na ASP.NET MVC + SharpArch, generalnie IoC, DI i główne
> "funkcjonalności" rozdzieone pomiędzy ICośtamCośtamService i
> ICośtamCośtamRepository. Nie narzekam.
>
Mógłbyś napisać coś więcej w jaki sposób / ze względu na co rozdzielasz
funkcjonalność na Service i Repository?
Repository wykorzystuje między warsta dataAccess a businessLogic. Jak
rozumiem repository ma tylko operacje CRUD? Dlatego nie bardzo widzę
zastosowania repository w serviceLayer, jako że potrzebuje konkretnych
akcji. Być może coś źle rozumiem...
Ciekawi mnie bardzo opinia kogoś już doświadczonego, czyli co powinno
znaleźć się w serviceLayer? Jakie patterny? W serviceLayer korzystam z unity
do pozyskania konkretnej implementacji danego serwisu.
Może jakieś ciekawe artykuły jak dobrze zaprojektować to wszystko?
>> - Gdzie znajde jakieś informacje best practice jak się piszę porządne
>> api,
>> które może się rozrastać bez końca?
>
> To raczej advocacy, ja śledzę m.in. blog Ayende, czasami się nie zgadzam,
> ale z reguły ma rację co do architektury i okołotematycznych rantów.
>
> (Wiekszość akapitów można przyozdobić "IMO", żadne objawienia,
> dlatego żadnych linków ani sławnych nazwisk nie podałem, nawet
> jeżeli są to ideologiczne zaczerpnięcia z Fowlera czy kogokolwiek
> - jestem zbyt leniwy żeby je znaleźć - a.k.a. Wikipediowe
> "missing references" ;-)).
>
Dzięki za odpowiedź.
Pozdro.
M.
Technical Details
Message-ID:
<ji373f$i9m$1@mx1.internetia.pl>Path:
mbox-mo1.pugleaf.net!news-archive.icm.edu.pl!news.icm.edu.pl!opal.futuro.pl!news.internetia.pl!not-for-mailReferences:
<ji2pdf$ht6$1@mx1.internetia.pl> <12b400yh5lb3r.mg3vhkurgug9.dlg@40tude.net>Show Full Headers
Complete Article Headers
Path: mbox-mo1.pugleaf.net!news-archive.icm.edu.pl!news.icm.edu.pl!opal.futuro.pl!news.internetia.pl!not-for-mail From: "MarlonB" <zyrafa@wieloryb.kon> Newsgroups: alt.pl.comp.lang.csharp Subject: Re: ServiceLayer - rozważanie, best practice Date: Wed, 22 Feb 2012 18:02:20 +0100 Organization: Netia S.A. Lines: 174 Message-ID: <ji373f$i9m$1@mx1.internetia.pl> References: <ji2pdf$ht6$1@mx1.internetia.pl> <12b400yh5lb3r.mg3vhkurgug9.dlg@40tude.net> NNTP-Posting-Host: 87-205-19-179.ip.netia.com.pl Mime-Version: 1.0 X-Trace: mx1.internetia.pl 1329930159 18742 87.205.19.179 (22 Feb 2012 17:02:39 GMT) X-Complaints-To: abuse@inetia.pl NNTP-Posting-Date: Wed, 22 Feb 2012 17:02:39 +0000 (UTC) X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.6157 X-Tech-Contact: usenet@internetia.pl X-Newsreader: Microsoft Outlook Express 6.00.2900.5931 X-Priority: 3 X-Server-Info: http://www.internetia.pl/ X-MSMail-Priority: Normal Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit