Hej! Målet med att ha ett lager mellan PL och DAL handlar om "Separation of Concerns". Ursäkta om jag inte helt hänger med på vad du menar. Ah Aha, då förstår jag då blir det genast mer kod i BLL och mindre i DALet. Men då kommer en följd fråga. Säg att man skapar en TabelAdapter med en SQL/StoredProcedure statement vilket gör att ett typat dataset genereras(i detta fallet). Den metoden man anropar på TabelAdaptern kan då mycket väl heta ex getEmailByUserId, och detta gör ju direkt att det inte är BLL som skapar frågan utan den är redan fixad i DAL:et för att kunna skicka tillbaka typade dataset? TableAdapters är inte DAL det är ett BLL av typen TableAdapter :) Hur menar du ? Nej vad jag menar är att ett lager framför tableadepters (det du kallar BLL) är nödvändigt för att ha en konsekvent accessmetod. Okay men tabelAdapters kan jag inte ha i BLL? de måste ju vara i DAL:et efter som vid skapandet så kräver de direkt förbindelse till databasen. Nja, Ja absolut. Du har rätt i att det kanske finns ett intresse av att separera i lager (PL, BLL, DAL), men det känns som det intresset snarare kan skrivas som 'Separation of Skills'. Fast du missar hela poängen och ger dig på patterns som inte har något med saken att göra. Du nämner bl a att vara konsekvent i designen och det menar jag är att driva en principsak. Kopplingen till patterns gjorde jag eftersom jag ser patterns som att vara konsekvent och följa principer. Här är förresten en bra artikel om hur beteende och SoC kan giftas samman. Mycket intressant konversation ni för. Om man i BLL koncentrerar funktioner som har med domänen/branschen att göra av samma anledning som man separerar produktspecifik (native SQL) kod till DALet, betyder det då att man kan skifta ut ett BLL mot ett annat då man vill lansera systemet för en ny bransch? Patrik, Hmm det är inte så lätt, det beror på hur man ser på det. vill man bygga en generell lösning som man kan använda för flera produkter(ex en bas site som sen cosomizeras till kund), vet man vidare att man har makten att tvinga användning av en viss databas ex MS SQL 2005 så är det en klar fördel att bygga in de SQL kommandon som är brach beronde i business lagret, detta gör det sen enkelt att bara byta ut just detta lagret. Självklart måste det finnas en del generella metoder med dessa delegeras bara ner av business lagret till DALet. Treskiktslösningen lämpar sig väldigt bra om du genererar ditt DAL, via de olika verktygen som finns där ute, eller använder ett eget. DAL'et gör basgrejerna som laddar in, uppdaterar, tar bort, etc. Den vet inte ens vad BL är för något. Här finns t.ex "CustomerList, CustomerItem" etc. Jag fortsätter på egen blog med attacken ;) på skiktade arkitekturer m a p förmågan att anpassa sig till förändringar. Såna här diskussioner blir jobbiga utan att definiera vissa fundamentala saker som man hela tiden sedan hänvisar till (business logic, BLL, DAL). Jag är på vår konferans Developer Summit, men kommer snart tillbaka i debatten :) Intressant läsning(även om man fick läsa det 2 gånger =) ) Jag har nu bestämt mig för att inte skicka ner några SQL kommandon till DALet. Detta kommer så klart betyda att jag måste binda samman DAL:et med BLL mer, d.v.s metoder som getUserEmail mfl, men samma ska gäller relationen mellan PL och BLL.3 skicktad lösning?
Jag planerar att påbörja ett nytt web projekt som kommer vara 3 skiktat d.v.s. business Logical Layer, Data Access Layer och Presentation Layer.
Jag skulle gissa på att större delen av datan som hämtas från databasen kommer INTE modifieras något i BLL utan kommer skickas direkt upp till PL där Datan binds(DataSet/DataTabel).
Ska man lösa detta på RÄTT sätt så ska man se till så att PL inte anropar DALet direkt, men detta gör också att det blir en väldig massa extra kod i BLL som egentligen bara redirectar informationen.
Säg att jag ska hämta ut en lista på senast inloggade, då anropar jag kanske en funktion som heter getLatestUsers i BLL, efter som BLL inte ska modifiera på något vis så skickar den direkt ner till DALet som hämtar datan från databasen och returnerar den upp till DAL->BLL och slutligen returneras datan upp till PL utan modifikationer.
Säg vidare att vi har 100-200 liknande funktioner, det innebär en väldigmassa "redirections" i BLL och detta kan göra det svårare att följa koden när det väl uppstår fel.
Så som jag har löst det nu så skapar PL en singelton instans av BLL, förutom diverse klasser i BLL så innehåller den även en singelton instans av DALet. Detta gör att jag kan hämta data direkt från DALet från PL igenom BLL. Dessutom kommer all trafik ner till DALet gå igenom samma instans vare sig anropen görs från PL eller BLL.
Jag kan hålla med om att detta inte känns helt rätt även om det känns som en simpel lösning, frågan är om det finns något annat/bättre vis att göra detta på?
MVH
SnowjimSv: 3 skicktad lösning?
Vanligtivs är det tex BLL som bestämmer vilken fråga som skall ställas via DAL, för mig kan en typisk metod i ett repository se ut så här:
public Instructor FindByEmail(string email) {
ICriteria criteria = CreateCriteria();
criteria.Add(Expression.Eq("Email", email));
Instructor instructor = criteria.UniqueResult() as Instructor;
return instructor;
}
Det innebär att min repository tar ansvar för att skapa frågan, bygga parametranra för att sedan fråga DAL efter ett svar.
Sv:3 skicktad lösning?
Jag förstår att tanken med BLL är att dela upp och på så vis förekla om man bestämmer sig för att byta PL eller DAL i framtiden.
Men titta på följande exempel:
PL
private void Page_Load()
{
lblEmail.text = base.BLL.FindEmailByUserId(base.CurrentUserId);
}
BLL
private string FindEmailByUserId(string inUserId)
{
return this.DAL.FindEmailByUserId(inUserId);
}
DAL
private string FindEmailByUserId(string inUserId)
{
//Kod för att sätta upp förbindelse och skicka iväg den tänkta frågan med inparametern
}
Som vi kan se i exemplet så kommer kod delen i BLL endast stå för en "redirection" till DALet, d.v.s man modifierar inte datan på något vis då det inte behövs.
Säg då att vi har 100-200 sådana metoder, då kommer BLL bli helt fylld med kod som igentligen inte gör något annat än redirectar ner till DALet? Är det rätt att göra på det viset?
I dagsläget har jag istället laggt ett singelton object av DALet i BLL så att man gör på följande vis:
PL
private void Page_Load()
{
lblEmail.text = base.BLL.DAL.FindEmailByUserId(base.CurrentUserId);
}
BLL - Innehåller ett singelton object av DALet, det är även detta object som används för både anrop från PL och från anrop från BLL.
DAL
private string FindEmailByUserId(string inUserId)
{
//Kod för att sätta upp förbindelse och skicka iväg den tänkta frågan med inparametern
}
I detta exempel slipper vi helt att ha någon kod i BLL för sådana anrop som inte kräver någon modifikation från BLL. Varför man ska ha BLL överhuvudtaget är för att det faktiskt finns ett mindre antal anrop som ska modifieras på ett eller annat vis, och dessutom finns det funktionalitet som inte ens har med DAL:et att göra.
Jag tycker dock att min lösning inte känns helt rätt, men frågan är om det finns någon bättre?
Sv: 3 skicktad lösning?
Alltså DAL's uppgift är att vara generell mot databasen. DAL ska inte ha specifika funktioner som FindByEmail utan mer ReturnList(string query).
Det är BLLS uppgift att välja vilken typ av fråga, hur den skall vara uppbyggd osv.Sv:3 skicktad lösning?
Visst PL ska inte känna till DALet och därmed inte det typade datasetet men det kan ju vara så att man lägger typade datasetten i ett externt bibliotek som alla projekt känner till.Sv: 3 skicktad lösning?
Att skapa designer med DataSet / TableAdapters är jag urdålig på. De blandar och ger i den teknologin och är inte helt sanna mot det som kallas "separation of concerns".
Kanske inte ens går att få till en bra design ;)
Nädå, jag hade skapat ngn form av Fasad ovanför. Speciellt med tanke på att du kanske tillslut inte lägger in alla funktioner i tableadaptern utan får skriva ngra utanför.
Att då konsekvent jobba mot en fasad oavsett hur datat hämtas gör det enkelt och konsekvent från PL eftersom du alltid då får mot samma lager.Sv:3 skicktad lösning?
Menar du att man ska lägga ett lager mellan BLL och DAL som står för SQL kommandon och TableAdapters?
Problemet med TableAdapters i designern är ju att den gör en direkt koppling till databasen.
Visst kommer det finns en hel del funktioner som inte ligger i TabelAdapters men som ändå jobbar direkt på databasen.
Kanske är det så att man får skippa TabelAdapters helt om man ska köra med en skiktad lösning?
Det jag kom o tänka på var följande: säg att man ska byta databas från mySQL till msSQL, detta skulle betyda att vi måste ändra DAL:et men som du säer så ska SQL kommandon genereras i BLL och efter som SQL skiljer sig något från mySQL till msSQL så måste man alltså inte bara bygga om DAL:et utan även delar av BLL:et? Det känns som att vi tar ett steg ifrån fördelen med skiktad lösning där det ska gå att byta ut vilken layer som man vill utan att det gör någon skildnad på restrerande Layers.Sv: 3 skicktad lösning?
Det blir rörigt om du från PL ibland går mot tableadapters och ibland mot ngt annat.
Genom att ha en konsekvent design så blir det tydligare från PL vad du skall göra.Sv:3 skicktad lösning?
Sv: 3 skicktad lösning?
TableAdapters kan du mycket väl anropa från BLL, att de i sin tur konktaktar databasen direkt är oväsentligt. TableAdapterns blir ju ett lager i sig.Sv: 3 skicktad lösning?
SoC är en otroligt viktig komponent i allt vad som har med skikt att göra, högt som lågt.
Även när du pratar om lågnivå design som abstraction och encapsulation så är SoC en faktor i hur du ska abstrahera och kapsla in.
Grundprincipen i SoC är ju att varje komponent av systemet (lager, tjänst, klass eller vad du nu vill blanda in där) skall ha sin specifika uppgift.
När det gäller lager i arkitekturen så handlar det om att tex inte blanda ihop dataacess kod med presentationskod tex. Dvs ropa inte på din databas från Page_Load i asp.net sidor. Då ASP.NET sidans "Concern" är att presentera och det är allt den skall fokusera på.Sv:3 skicktad lösning?
Lager i arkitekturen har en fördel när man ser till hur olika kompetenser kan arbeta tillsammans i ett utvecklingsprojekt. Någon kan arbeta med UI i PL, en annan med UC-implementation i BLL och en tredje med med data-access i DALet.
Pratar man snarare om 'concerns' så tycker jag man måste fråga sig 'Who does it concern'? dvs vem bryr sig om det här är ett eget lager - finns det ett systemspecifikt intresse av att separera databasanrop från ASP-sidan eller blir det mer av en principsak (läs design pattern).
Det är lätt att det blir som Jimmy skrev, med mycket extra kod som inte gör annat än att delegerar till underliggande lager, när man underkastar sig en princip/pattern.Sv: 3 skicktad lösning?
Nu pratar vi grundläggande system engineering, och ja det handlar om vem som bryr sig. Men även systemets olika delar är spelare i den disskusionen också. Bryr sig asp.net lagret om var datat kommer från? Vanligtivs inte, då ska inte den koden ligga där heller.
Det har inget med "olika kompetenser" i projekt att göra utan det har med att isolera olika subdelar av ett system från varandra. Det här är viktigt ur flera aspekter än att kunna jobba fler i samma projekt.
Dels handlar det om att ett system blir förberett för förändring, allt förändras hela tiden och då måste koden kunna förändras enkelt den med. Genom att separera delar i olika "concerns" (ex dataaccess, caching, query creation etc) så blir det så mycket enklare när ngn av de delarna måste bytas ut eller förändras på något sätt.
Det underlättar också för killen som kommer efter och skall underhålla koden. Genom att ha tydliga lager som är separerade av "concerns" så blir också syftet med olika delar väldigt tydligt och det blir enormt mycket lättare för "killen efter" att underhålla än om han skall vada igenom dataaccess kod blandad med asp.net kod.
Det finns många mer poänger med varför SoC är extremt viktigt i designen för kod, det här är bara några av dem.
Självklart skall man använda förnuft när man skapar sina lager. Men i Jimmys fall är det väldigt viktigt med SoC och att låta BLL abstrahera bort en väldigt viktig "Concern" dvs om datat han vill ha kommer från en tableadapter från dataset eller om den kommer från en query han av någon anledning vill skapa utifrån.
Genom att då skapa ett BLL ( repository, gateway eller what ever) så behöver asp.net koden inte fundera på /hur/ datat den behöver hämtas, den får ett dataset/datatable, punkt. Om han sedan vill ändra en fråga till att använda / inte använda TableAdapters så kommer det inte påverka någon av hans asp.net sidor.
Och ja, det kan innebära att han på vissa ställen får enbart forwarding calls. Men det är viktigt att vara konsekvent i designen av många skäl inkluderat att det skall vara lätt att hitta vad man vill ha även för "killen efter" att ha vissa saker i adapters och andra i ett BLL är bara förvirrande.
BTW Jonas, du är en tidstjuv, här sitter jag med förberedelserna för developer summit också är du tvungen att slänga ur dig ngt sånt här ;)Sv:3 skicktad lösning?
En av de stora fördelarna med SoC är ju som du skriver att kunna förändra delar av systemet utan att påverka hela systemet. Men lager på lager är ju ingen garanti för att uppnå det. Lager är ett sätt att separera dels det jag nämnde om kompetenser, men framför allt förändringen av underliggande teknik. Det ger en rörlighet i att kunna byta ut en teknik (Oracle DB) mot en annan (SQL Server) eller att tillföra exempelvis mobila klienter. Det finns däremot inga fördelar i att separera i lager då man pratar om förändringar i systemets beteende.
Frågan är ju egentligen vad som anses vara en sannolik förändring i systemet och när i tiden den kommer att inträffa. Är det att 6 veckor efter lansering byta databasserver, skapa nya egenskaper för en entitet, att förändra implementerade affärsprocesser eller affärsregler. Separera baserat på lager underlättar enbart förändring i första exemplet, möjligtvis det andra beroende på hur man implementerar det, men knappast i de senare två som förändrar beteendet i systemet.
Olika förändringar skapar olika behov vilket borde vara det grundläggande för SoC. Behoven dikteras ju sannolikt av den som bryr sig och där skulle jag nog inte vilja räkna in systemets olika delar. För om man på frågan om vem som bryr sig (concerns) räknar in systemets olika delar så är det som att en åtalad får vara både tilltalad och dommare på samma gång.
Lager på lager inför dessutom komplexitet (och mer kod). "Killen efter" kanske inte är intresserad av att hantera mer och komplexare kod utan istället mindre och sammanhängande kod. Det är inte självklart att lager på lager skapar enklare förutsättningar för "killen efter". Jag skulle snarare vilja hävda motsatsen i minst lika många fall som att det skulle vara en fördel.
Ledsen om jag stör i förberedelserna, men jag kan inte hålla mej ...Sv: 3 skicktad lösning?
http://today.java.net/pub/a/today/2006/02/14/separation-of-concerns-and-bpel.htmlSv:3 skicktad lösning?
Det som förvånar mig är att man ska skapa SQL kommandon i BLL? Det hade känts mer rätt att skapa dessa i DALet då denna faktiskt ska handskas med allt som har med Databasen att göra. business lagret bör inte känna till databasen och således bör den inte häller skapa SQL kommandon då de mycket väl kan vara specificerade för databasen(MS SQL 2005/MySQL4/MySQL5 mfl).
Jag har fått för mig att business lagret endast ska handskas med sådant som rör branchen som mjukvaran jobbar inom, ex kan det vara planera in tider, Räkna ut moms o.s.v.
Dock behöver jag bygga en generell paging metod för alla sidor som ska hantera pagin på lister på en site så ska detta så klart hamna i PL.
Är inte detta rätt tänkt?
Vidare förstår jag inte varför ni pratar om DataAdapters som ett enskilt lager? DataAdapters skulle jag vilja skapa i DAL:et ihop med de SQL statsment som körs sidan om( i mitt fall ser det ut att inte bli några DataAdapters då pluginen för mySQL till Visual studio inte stöder detta).
Om man skulle följa ert tänkande så är frågan vad som kommer finnas i DALet? som jag ser det så kommer det endast finnas en klass som publiserar funktioner för att exikvera SQL statments mot databasen, så kanske en 4-5 olika metoder(ExecuteScaler, ExecuteNonQuery o.s.q), destu mer kod kommer hamna i BLL?
Detta gör så klart att DALet blir helt generellt och kan enkelt bytas ut, men som jag nämnt sen tidigare så kommer det troligen krävas att man ändå måste in i BLL för att updatera SQL statments för att de ska passa den nya databasen.
Mitt mål är att ha så mycket StoredProcedures i Databasen som möjligt för istället ha dem i DAL eller BLL, är detta att gå emot den skikade lösningens principer? Det borde insta spela någon roll då man kan köra Call myStoredProcedure(eller som i MS SQL 2005 execute myStoredProcedure). Dessutom kan vi bara där se att syntaxen skiljer sig vilket gör att vid ett byta från mySQL till MS SQL skulle betyda att vi måste ändra i DALet och i BLL(om vi nu ska ha SQL statments här)
Så min fråga är då, är det så farligt om DALet har funktioner i stil med getLastLogins, getEmailByUser? i vid ett byta av SQL databas så behöver vi endast ändra i DALet medan BLL blir oförändrat då den jobbar mot samma interface.Sv: 3 skicktad lösning?
Ursäkta om frågan är aningen cynisk, men jag ställer mej fortfarande kritisk till att bygga lager på lager av principskäl.
Jimmy, det var inte svar på dina frågor men jag antar att dom var riktade till Patrik eftersom jag själv inte är en vän av principer ... Sv:3 skicktad lösning?
Kollar man på Data Access Objects (http://java.sun.com/blueprints/corej2eepatterns/Patterns/DataAccessObject.html) så gör de ju åt Jimmys håll (updateCustomer().. osv), så har även jag jobbat men det blir ju som sagt mycket "tomma" metoder i BLL-klasserna med endast vidarebefodring ner i DAL, vilket ju känns onödigt.
Din rekommendation blir alltså att köra endast generella databasmetoder i DAL och det låter ju logiskt. Men hur bör man göra med tex SQL osv, jag vill ju hårdnackad vägra att generera SQL i BLL tex utan det bör ligga i DAL så att man kan vid behov lyfta ut det och ersätta med annan databas tex. Någon form av queryobjects antar jag som sedan låter DAL översätta till SQL som sedan exekveras i DAAB eller HQL som skickas till NHibernate? Finns det bra exempel att titta på någonstans?Sv:3 skicktad lösning?
Är det dock så att databasen mycket väl kan ändras, ex MS SQL 2005 till My SQL då är frågan hur bra det är att bygga in SQL statements i business lagret, då hade det troligen varit bättre att ha dessa i DALet?
Anledgningen till att jag väljer en skiktad lösning är inte av principsak utan det är för att jag vill prova på att jobba på detta vis och se om jag får något ut av det, jag har ju nyligen byggt http://www.bradspel.net/ där jag INTE använt en skiktad lösning(även om det är något updelat internt), det hade varit intressant att se skildnaden.Sv: 3 skicktad lösning?
BL är starkt beroende av sitt DAL. Här i BL skriver du funktioner som t.ex GetByEmail, GetListOfMostActivePeople, allt med logiska och självbeskrivande namn så att det inte uppstår någon fråga för implementatören, och dessa i sin tur - arbetar mot DAL.
Det enda som UI(Presentation) ska göra är att påverka utseendet. Här hämtar och lämnar du dina önskemål till BL, som arbetar åt dig, just genom de smarta funktioner ovan.
Gör du på detta sätt får du en fin skalbar applikation, och sen kan du byta ut UI't helt och hållet om du vill. Säg att du helt plötsligt får in en order om en webapp. BL och DAL återanvänder du utan förändring, och du bygger bara websiten och jobbar mot BL.
(med undantag med typer från DAL, som kan komma ner till UI endast i läsnings-syfte, man ska inte köra någon CRUD här inte!)
Har testat även ModelViewPresenter pattern, men tyckte att den här traditionella 3skiktslösningen fortfarande är bäst !
Och Jonas: Det kan låta onödigt det där med att du skriver om att BL i många fall endast gör vidarebefordring, men det stämmer inte om du t.ex vill logga antal metodanrop, kanske en annorlund felhantering redan i BL där du loggar eller ej. Om du använder dig av DAL på UI't så måste du ändra på alla UI sen. Sen kan du även göra testprojekt som testar igenom hela BL'ets alla funktioner ! det kan du inte göra på något bra sätt mot UI.
Och tänk om du i efterhand får reda på att när du ska hämta kundlista, helt plötsligt måste hämta kunder baserat på .... ja kundnummer måste börja med 'A', då ändrar du bara på 1 ställe !Sv:3 skicktad lösning?
Läs mer http://choreographictechniques.blogspot.comSv: 3 skicktad lösning?
Det finns faktiskt olika terminologier som överlappar varandra och använder liknande ord och koncept.
Med andra ord är det inte självklart vad ett "business Logical Layer" är eller hur ett API för ett "Data Access Layer" ska se ut.
Patrik Löwendahl skrev:
> DAL ska inte ha specifika funktioner som FindByEmail utan mer ReturnList(string query).
När du så bestämt påstår att signaturen till en DAL-metod ska ta en sträng (SQL förmodar jag) som parameter,
så vill jag också påpeka att det finns t.ex. ett "Data Access Object" interface som definieras i Core J2EE patterns, som inte exponerar vare sig någon sträng med sql query eller en xpath query, just eftersom en implementation av ett sådant DAO skall kunna implementeras av exempelvis en klass som hämtar datat från en RDB, men alternativt kan den hämta datat från XML-filer.
http://java.sun.com/blueprints/corej2eepatterns/Patterns/DataAccessObject.html
Här är ett annat exempel (från Microsoft dena gång)som uttryckligen använder begreppet Data Access Layer (DAL):
http://msdn2.microsoft.com/en-us/library/ms978510.aspx
> // Method exposed by a Data Access Layer (DAL) Component
> public string GetProductName( int ProductID )
Som synes så är även denna "DAL-metod" oberoende av en specifik persisteringsmekanism (d.v.s. ingen SQL sträng som parameter).
Jag vill absolut inte påstå att det inte existerar någon publikation som definierar DAL på det sättet som Patrik beskriver, med ett direkt SQL-beroende i API:t, utan jag vill bara efterlysa källhänvisningar (till DAL/BLL-definitioner) innan man börja svänga sig med terminologi och gör bestämda påståenden av typen "DAL's uppgift är att..." , "DAL ska inte ha..." , "Det är BLLS uppgift att..."
Angående "BLL" (business Logic Layer) så är det inte självklart vad detta kan tänkas vara.
Möjligen kan jag försöka gissa att Jimmy menade ungefär så här:
business logic layer = business layer = domain layer
men det infogade ordet "logic" bör rimligen innebära någon skillnad jämfört med något som endast kallas "business layer" (d.v.s. utan "logic")
och en alternativ tolkning är då att "business logic layer" ska innehålla mer renodlad business (domain) logic
och vara ett procedurellt lager med metoder som applicerar domain logic på externa object från domain layer snarare än att försöka låta domain logic vara integrerade i de domain objects som finns i domain layer.
Okej, den ovanstående meningen blev kanske lite svårläst, men läs då istället detta:
http://www.martinfowler.com/bliki/AnemicDomainModel.html
och i synnerhet följande formulering tycker jag verkar vara relevant för Jimmys "business logic layer":
> "Instead there are a set of service objects which capture all the domain logic"
Frågan du bör ställa dig är alltså vilken typ av logik är det egentligen du vill klämma in i ditt s.k. "business logic layer" och hur skulle den koden se ut ?
I första hand bör du försöka placera logiken inuti ett domain object, om det finns något naturligt sådant, alternativt ett särskilt Service object i nödfall om logiken inte passar in i något annan klass.
När jag ovan skriver domain object så kan man dels tänka sig en Entity eller Value object, enligt terminologi från "Domain-Driven Design" (DDD) http://domaindrivendesign.org/
Den som är bekant med GRASP mönster kan även betrakta ovanstående hänvisning till domain object som en "Information Expert" (putting the behavior in the same class as the data):
http://davidhayden.com/blog/dave/archive/2005/03/27/895.aspx
http://www.martinfowler.com/bliki/GetterEradicator.html
När jag ovan skriver service object så kan du se det som en "Pure Fabrication" (GRASP terminologi) t.ex. ett DDD Service Object.
http://davidhayden.com/blog/dave/archive/2005/09/18/2476.aspx
Frågan är alltså vilken typ av "logik" du vill ha i din s.k. BLL (och varför du vill placera den där) och hur koden då skulle implementeras.
Antag t.ex. att du vill visa en del av websidan endast för användare som är vuxna.
Då kanske du med en "BLL-klass" skulle göra något liknande ?:
private void Page_Load()
{
int age = base.BLL.FindAgeByUserId(base.CurrentUserId);
this.panelForAdults.Visible = (age > 18);
}
eller kanske så här:
private void Page_Load()
{
bool isAdult = base.BLL.IsAdult(base.CurrentUserId);
this.panelForAdults.Visible = isAdult;
}
eller kanske så här:
private void Page_Load()
{
User user = base.BLL.FindUserByUserId(base.CurrentUserId);
this.panelForAdults.Visible = (user.GetAge() > 18); // OBS ! se länken ovan till "GetterEradicator"
}
I ovanstående fall så menar jag att "BLL-klassen" skulle vara ett Service-objekt som i onödan lägger logiken i en extern klass snarare än i det domain object där logiken naturligt hör hemma.
Jag förutsätter nu alltså att User-klassen har ett age-fält, och skulle då snarare rekommendera att göra något liknande som nedan:
private void Page_Load()
{
User user = UserRepository.FindUserByUserId(base.CurrentUserId);
this.panelForAdults.Visible = user.IsAdult();
}
En poäng med objekt-orienterad programmering är att man ska sträva efter att placera tillhörande data och beteende i samma klass, och i det här exemplet är det alltså "User" som är en "Information Expert" (GRASP) som innehåller det relevanta datavärdet för åldern och därför bör den också innehålla den metod som svarar på frågan om personen är vuxen (vilket alltså huvudsakligen är baserat på åldern).
Dock kanske resultatet på denna bool-metod inte nödvändigtvis är enbart åldersbaserad, och om man skulle börja skriva sån här kod i ett BLL (procedurellt Service object) så ser man tydligare poängen med att lägga in logiken direkt i den relevanta klassen som innehåller datat:
(jag kommer just nu inte på någon bra engelska, så det fick bli svengelskt metodnamn "IsOmyndigForklarad" nedan...)
public class BLL {
public bool IsAdult(string userID) {
User user = GetByUserId(userID);
return (user.GetAge() > 18) && (!user.IsOmyndigForklarad()); // GetterEradicator !!!
}
// ...
}
Det är alltså bättre att lägga in "logiken" direkt i den aktuella klassen som innehåller datat:
public class User {
private int _age;
private bool _isOmyndigForklarad;
// ...
public User(...) { ... // konstruktor..
public bool IsAdult() {
return (this.GetAge() > 18) && (!this.IsOmyndigForklarad());
}
private int GetAge() { ...
private bool IsOmyndigForklarad() { ...
// ...
}
Sammanfattningsvis tycker jag alltså att du bör fundera över vad du egentligen ska ha ditt BLL till, och om du verkligen vill lägga diverse "logik" dit istället för att försöka placera logiken direkt i domain klasserna där de hör hemma, och endast vid behov använda särskilda service objekt, t.ex. om logiken inte hör hemma i något specifikt objekt t.ex. om logiken använder flera domain objects. I det sistnämnda fallet kan du eventuellt placera metoden i en av klasserna och därifrån skapa ett beroende till den andra klassen, men ett alternativ är alltså att istället externalisera logiken till ett separat service object utan kopplingar mellan de olika domain klasserna. Dock bör du inte skapa en "Anemic Domain Model" med allt beteende separerat från dina domain klasser.
http://www.martinfowler.com/bliki/AnemicDomainModel.html
/ TomasSv:3 skicktad lösning?
Sv:3 skicktad lösning?
Domain klasser är helt klart något jag ska sträva efter, frågan är om dessa kanske ska läggas i ett externt entity klass bibliotek? Dessa får då såklart bli fyllda av funktioner som finns i BLL, alltså ingen direkt kontakt till databasen. De som kommer känna till Entity objekten är isåfall BLL och PL.
Det kommer finns funktionalitet som inte kommer vara knutit till något specifik domain objekt, dessa kan man också tänka sig lägga i BBL i en typ av service klass som du(Tomas) nämnde.
En fråga då som uppstår är: säg att vi vill bygga en generell page metod för listor som visas på siten(PL) denna paging metod måste alla sidor kunna komma åt genom någon form av service objekt, ska man då lägga denna paging metod på BLL eller ska den läggas i något service objekt som finns i PL?
Jag är fortfarande emot att ha SQL statements i BLL, att skicka ner SQL statments till DAL känns inte som en skickad lösning helt enkelt. Säg ex att vi ska hämta ut viss data från databasen baserat på 4 inparametrar, efter som jag vill undvika injection attacs(i PL) så specificerar jag och tilldelar dessa parametrar i SQL Command objectet. Detta SQL Command objekt exiveras sedan av DALet(ex mySQLCommand.ExecuteNonQuery). Detta gör att jag inte bara kan skicka ner ett SQL statment till DALet efter som parametrar ska med och de måste vara kända för den som lägger in dem i Command objektet.
Om man nu absolut inte ska ha dem i DALet så kanske man ska utveckla ytterligare ett lager som ligger mellan BLL och DAL, detta lager skapar SQLCommandObjekt på begäran av BLL och delegerar dem vidare ner till DALet?Sv: 3 skicktad lösning?
Vidare har jag taget bort alla statiska klasser, istället så kontrollerar jag vid initiering av ett objekt om denna användare/tråd redan skapat detta objekt.
Min fråga är:
Är det rätt att lägga 1 klass i varje lager(BLL och DAL) som representerar lagret funktionalitet. Ex i BLL har vi en klass som heter BusinessLogicalLayer, denna tillhandahåller properties som exponerer det som faktiskt finns att använda i BLL ex ExceptionHandler klassen, DataAccessLogicalLayerHandler klassen o.s.v.
När klienten försöker komma åt ett objekt(ex DataAccessLogicalLayerHandler) så kommer BusinessLogicalLayer klassen kontrollera om det redan har skapats ett objekt av denna, om inte så skapas det. På detta vis kommer en användare endast jobba mot ett objekt i taget och inte skapas upp en massa objekt av samma typ.
Eller ska man helt enkelt strunta i denna typ av klass och istället manuellt skapa upp de objekt som behövs i pageBase(basklassen till websidorna)?