Äntligen har man fått lite tid över till sina egna projekt. För min del så innebär det att jag äntligen kommit igång att läsa Eric Evans Domain Driven Design och jag kan bara ställa mig i ledet bland alla de som lovordar den. Joo, det hoppas jag väl. ;) Tyvärr har jag inte haft tid de senaste åren att engagera mig i designfrågor på den nivån. Men det här låter intressant, jag skulle gärna vilja studera lite källkod skrivet efter den här principen. Så länge hälften av termerna i ditt inlägg är okända för mig kommer jag inte att förstå, men med lite källkod brukar man förstå principen...Var någonstans passar detta in i en flerskiktad lösning? Affärslagret? Affärslagret + datalagret? Japp, sånt här är kul. :) Hej Patrik, Hej Johan! Hej, Myckte intresant och kul ämne, pratade nyss med min bror Johan som sa att denna tråd fanns så jag tänkte bidra med lite information, kan bli dubbel info från vad Johan skrev med det skadar ofta inte. Johan: "MEN, du säger att du vet var rören skall dras. Det är inte riktigt sant. Du vet hur rören skall dras inne i huset men du måste ta reda på var vattenkällan är för att veta hur vattnet ska komma in i huset. Utefter det får du sedan planera hur rören skall dras från källan till husets "vattenfördelare". Patrik: Varför vill du ha dumma objekt? Walle: DL - domain layer, min problemdomän eller applikationsdomän. Regler och validering för hur man får jobba i systemet. Walle: DTOs: Hej! Kalle, Patrik, Patrik, Johan, Johan, Patrik. Mats, Bollplank: DDD
Det innebär också att jag försöker bygga in en O/R Mapper (Paul Wilsons) i mina projekt.
Och då självklart så försöker jag implementera båda dessa "saker" i projektet.
Så kom igen, bolla lite med mig. Jag vet att Johan Normén är lyrisk över DDD. ;)
Till att börja med så tänkte jag dra lite snabbt hur jag tänker mig att min arkitektur ska se ut.
Har en Manager Model istället för Domain Model. Dvs. mina objekt ska vara "dumma". De ska endast innehålla data och properties. Detta innebär att all logik hamnar i Manager klasser (Repositories). Det är Repository-klasserna som i sin tur skall använda O/R Mappern för persistance och kontakt med databasen.
Skulle även gå att lägga in Factories som Repository får anropa och så är det Factory-klassen som anropar O/R Mappern. Får se, det kanske blir ett onödigt steg.
Flödet skulle då kunna se ut så här:
1. I ASP.NET sidans code-behind skapas en instans av CustomerRepository
2. En instans av Customer-klassen skapas genom CustomerRepository.Create()
3. Data läggs in i Customer-objektet
4. Objektet skickas in i CustomerRepository.Save(myCustomer) som i sin tur lägger in det i databasen genom att skicka objektet till O/R Mappern.
Ska vi börja där och se vart vi hamnar?Sv: Bollplank: DDD
Men det finns några problem. Bland annat ett som diskuteras i ett antal bloggar. Och det är hur man gör med aggregates. Om jag har en Customer som har ett Address objekt. Jag ändrar i både Customer och Address och sen sparar. Då måste Repositorien se till att Address objektet sparas också.
Då kollade jag lite hur Wilsons OR Mapper hanterar child-entiteter och den ska fixa detta, med andra ord sparas Address-objektet när jag sparar Customer.
MEN, ett litet krux dyker då upp. Eftersom OR Mapperns ObjectSpace skapas i CustomerRepository, skickar ut ett Customer objekt till den anropande sidan som ändrar och sen skickar tillbaka den till CustomerRepository.Save för att spara så funkar det inte. Det ena är att Entiteten inte spåras. Förmodligen för att det inte är samma objekt som skapades av OR Mappern. Det andra är att Address inte sparas, vilket förmodligen beror på samma sak.Sv: Bollplank: DDD
Några bra länkar som introducerar begreppen du använder?Sv: Bollplank: DDD
Mitt datalager består av databasen och en O/R Mapper.
I affärslagret så har jag mina domänobjekt (entiteter) samt ett ServiceLager som innehåller Repositories. Repositories är managers som hanterar all affärslogik. Martin Fowler beskriver Repository mönstret i PoEAA.
För att citera:
"[The Repository pattern] mediates the domain and data mapping layers using a collection-like interface for accessing domain objects."
Lite kodexempel:
Customer objekt
<code>
Public Class Customer
Private _iD as Int32
Private _firstName as String
Private _lastName as String
Private _email as String
Public ReadOnly Property ID() as Int32
Get
Return _iD
End Get
End Property
Public Property FirstName() as String
Get
Return _firstName
End Get
Set(ByVal value As String)
_firstName = value
End Set
End Property
Public Property LastName() as String
Get
Return _lastName
End Get
Set(ByVal value As String)
_lastName = value
End Set
End Property
Public Property Email() as String
Get
Return _email
End Get
Set(ByVal value As String)
_email = value
End Set
End Property
End Class
</code>
CustomerRepository
<code>
Public Class CustomerRepository
Inherits DomainRepository
Public Sub New()
MyBase.New()
End Sub
Public Function Create() As Customer
Dim nCustomer As Customer = DirectCast(Manager.GetObject(GetType(Customer)), Customer)
Manager.StartTracking(nCustomer, Wilson.ORMapper.InitialState.Inserted)
Return nCustomer
End Function
Public Function FindByID(ByVal id As Integer) As Customer
Return DirectCast(Manager.GetObject(GetType(Customer), id), Customer)
End Function
Public Function Save(ByVal nCustomer As Customer) As Boolean
Manager.PersistChanges(nCustomer)
Return True
End Function
Public Function Delete(ByVal nCustomer As Customer) As Boolean
Manager.MarkForDeletion(nCustomer)
Manager.PersistChanges(nCustomer)
Return True
End Function
End Class
</code>
DomainRepository som klassen ärver är en grundklass där själva O/R managern initialiseras.
Sen kan man då lägga till en Factory, en klass som bara har till uppgift att skapa objekt. Men eftersom O/R Mappern gör detta så känns det som att O/R Mappern "är" en Factory. Vilket gör det lite onödigt.Sv: Bollplank: DDD
Factories i en Repository baseras inte enbart på att "skapa" entiteterna (Aggregate Roots) utan även andra Repositories. Det är en massa filosofi ang hur saker o ting skall hanteras.
Repositories sköter i regel CRUD (create, Read, Update, Delete) med eller utan O/R Mapper. En O/R Mapper är nått som existerar i Infrastruktur lagret likså Data Access helper om man inte nyttjar O/R Mappers.
Repositories är på sätt o vis wrappers mellan datakälla och Domain objekt (Entiteterna eller Value Ojects.) Man kan antingen gå direkt mot db skapa Entetietn i Repositoryn eller nyttja O/R mapper som gör detta åt en för att minimera kod. Ta ditt exempel med Customer och Address. Låt säga (vanligtvis inte fallet) att din Address är ett Value Object till Customer som är en aggregate root. Du använder då CustomerRepository för att bygga upp din Customer, CustomerRepository i sig använder sig av AddressRepository för att bygga upp Address. (detta är den lilla luriga biten som kan lösas på många sätt.) Vanligtvis kan en smart o bra O/R Mapper ladda in både Customer och Address. Men låt säga att du inte baserar dina Repositories med O/R Mappers utan fyller dem själv med egen kod (så som man alltid gjort utan O/R Mapper, customer.Name = reader["name"].ToString() ) Då kan man använda Factories för att skapa upp de olika Repositories man skall använda sig av i sin Main Repository (eller vad vi skall kalla den. ) Är du med?
CustomerRepository skapar Customer samma metod som skapar Customer har en Facotry som skapar AddressRepository för Address classen. I mina lösningar har jag en FactoryRepository som skapar upp alla mina Repositories.
Men strunta i detta nu, för det blir bara rörigt då DDD är rätt stort.
Du säger att du vill ha Business logik i dina Repositories, detta är fel, de skall inte placeras där. Om du nu syftar på det som Microsoft la i sitt så kallade BOL (Business Object Layer). Utan denna Business logik skall placerar i Domain Layer och till stor del i entiteterna själva, men vissa regler kan även läggas i Repositories baserat på Specification klasser. (http://www.nsquared2.net/johan/viewpost.aspx?PostID=126&showfeedback=true)
Entiteterna skall vara smartare än icke smarta. De skall inte bygga upp sig själva (så vida Lazy Load inte är ett krav. http://www.nsquared2.net/johan/viewpost.aspx?PostID=111) utan ha viss affärs logik. Exempelvis kan en Invoice ha en metod som kollar om ens Invoice är förfallen.
boolean isDelinquent(Date currentDate) m.m.
Jag har nämt Application Layer och Domian Layer samt Infrastruktur Layer. Dessa är tre av fyra lager i DDD. Det fjärde är Presentation Layer. Alla lager pratar mer eller mindre med varandra. Pres med App som styr applikationen och flöden. Domian som innehåller kärnan för Business verksamheten i ens produkt. Infrastrukturen är den del som vi mer ser som hjälpklasser, ramverket.
Pres lagret kan använda Entiteter för att visa data på så vis pratar Pres med Domain. Pres styrs av Applagret som i sin tur nyttjar Domain lagret. Pres och Applagret nyttjar även Infrastrukturlagret med hjälpklasser ex navigerare eller liknande. Domain lagret nyttjar oxå Infrastruktur lagret som kan ha data access klasser m.m. Ett datalager finns inte riktigt på samma sätt som I MS modeller utan är den del av Infrastrukturen och delas med hjälp Av abstrakta datakällor. (Om man nu nyttjar sådan teknik.)
Detta var lite kort om DDD och dess uppbyggnad. Självklart finns det mkt mkt mer att säga och flera olika lösningar och tanker kring DDD som jag inte tagit med här och som ni säkert även har synpunkter på.
Mvh JohanSv: Bollplank: DDD
Jo, jag har insett att jag är lite snett ute om man ser det från DDD:s synvinkel. Anledningen till detta är att jag istället för att ha en "rich" Domain Model har varit insnöad på att ha en Manager Model. Detta eftersom jag inte vill ha tunga objekt fyllda med logik. Det blir bara komplicerat om jag behöver generera om koden för mina objekt.
Men jag har förstått att DDD använder sig av en rik Domain Model, dock ej vad jag varit inne på tidigare, så jag hamnar väl där till slut.
Sen har jag alltid varit lite avig till att ta på mig ett koncept till 100%. Jag har nog aldrig hittat en lösning där allt känns som att det funkar. Så det brukar leda till att de delar som jag känner inte fungerar för mig ersätts av något från något annat koncept som känns mer rätt. Ibland blir det en bra lösning, men det är klart att det kan bli så att jag skjuter mig själv i sank. Får väl se var jag hamnar den här gången. ;)
En av anledningarna till att jag helst inte vill ha logik i mina entiteter är produktivitet. Jag genererar koden för alla objekt och om jag lägger in logik och sedan gör en ändring, genererar om, så måste jag se till att all logik kommer med.
Sen känns det som att mycket alltid handlar om semantik. Om någon kommer upp med en ny metodik så ska det vara nya namn, för att det finns vissa defintionsskillnader, annars är det ju samma sak som tidigare.
Om vi tar t.ex. Business Object Layer så innehåller det affärslogik och objekt.
Om vi tar Domain Layer så innehåller det affärslogik och objekt.
Visst, definitionerna skiljer åt om man går in på detaljer men i grund och botten är båda helt enkelt ett affärslager.
I DDD har man sedan lagt på ett Service Layer.
Om man ser det ur Manager Model och Domain Model synpunkt så har vi i DDD en blandning av de två. Use-Case scenarion ligger i Application Layer och affärslogik och objekt ligger i Domain Layer. Eftersom det finns logik i entiterna blir det en någorlunda rik domänmodell.
Mats Helander skrev en rätt bra kommentar på http://weblogs.asp.net/fbouma/archive/2004/03/06/85105.aspx om detta. Observera att dessa inlägg inte handlar om DDD utan om Manager vs. Domain Model.
Så om man bygger lösningen med ovanstående lager men flyttar ut affärslogiken till själva Use-Case scenariona så frångår vi DDD litegrann och går istället mot en renare Manager Model. Om vi går upp ett steg i detalj och inte bryr oss om var logiken ligger så ser det ut så här.
Infrastructure
O/R Mapper
Domain Layer
Entiteter och Repositories
Application Layer
Use-Case scenarion
Presentation Layer
Då har vi en DDD struktur. Om vi från PL använder våra Use-Case scenarion ur Application Layer så kan vi sömlöst flytta affärslogiken mellan Use-Case scenariot och entiteten utan att påverka applikationen ur användarsynpunkt.
För mig så känns det rätt bra att ha den valmöjligheten.
Men som sagt, det är inte DDD längre.
Sen får vi se om det är en sådan arkitektur jag kommer ha. Ska ju dels läsa klart DDD-boken, testa lite och så ska jag på seminarie 25 november på Dataföreningen. Jimmy Nilsson ska prata om DDD med .NET. Det ska bli intressant.
I vilket fall som helst så är det kul att diskutera olika scenarion där olika metodiker blandas in. Design är kul och man lär sig mycket genom att prova lite olika grepp. :)Sv: Bollplank: DDD
"En av anledningarna till att jag helst inte vill ha logik i mina entiteter är produktivitet. Jag genererar koden för alla objekt och om jag lägger in logik och sedan gör en ändring, genererar om, så måste jag se till att all logik kommer med. "
Detta är just en anledning till att jag själv inte gillar det jag kallar för Table Driven Design.
http://www.nsquared2.net/johan/viewpost.aspx?PostID=119&showfeedback=true
Jag verkligen vrider mig av smärta när jag ser att många speglar sin db till objekt. Detta för att en DB är inte OO den har relationer. Tabeller i en DB är i en DDD mer som ett minne. i DDD bryr man sig inte om hur datakällan ser ut eller vilken datakälla man har utan det är mer hur objekten lever som är det viktiga. Se det så här. När du bygger upp din DM låtsas då att din dator aldrig kan gå sönder, att objekten hela tiden lever i datorns minne. Sedan kommer nästa steg det är att göra (backup) eller vad man skall kalla det ang dessa objekt. Du använder en datakälla för att skydda dig mot problem som strömavbrott m.m. Ens DM bör inte spegla en data källa, på ett eller annat sätt blir du helt plötsligt mer beroende av datakällan än din DM. Det kan lätt skapas begränsningar och problematik.
Som du även påpekar så blir det problem med att generare sina DTOs då ev logik försvinner, samma problem blir det om du ändrar datatyper och genererar om (där databasen ev satte ett krav för din applikation och inte tvärt om.) då måste du ev bygga om alla dina Handlers/Managers så de klarar den nya datatypen etc... Det fina med Repositories är att du kan enklet ändra DB typ i databasen men behålla din gamla typ i dina Domain Object och låta din Repositories wrappa om datan.
Jag har gillat idén att generera entiteter från DB men detta för att det går snabbt och smidigt. Men aldrig gillat arbetssättet då det blir en avig DM och en annan constraint mellan ens Sotires/Use cases och databasen. För det är ens DM som berättar hur Datakällan skall se ut och nä rman har sin DM bygger man sin datakälla och inte tvärt om.
Jag vet inte varför man envisas med att oftast bygga db först. Om det härstammar från hur vi lärt oss tänka i skolan? eller om det bara är så att vi väljer det som en enkel väg?
Tänk dig när du bygger ett hur, först ritar du upp huset baserat på krav. Ens DM. Sedan delar man upp bygget i itterationer där man efter sin DM vet hur grunden skall se ut o vart alla rören skall dras. Samma med applikations bygge.
Rita upp en DM baserat på krav, skapa itterationer. När man vet sin DM baserat på kraven kan man bygga grunden. Baserar du ditt byggsätt på TDD (Test Driven Development) så börjar man alltid koda classerna och logiken med hård data tills allt fungerar och är klart, sedan tar man bort den hårda datan och bygger delen som plockar in den dynamiska datan. (ex databasen, men du vet inte riktigt vilken data du vill ha i databasen innan du är klar med hur din domian skall se ut och fungera.) TDD är som bekant något som i alla case kommer kräva justeringar i ens DM då man faktiskt inte kan tänka på allt. Det kan vara så att ett problem måste generera ett visst designmönster som man inte tänkt på innan, etc...
Tid är ju även viktigt. Man kan ju bygga ett hur och sedan hacka hål i betonggrunden för att kunna dra en extra kabel eller lägga ett extra rör, men det gör att det tar mer tid och kan göra mer skada än nytta. Samma med att bygga databas först (utan att känna till sin DM och hur objekten lever med varandra.) ens DM kanske blir begränsad eller så måste man justera både DM och Databas eller bara databas när man inser att man glömt nått. Detta kan oxå ta tid och äventyra projektet. Tar man datakällan i slutet av sitt DM bygge så har man en mer klarhet vad som krävs. Med TDD kan man även nyttja Mock objects om det känns konstigt att inte få gå mot ett dataset eller en reader.
Mvh JohanSv: Bollplank: DDD
När det gäller Affärslogik så ska det ligga i Application/Service Layer (Dock beror det på vad för affärslogik det är), kan även finns i Entiteterna om logiken berör dess state. Tex räkna ut rabbatter etc.
Att enbart lägga affärslogik i applicaion layer kan leda till Transcript pattern [PoEAA] vilket jag anser ska undvika. Affärslogik som befinner sig i Application Layer kan tex vara att starta en tranasktion mellan flera repositories eller någon mer generell logic som inte får plats i domain layer. OBS! att viss gemensam logik som används av många klasser kan placerars i Infrastrukture layer, dock är inte detta affärslogik som berör själva applikationen man bygger utan mer hjälp klasser, tex data access hjälp, O/R-mappers etc.
En Repository har CRUD metoder men även metoder som tex antalet kunder etc. Repository kan använda sig av Facotries för att skapa upp instanser av ett objekt, tex en Customer med dess standard värden, factory kan oxå ha som uppgift att fylla en Customer med info utifrån en datakälla.
En arkitektur inom DDD som jag gillar är följande:
1) Presentation layer - (ASP.Net med MVC pattern tex MS PAGs UIPAB)
2) Application/Service Layer - Starta transaktioner, Service fasader etc.
3) Domain Layer - Entiteter, Repositories och Factories etc.
Om vi tex använder nHibernate som O/R-maper eller nPersist så har de en klass som bygger på Unit Of Work [PoEAA] och Indentity map. nHibernate har Sesson och nPersist Context. Dessa klasser kan oxå ingå in en transaktion och det är dessa man initierar från Application Layer och skicar in som argument till Repositories för att hantera transaktioner (om man använder O/R-mappers).
Ett ramverk som är väldigt snyggt och som fungerar som facotries är Spring.Net. Med Spring.Net så kan vi utföra Dependency Injection och kan utföra instasiering och injection som utvecklaren inte behöver tänkta på, mer än att speca detta i en XML fil. Det ger renare kod och engligt mig väldigt snyggt.
Fär er som vill läsa mer om O/R-mappers och vilka patterns de använder sig av mm kan läsa följande inlägg på ming blog:
http://fredrik.nsquared2.com/viewpost.aspx?PostID=209
Ni som är intreserade av TDD som är ett mkt bra hjälpmedel föär att skapa snygg design, kan läsa följande inlägg på min blog:
http://fredrik.nsquared2.com/viewpost.aspx?PostID=188
Sedan har vi Refactoring som gör koden lättläst och snygg, ni som är intreserade av det kan läsa detta inlägg på min blog:
http://fredrik.nsquared2.com/viewpost.aspx?PostID=200
Ni som vill se ett väldigt enkelt koncept på DDD i ASP.Net 2.0 kan läsa följande på min blog:
http://fredrik.nsquared2.com/viewpost.aspx?PostID=194
/Fredrik Normén NSQUARED2
http://fredrik.nsquared2.comSv: Bollplank: DDD
"Detta är just en anledning till att jag själv inte gillar det jag kallar för Table Driven Design.
http://www.nsquared2.net/johan/viewpost.aspx?PostID=119&showfeedback=true
Jag verkligen vrider mig av smärta när jag ser att många speglar sin db till objekt. Detta för att en DB är inte OO den har relationer."
Det finns lite olika sätt att se på det här. Som jag förstår det så ogillar du när man bygger en databas och sen skapar objekten därefter. Dvs. en tabell blir en klass vilket omöjliggör t.ex. arv.
Där håller jag med dig helt och fullt men det var inte vad jag pratade om när jag sa att jag genererar min kod.
Men så gott som alltid så vet man vilken typ av datakälla projeket kommer ha när man börjar. Så varför inte inkludera detta i tänket redan från början? På det sättet får man dels fördelen med att man kan generera sin kod, och man hamnar inte i "Table-driven design"-fällan. Då speglar datakällan ditt DM och inte tvärtom.
Om vi tar exemplet i ditt inlägg ovan så ser vi att databastabellen är exakt samma i båda fallen. Så om vi skapar databastabellen när vi designar våra klasser så kan vi snabbt generera koden för klasserna och på så sätt spara tid. Och tid är pengar vet vi väl alla. ;)
Om vi bara genererar första gången när vi skapar klasserna och så skippar vi genereringen när vi ändrar i databasen så blir vi av med problemen att logik skulle försvinna och använder oss av wrapping i våra Repositories istället. Det blir nog den lösning jag kommer att använda mig av i så fall. För den första genereringen kan spara mycket tid och som sagt, tänker man från båda hållen när man designar så blir det ingen Table-driven design.
Johan:
"Jag vet inte varför man envisas med att oftast bygga db först. Om det härstammar från hur vi lärt oss tänka i skolan? eller om det bara är så att vi väljer det som en enkel väg?"
Jag vet inte varför det är så. I mitt fall så är det inte så utan en fråga om att bygga DB samtidigt.
För att ta ditt exempel med husbyggen.
Visst, du ritar upp huset efter dina krav. MEN, du säger att du vet var rören skall dras. Det är inte riktigt sant. Du vet hur rören skall dras inne i huset men du måste ta reda på var vattenkällan är för att veta hur vattnet ska komma in i huset. Utefter det får du sedan planera hur rören skall dras från källan till husets "vattenfördelare".
Allt beror på hur man tolkar det. Ju mer information du har desto bättre lösning kan du få fram.
Det finns säkert någon som suttit och designat sitt DM och när databasen sätts ihop i slutändan så kanske det dyker upp prestandahål som kunde fixats om man tänkte på datakällan i samband med designen.
Sen måste man även vara realistisk och inse att i stora projekt så sitter du och designar ditt DM men det är en databasadministratör som bestämmer över databasen och när du kommer med ditt DM så bygger den databaslösningar som DA inte alls tycker är optimala och då lär du få ett riktigt farthinder.
Jag vill nog inte låsa in mig i några tankesätt.
Fredrik:
"Myckte intresant och kul ämne, pratade nyss med min bror Johan som sa att denna tråd fanns så jag tänkte bidra med lite information"
Japp, sånt här är kul tycker jag. Och som Johan nämnt tidigare (i min blog tror jag) så kan design, precis som konst, aldrig bli fel. I och för sig en sanning med modifikation, för tekniska fel kan det ju bli. ;)
Men kul är det i alla fall.
Fredrik:
"När det gäller Affärslogik så ska det ligga i Application/Service Layer"
Jaså? Nu tycker jag nog att du säger emot både din bror OCH DDD-boken litegrann.
Johan: "denna Business logik skall placerar i Domain Layer och till stor del i entiteterna själva, men vissa regler kan även läggas i Repositories baserat på Specification klasser."
DDD: "[The Application Layer] is kept thin. It does not contain business rules or knowledge, but only coordinates tasks and delegates work to collaborations of domain objects in the next layer down."
I och för sig, då uppstår frågan hur vi skall tolka ordet "affärslogik". För mig så innefattar ordet även affärsregler men jag kan ju ha fel.
Inget att orda om egentligen, jag tror att vi allhop ligger på samma linje ändå att affärslogiken delas upp i Application och Domain Layer.
Jag börjar i alla fall att gilla DDD skarpt så jag känner väl redan nu att viss logik kommer stoppas in i mina entiteter till slut.
Ska väl komma igång med TDD också. Problemet för mig är alltid tid. Har aldrig tid att blanda in nya koncept i utvecklingsarbetet. Tar för mycket tid som jag måste lägga på att göra klart alla projekt i tid. Men jag ska i alla fall och lyssna på Jimmy Nilsson på onsdag om TDD. Ska bli intressant.Sv: Bollplank: DDD
Allt beror på hur man tolkar det. Ju mer information du har desto bättre lösning kan du få fram. "
Det är det som är infrastrukturen. Det lagret du nyttjar för att kunna hämta vatten. etc....
Prestanda är ett problem, sant. Dock har jag aldrig vart den som hakat upp mig för mycket på prestanda. Detta för att jag en gång hade ett samtal med en duktig affärsman som sa att prestanda kan man köpa billigt men utvecklingstiden för att öka prestandan kommer att kosta så mycket mer. Tack vare detta har många av mina lösningar både klarat prestanda och vart flexiblare än vad de var innan, då jag gjorde som MS sa, satsade på prestadan. Jag var aldrig någon fan av MS DNA och kommer inte bli, men jag klandrar inte deras model heller för den passar till många projekt, men mer de prestandakrävande lösningarna. Det är oftast en biprodukt till huvudapplikationen. En Win Service, en transflormator (BizTalk) etc som sköter dessa bitar idag.
I en god DM lever objekten med varandra i minnet, datorn är snabbare än databasen på att göra beräkningar och skicka reff pekare hit och dit eller kopiera data mellan minnes blocken. Det är Oftast villket många inte riktigt kan förstå Databasen som kan bli flaskhalsen.
En join kan ibland kräva mer än två separata anrop. en bra DM kan baseras på god cache hantering av objekt. Ex Fowlers Identity Map. Access mot databas minimeras i om nyttjandet av en sådan.
Självklart kan du bygga DB när du vet hur din DM ser ut. Men att basera sina Domian Objekt efter databasen är något man bör undvika. I många fall speglar kanske inte databasens tabeler ens DM alls. Det kan vara 2 tabeler med 1 kors reff men tio olika objekt för dessa.
DDD går ut på att man inte skall bry sig om sin datakälla. Och att det är Repositories som hanterar mappningen mot den datakälla man vill ha. Kan vara, SQL, XML, flat fil vad som. Sedan blir det så tyvärr att du får DTO klasser och inte Domain klasser. DTO klasser som normalt skapas upp i Anticurroption Layer.
Dock är ju utveckling rätt vackert, för det finns inget som heter rätt eller fel. Utan mer smak och tycke. Min efarenhet och mitt sätt att tänka och bygga går alltid ut på att effektivisera till nästa gång, testa nytt för att se om jag når mitt mål snabbare och bättre och att steget efter deployment ger mig den flexibilitet och tidsparande jag vill ha. För mig har databasen vart flaskhallsen i bygget, oftast den som förstört och begränsat min Domain. Inte i version 1 av mina applikationer utan mer i version 1.2, 2.0, kund anpassningar etc... I det långa loppet... Sedan jag kopplade bort mig från att inte bry mig så mkt om det datakälla jag skall ha, har jag enkelt kunnat optimera, kundanpassa och fixa till mina applikationer efter deployment skedet på ett mycket effektivare sätt än innan. Men ingen säger ju att mitt sätt passar andra för mina tankar går kanske i en annan bana som gör det mer problematiskt än nytta. Dock säger Eric Evans att det är ens Domian som är det viktiga, Pres och Data källan är det man inte fokuserar på utan ens DM datakällan och presentationen är ett minde bekymmer, och det håller jag med om.
Mvh JohanSv: Bollplank: DDD
>>Fredrik:
>>"När det gäller Affärslogik så ska det ligga i Application/Service Layer"
>Jaså? Nu tycker jag nog att du säger emot både din bror OCH DDD-boken litegrann.
Nu kanske begreppet affärslogik är fel när jag pratar om logik som ett allämnt begrepp som att tex starta transaktioner och anropa flera repositories etc. Jag själv burkar använda affärslogik istället för applicationlogik. Jag vet inte om du såg parantesen efter "Application/Service Layer", men där gjorde jag det lite tydligare att viss affärslogik läggs på entitesnivå (kanske skulle använda rules här, fär att skilja på vilken typ av logik jag pratar om). Som du säger så i DDD står det att affärs regler inte ska finnas. Ofta ser jag olika använda affärslogik och rules som olika saker i olika sammanhang, och kanske är det jag som använder dom fel!?
När det gäller skapandet av databaser så väntar jag med det tills jag anser att jag måste ha med en database tex vid testing där jag ska spara ner data i en databas. Jag anser att det inte går att test ordentligt om man inte har en DB när man ska gå mot en DB (En databas uppför sig på sitt lilla sätt och det sättet måste ingå i testning anser jag). Jag anser att man måste ibland gå åt båda hållen, skapa UI och skapa databas och mötas i mitten. Jag vet att många arkitekter inom DDD ofta säger att man börjar med UI och skapar databasen sist eftersom applikationen man bygger kanske inte ens behöver ha en databas, det kanske går lika bra med XML när man väl anser att man behöver en datakälla av något slag. Men det jag bygger behöver oftas en databas. Jag försöker alltid skapa UI först för där ser jag vilken data som användaren behöver använda sig av. Sedan skapar jag ofta databesen eftersom en tabels kolumner stämmer ofta överrens med en entitets attribut. Efter att tabell(er/en) är skapad så kan entiteter och dess Repostitories genereras, självklart behövs det ibland modifiering efteråt av koden men det är ofta lite som behövs modifieras. Det är vikitgt att tänka på att en tabell i en RDBMS inte är ett entitet objekt och kan aldrig vara det, därför behövs det ibland modifiering av kod.
/Fredrik Normén NSQUARED2
http://fredrik.nsquared2.comSv: Bollplank: DDD
Har sett att fler rekommenderar detta men aldrig riktigt förstått varför de skall vara dumma. Förlorar man inte meningen med en objektmodell då? Vad jag menar med dumma objekt är i princip rena dto objekt som bara innehåller data (mao datasets). Om du genererar dem å på så sätt inte kan/vill lägga in logik i dem så kan du väl ärva från dessa "bas"klasser och sen implementerar logiken i de ärvad klasserna, så försvinner inte detta om du skulle vilja generera om dem. Tror LLBLGen Pro gör något sånt per default.
Styrkan som jag ser med oop el. ddd. eller vad man nu kallar det, är just att logiken finns i objekten och att varje objekt har logik som endast berör det egna objektet. Sv: Bollplank: DDD
I C# 2.0 finns partial classes så du slipper skapa basklasser med logik ;)
När du säger att objekt ska ha sin egna logik som endast berör det egna objektet. Pratar du då tex om CRUD metoder också? Om, så får du tänkate på att om du har måga beroenden mellan objekt, tex att en Entity använder Repository så blir systemet genast svårare att underhålla och du kommer då inte följa OCP och DIP principerna. Det går självklart att flytta på logiken genom att använda Proxy pattern, men det finns ändå kvar ett beroende. Vissa beroenden går iof att få bort med hjälp av AOP.
När det gäller "dumma" entiteter, så förekommer är dom ibland. Ibland så har man har så mkt logik i entiteterna och den logik som finns i en entitet ska helst bara hanterar dess egna tillstånd.
Något som jag tycker om att blanda in i DDD är Object Thinking, där man tänker som ett objekt.
Jag skulle inte säga att OOP = DDD.Sv: Bollplank: DDD
AL - Application layer, hanterar flödet, både för UI och för hur data skall laddas för DAL.
DAL - innehåller Repositories, factories. Allting som har med databasen och att skapa objekt ligger här.
AL pratar med DAL och DL. Hanterar lazyload och sånt.. rävmat..
DAL använder sig av DL's interface.
DL har inga kopplingar till varken AL eller DAL. Separeras mha av interface.
CRUD metoder tillhör inte DL i mina ögon. Dessa tillhör DAL. Jag vill inte att mina entites skall prata med DAL, DAL använder DL dock. En del har crud metoderna i dl men jag trivs inte med detta, troligtvis pga mitt val av O/R-mapper och att jag har svårt för llblgen pro. :)
Med logik menar jag, att ett namn är max 10 tecken långt, att man inte kan lägga till nya rader i en order då en order är skickad, regler för hur man får lägga till objekt, regler för hur aggregaten skall hanteras. En del av denna logiken kommer att hamna i AL ,managers(al är endast managers eller service objekt för mig) tror jag de för kallar dem i boken DDD, här lägger jag regler som är "flytande", där jag kanske behöver anropa något i databasen för att kontrollera att värdet är ok, eller där jag skulle behöva ladda alldeles för många objekt för att endast kontrollera ett värde. Jobbigt att gå genom DL då. I princip har jag exakt samma upplägg som Patrik, fast objekten innehåller mer eller mindre all logik.
Jag säger inte att det är fel att ha "dumma" objekt, jag undrar bara varför. Vilka fördelar finns det med detta? Jag bakar in en massa saker i mina objekt och ibland gör de saker som de inte borde göra. Jag tycker jag börjar få kläm på hur det skall se ut men har ingen som jag kan bolla med så är inte 100% att man tänker på rätt sätt, och som jag skrev har sett många som rekomenderar det men inte riktigt sätt nyttan med det.
tja, jag sätter nästan likehetstecken mellan OOP och DDD. http://bepp.8m.com/english/oop/about_oop.htm
Det poppar upp så många termer och uttryck som inte är så nya egentligen. Boken DDD är kanon, förklarar på ett mycket bra sätt men tänkandet är inte nytt. Något som hade varit trevlig här på pellesoft är ett sorts uppslagsverk så att man slå upp vad folk menar, för stundtals så är det väldigt snurrigt med alla termer, OCP å DIP :). En wiki kanske?Sv: Bollplank: DDD
Precis den logik du pratar om, som validering (finns även i PL "input validering") och annan logik som ser till så ingen kan påverka ett objekt tillstånd utan tillåtesle (det vill säga, ändra saker, lägga till saker etc) eller gå in bakvägen in i entitetns "data" och förändra datan utan att den är medveten om det, ska finnas i entitets objkten. Vem vill att någon annan ska gå in i våra hjärnor och helt plötsligt styra våra rörelser utan att vi har kontroll äver det ;)
Jag har inte sett någon som inte har den typ av logik i sina entiteter (alltså enbart använder entiterna som DTO), men jag har hört att många ogillar att använda entiteter enbart som DTO (transportera data mellan lager etc), med dom fyller sitt syften vid remoting.
Enligt DDD, så ingår Repositories i Domain Layer och för mig så hör dom hemma där, men att säga att dom ligger i DAL går ju, inget som säger att det är fel, men då skulle jag nog mer säga att DAL är ett extra skikt i DL och inte ett separat lager.
Många anser att DDD är den nya generationen av OOP, därför är min uppfattning av att OOP inte är DDD, även om de påminner mkt om varandra i sättet att tänka.
OCP = Open-Close principal
DIP = Dependency-Inversion Principle
Det borde finnas en Ubiquitous Language lexikon ;)
/Fredrik Normén NSQUARED2
http://fredrik.nsquared2.comSv: Bollplank: DDD
http://www.nsquared2.net/johan/viewpost.aspx?PostID=127&showfeedback=true
DTOs är så kallat dumma entiteter, entiteter utan logikik, :-) man använder dessa för remoting. Ett smidigt sätt är att använda sig av Anticorrution Layer, där du wrappar om dina Entiteter till DTOs för remoting. Anticorruption layer styr även interfaces och baseras på piplines m.m. Lite som Shadowfax.
Internet ---> Anticorruption Layer ---> Pipelines/Dto ---> Domain --> source
Mvh JohanSv: Bollplank: DDD
Jag reagerade på ett uttalande från dig Johan:
"Det är Oftast villket många inte riktigt kan förstå Databasen som kan bli flaskhalsen. "
Eftersom jag har jobbat väldigt mycket med databaser (allt från design till administration), så tycker jag att detta är intressant.
Av alla prestanda- och integritetsproblem som jag har sett genom åren så är det i särklass vanligaste att utvecklare som är jätteduktiga på sitt språk (vilket det nu är) ger sig på att konstruera en databas för persistance utifrån sin objektmodell eller liknande, och struntar fullständigt i att läsa på om databasdesign.
Detta resulterar ofta i dåligt konstruerade databaser som är långsamma att både uppdatera och att hämta data ifrån. Mycket vanligt är att man har en onödig mängd databasanrop (round-trips).
Det som är mest skrämmande är att jag har sett mängder med exempel på detta i kommersiell programvara inom den bransch där jag har jobbat de senaste åtta åren, finansbranschen. Dessa ogenomtänkta databaser skapar enorma mängder redundant information och rena integritetsproblem, som man senare får lägga ner enorma resurser på att reda ut.
Jag tycker därför att ett viktigt tillägg till diskussionen om arkitektur är att man inte bara tänker på databasen som en "låda" utan att man även lägger ner lite tankemöda på databasdesignen.
/KalleSv: Bollplank: DDD
Hej, jo visst är det så... Dock ör det många databas byggare som oxå precis som många kodare bygger en massa struntsaker eller anvädner mönster på fel sätt. Har sett Db folk som är duktiga har cert o allt som indexerar som tokar och föredrar blobbar i mängder som tom uppmanar folk att spara bilder i databasen m.m. Jag har sett SPs och SQL satser med joinar ändå till kina typ. Och självklar sett spagetti kod i mängder...
Nackdelen med en Mappare mot DB är just de dynamiska frågorna som byggs upp. Om jag fattat allt rätt kan man även optimera sql frågorna så man tar tabeller i viss ordning som databasen läser in olika fort, eller hanterar olika fort, detta klarar inte en O/R Mapper idag. Har inte sett någon som är så smart i alla fall. Så den flaskhalsen får man lösa med cache hantering och eller skala ut eller upp.
Tänk på att en DB är som en länkadlista med en massa objekt i en oehört stor fil på hårddisken. Så att läsa i den är inte alltid så effektivt. Att då göra flera simpla anrop kan ibland eller i väldigt många fall gå snabbare än att dra till med en massa joins. Och eftersom en bra Mapper kör med identity map som är en cache av redan inlästa objekt kommer läsandet mot DBn att minska oehört. I applikationer där de inte kör så mkt Objekt utan mer Data sets (urga) eller readers rak av genom lagren gör en access mot db:n vid varje anrop.
Det är även så att databasen missbrukas och man placerar affärslogik i stored procedurerna.. AJa baja... och det är inte direkt effektivt och kan även där ställa till med prestandaproblem som objekt i datorns minnse skulle klara av att räkna på. Det är datorns uppgift.
Men visst, vet man hur ens Mappare går mot en db bör man ju optimera där så gott det går med bra och smarta indexeringar där det behövs, minimerandet av blobs m.m.
Mvh JohanSv: Bollplank: DDD
"Men det finns några problem. Bland annat ett som diskuteras i ett antal bloggar. Och det är hur man gör med aggregates. Om jag har en Customer som har ett Address objekt. Jag ändrar i både Customer och Address och sen sparar. Då måste Repositorien se till att Address objektet sparas också."
Detta är exakt på pricken anledningen till att jag inte använder Repositories alls. Hela domänmodellen hänger ju i allmänhet ihop. Ok det kan finnas "öar" av klasser - där skulle man kunna tänka sig ett Repository per "ö", men, nja, jag ser knappast någon vits med det.
Så jag föredrar faktiska Manager-klasser och överlåter hela Identity Map hanteringen till O/R Mapparen.
/MatsSv: Bollplank: DDD
Har oxå lite att säga här ;)
"Men det finns några problem. Bland annat ett som diskuteras i ett antal bloggar. Och det är hur man gör med aggregates. Om jag har en Customer som har ett Address objekt. Jag ändrar i både Customer och Address och sen sparar. Då måste Repositorien se till att Address objektet sparas också."
Lite så här ser man på det hela. OBS! undantag finns alltid i hur man vill lösa saker o ting men...
Låt säga (nu ljuger jag... men i alla fall...) att Customer är ett aggregat root och Address ett aggregat till Customer där Address.
Enligt DDD skall du nu inte kunna få just den addressen via Address utan måste be Customer om den.
Metafor:
Jag sitter i en bil. Jag är ett aggregat till bilen. För att prata med mig måste man gå via bilen så den öppnar dörren och plockar ut mig. Man kan inte prata med mig direkt då bilen är i vägen.
...ja i alla fall... Customer har en CustomerRepository med CRUD funktionalitet. Då Address är en del av Customer och ev behöver hanteras så sker detta i CustomerRepository. Om Address har en egen AddressRepository är denna inkapslad i CustomerRepository. Sedan kan man utan problem använda O/R Mappers hit o dit. Om man så vill att O/R Mappern tar hand om Address så behövs det ex ingen AddressRepository...
Typ så...
Mvh JohanSv: Bollplank: DDD
Ibland tycker jag att granulariteten i min domänmodell är högre än den behöver vara i mitt Service-layer, och kan då låta en Manager-klass ansvara för flera klasser ur domänmodellen.
T ex kan jag i domänmodellen ha Forum, Thread och Post klasser, men i Service lagret bara en ForumManager klass, som även har metoder för att skapa och hantera trådar och poster i ett forum.
Ofta - som i detta fall- går det bäst när det gäller rena aggregat. Jag ser heller ingen större inkompabilitet mellan våra tillvägagångssätt - jag tror att jag skulle kunna byta namn från "Manager" till "Repository" och stå med ungefär samma lösning som du - med en skillnad /tror jag - men jag är ju inte säker på hur du gör här riktigt :-)
Jag låter inte mina Manager/Repository klasser ansvara för att hålla i den för Identity Mapen så nödvändiga objekt-cachen. Jag använder en samlad objekt cache/Identity Map för hella domänmodellen, och den bor i O/R Mapparen, inte i manager-klasserna, just för att undvika problemet med relationer mellan´klasser från olika Repositories.
Om t ex varje repository skapar sin egen Session/Context/Workspace/ObjectSpace/ObjectScope eller vad man än vill kalla det (dvs en instans av O/R mapparen, kort och gott) så /får/ man problem med det faktum att alla klasser i domänmodellen i allmänhet hänger ihop med varandra (precis som tabellerna i databasen).
/MatsSv: Bollplank: DDD
att kapsla in en Repository i en annan känns inte så vidare bra. Det här problemet har som sagt diskuterats en hel del i olika bloggar och jag måste säga att den trevligaste lösningen jag stött på använder Proxies.
Läs det här inlägget och särskilt Deyan Petrovs kommentar, http://dotnetjunkies.com/WebLog/seichert/archive/2004/10/03/27486.aspx
Titta även på det här inlägget, http://hyperthink.net/blog/CommentView,guid,279c7378-8f34-4fe7-b5a1-f15d8086098e.aspxSv: Bollplank: DDD
"att kapsla in en Repository i en annan känns inte så vidare bra. Det här problemet har som sagt diskuterats en hel del i olika bloggar och jag måste säga att den trevligaste lösningen jag stött på använder Proxies. "
Precis... Jag skrev i mitt inlägg.
"Lite så här ser man på det hela. OBS! undantag finns alltid i hur man vill lösa saker o ting men... "
För att på ett sätt förklara att det finns undantag. Bygger du ex efter XP metodiken så har du troligen ingen nytta av proxies, dessa bygger man för att skapa en transparant väg mellan Repositories.
Själv kör jag med en RepositoryFactory som kör under IoC (Inversion of control)
Men som jag även påpekat så finns det inget "Det rätta sättet". Utan allt baseras på tid och krav.
Skrev lite lite om detta... OBS! Mer om Lazy Load men lite annat ingår. här:
http://www.nsquared2.net/johan/viewpost.aspx?PostID=111
Men återigen beror det som sagt på kraven. Hur jag skulle göra i ett projekt skulle på så vis variera.
mvh JohanSv: Bollplank: DDD
Ok. Dock Ligger repositories mot datakällan, längst ner i Domain lagren. Dina managers var fasader eller services i över lager va?
Just repositories betyder väl behållare eller nått? I DDD boken står det inget spec om att Rep... nyttjar O/R Mapper, utan Rep... kan även vara en O/R Mapper... Båda har i uppgift att sköta tolkningen och skapandet av entiteter. Först kände jag att Repositories var onödiga då en O/R Mapper är mer global och transparant än repositories... Sedan kände jag lite att Repositories kunde öka spårbarheten och förståelsen av domain modellen... Där de i sin tur nyttjade O/R Mappern...
Men som vanligt är det ju fritt fram att göra som man känner är bäst. Finns inget direkt, så här måste du göra...
Mvh Johan