Jag har lite frågor kring arktiketuren för olika objekt. Om vi börjar med anropen till tex ett customer objekt. Hur skulle ni bygga upp en sådan struktur? Man ska kunna skapa, editera och ta bort kunder från databasen. Vad tror ni om följande "yttre" struktur? Jag tycker det här är ett slarvigt sätt att tänka och skapar stora tunga "ultimata" objekt. Jag brukar förespråka ngt som kallas "Separation of Concerns" vilket i princip innebär att varje klass får sin egen roll. Liten fråga, måste CustomerRepository känna till customer objektet eller använder man serialisering av någon typ? Det lät väldigt intressant! Kan du berätta lite mer om vad CustomerRepository har för funktionalitet? Vad händer tex i insert:en? "Liten fråga, måste CustomerRepository känna till customer objektet eller använder man serialisering av någon typ?" <b>Rimligtvis behöver CustomerRepository känna till Customer-objektet eftersom det är ett objekt av denna typ som tas emot i metod-parametrarna alternativt retuneras från klassens metoder.</b> Jag hittade en sida nu på morgon som ger lite exempelkod på något snarlikt kanske. Jag har ännu inte hunnit läsa artikeln men det såg ganska likt ut. Är det något som ni inte håller med om? <b>"Då kan/bör man ha en generell/bas klass som CustomerRepository känner till och vilket Customer ärver"</b> En fundering som jag bland annat har kring DDD är varför man endast ska ha ett ansvarsområde per klass. I länken jag hänvisade till så skiljde man på repository och factory. Det känns som att det blir lätt många klasser. Finns det någon anledningen varför man inte bakar ihop repository och factory till en klass? Jag antar att syftet med att man har två olika klasser är just det här med ansvarsområdet men varför bör man ha det så? Varför är det ett problem med många filer? Något jag har stött på är om ett objekt har ett antal andra objekt (av annan klass) kopplade till sig, t.ex. ett företag har ett antal anställda. Där tycker jag att det känns naturligt att ha en metod getEmployees() i företagsklassen, så att man kan skriva Employee[] = company.getEmployees(). Så vill jag ha det... >>Employee[] = company.getEmployees(). Roger, jag ser ingen principiell skillnad mellan ditt sätt att hämta alla anställda och mitt sätt att göra det. Det var bara en syntaktisk skillnad. Den stora skillnaden är det rent semantiska. >>Varför är det ett problem med många filer? Jag kanske är dum, men jag förstår inte hur det kan vara en sådan semantisk skillnad mellan Det finns en skillnad; att göra följande med company.Employees-varianten känns mer logiskt: Där håller jag med. Men för att det skall fungera krävs att company.Employees returnerar den inre variabeln genom referens. Och så kanske det är i C#, men inte i alla språk. <b> <b>1) CustomerRepository brukar väl placeras i BLL?</b> >>Att BLL ska vara beroende på vilken ormapper man har kan väl inte vara ett argument, eller? Ärligt talat, hur ofta byter du DAL i ett projekt som rullar? Denna satt jag och väntade på, dvs order.OrderLines.Add( orderLineToAdd ). Vad är det som säger att Company.Employees inte kan vara en array? Jag hade en sådan situation nyligen i ett system där olika företag kan registrera anställda och annat. Det blev många metoder som hamnade i Company, eftersom jag hellre ville att företaget skulle ge mig objekt med vissa egenskaper än att jag skulle fråga en förvaringsplats (repository) efter objekt som hörde till ett visst företag; det fick företaget själv göra.Arkitektur på objekt
// Instantiate the Customer class using the default constructor Customer
customer = new Customer();
// Assign some of its properties
customer.LastName = "Jones";
customer.FirstName = "Jeff";
// Call its Create() method to save the values in the database, and get its new primary key (CustomerID) value
int customerID = customer.Create();
// Instantiate the Customer class using the constructor that takes the CustomerID as a parameter
Customer customer2 = new Customer(customerID);
Trace.Write("LastName: " + customer2.LastName);
Trace.Write("FirstName: " + customer2.FirstName);
// Change the value of the first name then save the changes to the database
customer2.FirstName = "Stephanie";
customer2.Update();
// On second thought, let's just delete the record entirely
customer2.Delete();
Koden är hämtad från:
http://www.awprofessional.com/articles/article.asp?p=382852&seqNum=1&rl=1Sv: Arkitektur på objekt
Det innebär ungefär så här:
// Instantiate the Customer class using the default constructor Customer
customer = new Customer();
// Assign some of its properties
customer.LastName = "Jones";
customer.FirstName = "Jeff";
// Insert this Customer to the database
CustomerRepository.Insert(customer);
// Get a customer object by id.
Customer customer2 = CustomerRepository.GetById(customer.Id);
// Delete a customer
CustomerRepository.Delete(customer2);
Fördelen med att göra på det här sättet är dels att du får mindre klasser med enklare överblick, dels kan Customer klassen koncentrera sig på att vara Customer och behöver inte också vara databas access. Det finns böcker skrivna om fördelarna men det där är några av dem.
Sv:Arkitektur på objekt
Sv:Arkitektur på objekt
Sv: Arkitektur på objekt
Rimligtvis behöver CustomerRepository känna till Customer-objektet eftersom det är ett objekt av denna typ som tas emot i metod-parametrarna alternativt retuneras från klassens metoder.Sv:Arkitektur på objekt
Det är inte nödvändigt.
Om vi förutsätter att CustomerRepository tillhör ett lägre databas lager och att Customer tillhör affärslogiklagret.
Då kan/bör man ha en generell/bas klass som CustomerRepository känner till och vilket Customer ärver.
Eller att parametern är deklarerad som "serialiserbar" och därmed kan serialiseras till XML från vilket en den kan sparas till databasen.
Detta framkommer inte från koden som representerats.
För mig känns det naturligt att CustomerRepository igentligen skulle kunna vara en generell klass vilket sköter läsning och skrivning till databasen.
Varför skall den bara ha ansvar för en klass?Sv:Arkitektur på objekt
http://steve.emxsoftware.com/Domain+Driven+Design/DDD+Repositories++FactoriesSv: Arkitektur på objekt
Nej, det bör man undvika. Arv i domänmodellen för att stötta infrastrutkuren är inte att föredra utan är snarare ngt som försvårar utvecklingen. Tex kommer det att kräva reflection eller down-casts i repository metoden för att kunna komma åt olika egenskaper för objektet. Dessutom tar du upp det enda arvet som kan användas för att definera modellen istället.
<b>"Eller att parametern är deklarerad som "serialiserbar" och därmed kan serialiseras till XML från vilket en den kan sparas till databasen."</b>
Det här är verkligen inte att rekommendera. Av många anledningar; för det första kan datat inte indexeras i databasen. Databasen har ingen möjlighet att skapa index och ekeveringplaner på innehåll i en blob eller en stor varchar.
Dessutom kommer du låsa fast datat till användning bara från din applikation, eller applikationer som har tillgång till just dina klasser. Du kommer få välidigt svårt att tex ta ut rapporter med en rapportmotor som crystal eller Reporting Services. Du kommer också få det väldigt svårt att migrera till andra system eller hämta data till andra system direkt från databasen.
<b>"För mig känns det naturligt att CustomerRepository igentligen skulle kunna vara en generell klass vilket sköter läsning och skrivning till databasen.
Varför skall den bara ha ansvar för en klass?"</b>
Igen av flera anledningar. För det första därför att varje klass (eller aggregat av klasser, dvs där Customer tex är en root med flera underliggande klasser) alltid har specialregler för vad som gäller för dem. Att få till det generellt är svårt. För det andra har repository många fler ansvarsmoråden än bara Save och GetById. Den tar också hand om utformningen av datat vid flera usecases. Tex CustomerRepository.GetCustomersForCity(string city) eller CustomerRepository.FincCustomersByFilterOnName(string nameFilter). Att försöka göra de här sakerna genrella vet jag ingen som vågat sig på.
Sen är det också så att man inte får förväxla en Repository för ett DAL, det är inte samma sak. Ett repository tar hand om och förbereder alla frågor och hanterar och prepparerar datat som skall tillbaka. Ett DAL tar hand om den direkta databasaccessen.
I ett scenario byggt på DDD så har man förutom Domänklasserna (Customer, Order osv) och Repositorys; oftast en O/R mappare som exekverar alla frågor mot databasen och returnerar rådatat till repositoryn. Det är O/R mapparen som går att jämföra med ett DAL:Sv:Arkitektur på objekt
Jag har en annan fråga också :) För att slippa mängder med filer i projektet skulle det vara bra att köra customer, customerrepository och customerfactory i samma fil? Om ja, vad ska man kalla filen. Eller är en sådan design något som är "förbjudet" :)Sv: Arkitektur på objekt
man har ju filer för att lätt hitta till rätt ställe.
att lägga flera saker i samma fil gör ju bara sker bökigare.
>>skiljde man på repository och factory
att skilja på ansvar är bra för att på samma sätt som med flera filer, så blir det lätt att hitta var något blir fel.
blir det fel när man skapar ett nytt objekt så vet man att det är i factory klassen det blir fel och inte i repository.
Dock tycker jag personligen att just repo/factory är lite fånig separation.
den försvaras ofta med liknelser som "jo men böcker produceras i fabriker men lånas ut på bibliotek"
den liknelsen är rätt tafatt här eftersom det handlar om mer fysiska problem i det fallet.
det är tex opraktiskt att trycka alla bibliotekets böcker på samma ställe.
det är inte särskillt trevligt med en stor fabrik mitt i staden, etc etc.
När det gäller objekt så finns inte dessa problem, det finns ingen egentlig nackdel med att låta samma klass agera repo och factory, mer än att det kan vara lättare att prickskjuta fel om det är separerat.
>>Eller är en sådan design något som är "förbjudet" :)
det finns väl inget som är förbjudet när det gäller kod?
det finns ju bara tumregler på vad som är praktiskt.
tycker du det är smidigt med en enda stor klass och goto så fine.. så länge det fungerar och du kan underhålla det på ett bra sätt så är ju allt tillåtet.
men dessa tumregler har ju kommit fram för att det visat sig vara praktiskt för de flesta.
men är det så att du skruvar i en skruv med en bordskniv snabbare än med en skruvmejsel så för all del. fortsätt med det ;-)Sv:Arkitektur på objekt
Problemet är att snart vill man begränsa urvalet och börjar lägga in getEmployeesByBoss(), getEmployeesByGender(), getEmployeesByBossAndGender(), ... Då blir det snabbt rörigt.
Samtidigt, varför skulle det vara okej att ha employee.getCompany(), men inte company.getEmployees()?Sv: Arkitektur på objekt
fast det är ju inte så vanligt, iaf inte om du har en riktig ormapper.
då har du ju tex:
<code>
List<Emplyee> employees = company.Employees;
...
Company company = employee.Company;
</code>
det är standard för alla mappers.
vill du ha urval så har flera mappers olika DSL'er där du kan skriva frågor mot din domänmodell
tex:
<code>
//NPersist
string npath = "select * from Emplyee where Company.Name like 'a%' or Address.Street = 'abc'";
List<Emplyee> employees = context.GetObjects<Employee>(npath);
</code>
där npath är ett språk som liknar sql fast som går mot domänmodellen och kan traversera property paths (dvs du skriver inte joins , du anver bara en path "order.customer.address.street" tex , precis som om villkoret vore i vanlig kod)
NHibernate har HQL som är liknande, och MS är ju på g med sitt linq som också är ganska likt fast direkt integrerat i språket.
dessa filtrerade frågor placeras lämpligast i ett repo / servicelayer , så att domänmodellen endast hanterar den ofiltrerade datan.Sv:Arkitektur på objekt
Jag arbetar i PHP där jag inte har sett till ORmappers. Skall kolla närmare på det och fundera över hur man skulle kunna göra något liknande i PHP.Sv: Arkitektur på objekt
Ett företag har anställda och en anställd håller till på ett företag. Det är en del av domänen. Om du däremot börjar forma din data och ställa subquerys, då kan det vara läge att inte ha det i domänen. Just därför att det inte beskriver hur domänen ser ut utan hur den används.
Roger har förklarat factory poängen rätt bra, men jag skulle vilja tillägga en sak. En repository har som uppgift att kommuncera med persistenslagret. En factory skapar bara objekt. Dvs dess uppgift är att se till så att objektet är skapat på korrekt sätt med rätt förutsättningar, den kommunicerar aldrig med databasen. Därav separationen, repositories hämtar och lämnar objekten i persistensen, faktoryn jobbar bara i applikationen.Sv:Arkitektur på objekt
Vet man bara var allt ligger så är det kanske inte något större problem men har man en plattform som man bygger alla sina applikationer på så kommer BLL växa successivt allt större. Jag tycker det är viktigt att försöka strukturera upp i BLL på något bra sätt men det får bli en annan tråd.
Jag har två slutliga frågor :)
1) CustomerRepository brukar väl placeras i BLL?
2) I kodexemplet på nedanstående länk så skickar Steve in en datareader från DAL via CustomerRepository till CustomerFactory för att sätta några properties på Customer. Är det att rekommedera att skicka en datareader från CustomerRepository till CustomerFactory? Det känns inte helt hundra, Hur ser ni på det?
http://steve.emxsoftware.com/Domain+Driven+Design/DDD+Repositories++FactoriesSv:Arkitektur på objekt
Employee[] = company.getEmployees()
och
List<Emplyee> employees = company.Employees;
Menar du att det är okej att ha getEmployees() i Company, men inte att ha getEmployeesByQualification() där?Sv: Arkitektur på objekt
Employee my_new_employee;
//Create our employee here
company.Employees.Add(my_new_employee);
Sv:Arkitektur på objekt
Sv: Arkitektur på objekt
Menar du att det är okej att ha getEmployees() i Company, men inte att ha getEmployeesByQualification() där?</b>
Ja det menar jag. getEmployess är ju i princip samma sak ( bara i ett mindre bemedlat språk ;) som company.EmployeesSv: Arkitektur på objekt
Ja
<b>
2) I kodexemplet på nedanstående länk så skickar Steve in en datareader från DAL via CustomerRepository till CustomerFactory för att sätta några properties på Customer. Är det att rekommedera att skicka en datareader från CustomerRepository till CustomerFactory? Det känns inte helt hundra, Hur ser ni på det?</b>
Ja det är att rekomendera om du vill bygga det själv. Factoryn skapar objekt, om den skall skapa och fylla så kan du lika gärna skicka in en reader som att skicka in all data som parametrar.
Fast jag rekomenderar hellre att använda en O/R mappare :)Sv:Arkitektur på objekt
Lite sent inlägg men hakade upp mig på Roger Johanssons:
"fast det är ju inte så vanligt, iaf inte om du har en riktig ormapper. "
Att BLL ska vara beroende på vilken ormapper man har kan väl inte vara ett argument, eller? Då skapar man ju ett beroende neråt, så att säga. När man sedan vill byta ormapper får man ju skriva/ändra en massa i onödan.Sv: Arkitektur på objekt
det är det ju när ormapparna som finns fungerar på ett visst sätt..
om nu 99% av alla mappers använder properties för att exponera ut listor så vore det ju väldigt onödigt att försöka wrappa upp dina entiteter i några nya entiteter bara för att kunna exponera samma data på annat sätt..Sv:Arkitektur på objekt
Om du inte vill ha ett beroende på DALets API, får du själv skapa en abstraktionSv:Arkitektur på objekt
Är detta egentligen korrekt ur DDD?
Jag tycker personligen att order.AddEmployee( orderLineToAdd) är mer korrekt. objektet company vet vilka Employees som är tillåtna att lägga till.(säg att vi har en business rules som säger att vi bara får ha två anställda för den här typen av company). Objektet company bör/skall hantera denna relationen. (Separation of Concerns). Eller?
Varför låter man då collectionen hantera detta?
Samma sak gäller ju i princip property't Employees eller metoden GetEmployee(). I det första exemplet får jag en lista, där jag faktiskt kan lägga till nya anställda, utan att listan kan kontrollera med företaget om det är ok. (kan ju lösas med delegates iofs, men då skall man in å rota i o/r mappernas collections).
I GetEmpl.. får jag en array där jag inte kan lägga till anställda, för att lägga till poster så måste jag använda mig order.AddEmployee(...) Jag tycker därför att GetEmployee() känns mer rätt, givet att det är vyn för applikationslagret, om det är för o/r-mappern så behövs givetvis ett property't Employees
Bara en tanke.. Synpunkter?Sv: Arkitektur på objekt
Det du pratar om nu är bussinessrules, aggregatavgränsningar och valideringar. Det är en helt annan tråd :)
Men iaf.
Ja det är korrekt ur DDD om man förutsätter att du inte har specialregler för hur du får lägga till orderLine. Om du har specialregler så skall du istället (som du säger) flytta addandet. Förslagvis till en factory.
OrderLine aNewOrderLine = OrderFactory.CreateOrderLine(theOrder);
Du kan självklart också lägga det direkt på ordern:
OrderLine aNewOrderline = theOrder.CreateOrderLine();
men det går lite utanför just SoC eftersom det rena skapandet där kanske är ngt som en factory bör ta hand om.
Om man tänker på att istället flytta en orderLine. Så är det ytterligare en abstraktion, Serviceklassen.
OrderService.MoveOrderLine(orderLine, destinationOrder);Sv: Arkitektur på objekt