🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: alt.pl.comp.lang.csharp
Article #22358

Re: ServiceLayer - rozważanie, best practice

#22358
From: Łukasz 'Maly' Ostrowski
Date: Wed, 22 Feb 2012 15:54
133 lines
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-mail
References: <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