Article View: alt.pl.comp.lang.csharp
Article #22358Re: ServiceLayer - rozważanie, best practice
From: Łukasz 'Maly' Ostrowski
Date: Wed, 22 Feb 2012 15:54
Date: Wed, 22 Feb 2012 15:54
133 lines
6596 bytes
6596 bytes
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? ;-)
> 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
> 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...
> - 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 ;-).
> 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.
> - 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" ;-)).
--
Pozdrawiam,
Łukasz 'Maly' Ostrowski. http://l3v.pl/
Technical Details
Message-ID:
<12b400yh5lb3r.mg3vhkurgug9.dlg@40tude.net>Path:
mbox-mo1.pugleaf.net!news-archive.icm.edu.pl!news.icm.edu.pl!newsfeed.pionier.net.pl!news.dialog.net.pl!not-for-mailReferences:
<ji2pdf$ht6$1@mx1.internetia.pl>Show Full Headers
Complete Article Headers
Path: mbox-mo1.pugleaf.net!news-archive.icm.edu.pl!news.icm.edu.pl!newsfeed.pionier.net.pl!news.dialog.net.pl!not-for-mail From: Łukasz 'Maly' Ostrowski <maly@USUNnemo.pl> Newsgroups: alt.pl.comp.lang.csharp Subject: Re: ServiceLayer - rozważanie, best practice Date: Wed, 22 Feb 2012 16:54:11 +0100 Organization: Acme Lines: 133 Message-ID: <12b400yh5lb3r.mg3vhkurgug9.dlg@40tude.net> References: <ji2pdf$ht6$1@mx1.internetia.pl> NNTP-Posting-Host: static-78-9-71-129.ssp.dialog.net.pl Mime-Version: 1.0 X-Trace: news.dialog.net.pl 1329926054 4134 78.9.71.129 (22 Feb 2012 15:54:14 GMT) X-Complaints-To: abuse@dialog.net.pl NNTP-Posting-Date: Wed, 22 Feb 2012 15:54:14 +0000 (UTC) User-Agent: 40tude_Dialog/2.0.15.1 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit