Vad tycker ni om att göra domänentitetsfactories publika? Är det inte bättre att nå dessa factories via repositoryn? Detta för att bla minska beroendeförhållanden mellan tex klient och domänlagret. Nå factories via repositoryn? Hu? Nu är du nog ute på hal is :) Har läst boken men det var ett tag sedan nu men jag tycker inte boken/böckerna är så självklara i alla lägen. Det är väldigt många praktiska bitar som jag tycker saknas :) Det är normalt att klientkoden som anropar en DDD factory ingår i Application layer, och det är inte meningen att man skall ta bort beroendet från Application layer till Domain layer factories. -> Thomas Om du har boken så ser du i diagram 6.12 (sid 138) att klienten anropar factory direkt utan att gå via en repository. Jag glömde förresten att skicka med id till det existerande aggregatet som skapades från repository. Anledningen för att jag inte vill ha publika factories är för att man tex i code-behinden inte ska kunna anropa en factory. Att denna möjlighet finns tycker jag inte är bra. Jag vill minimera vägarna in i domänlagret och därför tycker jag att man bör gå mot factorien via repositoryn antingen via en create-metod eller genom någon metod för datafyllning (typ som din metod ovan findById). "Att denna möjlighet finns tycker jag inte är bra. "Publika entitetsfactories eller åtkomst via repositoryn?
Sv: Publika entitetsfactories eller åtkomst via repositoryn?
Krister du måste läsa boken... ;-)
Facrtories är ju till för att skapa upp dina objekt som exempelvis kan kräva lite för mkt kod för att instansieras så man slipper skriva samma sak om och om igen.
Dessa factories kan i ofs användas i servcie,repository klasserna. Så vist skall de vara publika.
mvh JohanSv:Publika entitetsfactories eller åtkomst via repositoryn?
Men kan du ta upp ett exempel där du använder factorien i klientdelen så kan vi resonera kring det.Sv: Publika entitetsfactories eller åtkomst via repositoryn?
Det är inte bara beroenden (low coupling) som man skall ta hänsyn till, utan även high cohesion ("hög sammanhållning/fokusering" inom klasserna).
En Factory är fokuserad på att skapa objekt-strukturer (s.k. DDD Aggregates) med hjälp av inparametrar av simplare typer (t.ex. strängar och boolska värden som skickas vidare från GUI-kontroller via Application layer).
En Repository är fokuserad på att hantera läsningen och skrivningen från/till ett persistent medium (vanligtvis en RDB).
Från en query-metod i ett Repository kan man visserligen vilja återanvända en factory-metod som hjälper till att bygga upp objekt-strukturen i aggregatet, istället för att duplicera liknande kod i Repository-implementationen, men
då en repository i det fallet anropar en factory så har den också själv tillfört något först, dvs den har från databasen hämtat de värden som den sedan skickar vidare till en factory metod.
Om man däremot (som du verkar föreslå) förhindrar publik åtkomst till factory från application layer, och istället placerar skapa-metoderna i en repository som sedan delegerar vidare till motsvarande factory-metoder (som då alltså skulle tillåta åtkomst från repository men inte från application layer, t.ex. genom användning av Java package access levels) så skulle inte repository-koden tillföra något själv utan endast delegera vidare, vilket är ganska meningslöst.
I så fall vore det nog t.o.m. bättre (alltså i jämförelse till 100% delegering, utan att tillföras något eget ansvar) att flytta in skapande-koden från factory till repository-implementationen och inte alls använda factory.
Det kan man förstås göra, men då kör man sitt eget race och har frångått en uttalad princip i DDD, nämligen att, citat: "The FACTORY makes new objects; the REPOSITORY finds old objects." (sid. 157 i Repository-avsnittet "The Relationship with FACTORIES")
Jag kan även passa på att bidra med ett tips som kan vara av intresse för alla svenskar som är intresserade av att diskutera Domain-Driven Design.
Ni känner antagligen till den internationella yahoo-diskussionsgruppen som har funnits i flera år, men det finns också en svensk google-group (se URL nedan) som är lite färskare (skapades i april) och med tanke på relativt få inlägg det första halvåret så är gruppen kanske tämligen okänd hittills, men sedan exempelapplikationen "DDD Sample Application" lanserades i oktober så har frekvensen på inläggen ökat en aning.
http://groups.google.com/group/dddsverige
http://dddsample.sourceforge.net/
/ TomasSv:Publika entitetsfactories eller åtkomst via repositoryn?
Tack för ditt utförligare svar och tips om DDD-gruppen. Ska ta en titt där.
Du skrev bland annat: "Det är normalt att klienkoden som anropar en DDD factory ingår i Application layer, och det är inte meningen att man skall ta bort beroendet från Application layer till Domain layer factories."
Kan du ge ett konkret enkelt kodexempel på detta så kan vi diskutera vidare detta i mer detalj hur jag menar.Sv: Publika entitetsfactories eller åtkomst via repositoryn?
På sidan 137 kan du också läsa att klienten (till en factory) kan vara application layer.
Problemet med enkla kodexempel är att det blir svårt att illustrera något vettigt eftersom det är meningen att factories främst ska användas för lite mer komplicerade strukturer eller om man vill skapa en GoF Factory method, dvs kunna returnera olika konkreta typer medan den deklarerade returtypen är ett interface
(dvs man vill abstrahera bort beroendet till den konkreta implementationen från klienten)
Jag försöker i alla fall illustrera de saker jag nämnde tidigare med lite kod (som ej är kompilerande, delvis pseudo-java-kod):
// Factory ska innehålla t.ex. kod som man inte vill duplicera från olika klienter:
// (men även om man bara har en klient så är det ändå bra att använda en factory om koden är tillräckligt omfattande/komplex)
public class Factory {
public Aggregate createAgregate(String s1, String s2, String s3, String s4) {
Entity1 entity1 = new Entity1(s1);
Entity2 entity2 = new Entity2(s2);
ValueObject valueObject = new ValueObject(s3,s4);
return new Aggregate(entity1, entity2, valueObject);
}
}
// Ovanstående factory kan användas från Repository:
public class RepositoryImplementationWithRawJdbc implements Repository {
public Aggregate findById(int id) {
try ...
String sql = "select ...from ... inner join ... where id = ?";
Connection ...
PreparedStatement preparedStatement = ...
java.sql.ResultSet rs = preparedStatement.executeQuery();
// återanvänder koden i factory från Repository:
return factory.createAgregate(rs.getString(1), rs.getString(2), rs.getString(3), rs.getString(4));
catch ...
finally ...
close rs, PreparedStatement, Connection ...
}
}
// Ovanstående factory kan också användas från Application layer:
public class ApplicationLayerClass {
public void doSomething(String s1, String s2, String s3, String s4, [... fler parametrar som används till annat, kanske för att skapa ett annat Agregate ...]) {
...
// återanvänder koden i factory från Repository:
Aggregate aggregate = factory.createAgregate(s1, s2, s3, s4);
..
}
}
I exemplet ovan finns det alltså två klienter till factory-metoden, och om man inte skulle använda någon factory så skulle man istället instansiera de olika ingående objekten i aggregatet inifrån de olika klienterna,
men det är inte meningen att klienten ska behöva referera till de olika instanserna som måste skapas (och dupliceringen är förstås inte heller önskvärd) vilket formuleras så här i ett fetstilt avsnitt i factory-kapitelet (sid 138):
"Provide an interface that encapsulates all complex assembly and that does not require the client to reference the concrete classes of the objects being instantiated."
Factory skall även upprätthålla eventuella invarianter på objekten så att man inte skapar icke-valida objekt, men i exemplet ovan har jag inte tagit med någon validering av invariant-villkor...
Det som jag vände mig emot i din teori om att förhindra access från application layer var alltså att lägga in en create metod i repository som endast delegerar vidare till factory:
package domain;
class Factory { // OBS! här har jag ändrat till från public till package level access som inte kan accessa från ett application layer package
public Aggregate createAgregate(String s1, String s2, String s3, String s4) {
... samma kod som tidigare ...
}
}
package domain;
public class RepositoryImplementationWithRawJdbc implements Repository {
// ny meningslös metod utan eget ansvar som inte hör hemma i Repository:
public Aggregate createAgregate(String s1, String s2, String s3, String s4) {
// härifrån kan vi anropa ovanstående factory eftersom den ligger i samma package ("domain")
return factory.createAgregate(s1, s2, s3, s4);
}
...
}
package application;
public class ApplicationLayerClass {
public void doSomething(String s1, String s2, String s3, String s4, [... fler parametrar som används till annat, kanske för att skapa ett annat Agregate ...]) {
...
// återanvänder nu (indirekt) koden i Factory via Repository eftersom direct-access till factory inte tillåts med public access:
Aggregate aggregate = repository.createAgregate(s1, s2, s3, s4);
..
}
}
/ TomasSv:Publika entitetsfactories eller åtkomst via repositoryn?
Om man ska återanvända en factory från ett repository, dvs återskapa persisterade objekt m.h.a. metoder i Factory, så måste man även se till att skapa de ingående entiterna med dess ID:n (men det slipper man förstås med value objects).
Detta kan innebära rätt många parametrar till factory-metoden, som dock förstås kan överlagras med en enklare variant som anropas då man skapar ett nya objekt som ännu inte har persisterats med ID:n.
Typ så här skulle man alltså kunna göra:
public class Factory {
// factory metod som anropas för att skapa helt nya objekt som ännu inte har persisterats
public Aggregate createAgregate(String s1, String s2, String s3, String s4) {
return createAgregate(s1, s2, s3, s4, 0, 0, 0);
}
// överlagrad factory metod som bl.a. kan återanvändas från ett repository (men även från den överlagrade metoden ovan)
// för att återanvända koden som instansierar och aggregar objekt-strukturen inom aggregatet
// för att återskapa redan persisterade objekt med ID:n
public Aggregate createAgregate(String s1, String s2, String s3, String s4, int idForAggregate, int idForEntity1, int idForEntity2) {
Entity1 entity1 = new Entity1(idForEntity1, s1);
Entity2 entity2 = new Entity2(idForEntity2, s2);
ValueObject valueObject = new ValueObject(s3,s4);
return new Aggregate(idForAggregate, entity1, entity2, valueObject);
}
}
public class RepositoryImplementationWithRawJdbc implements Repository {
public Aggregate findById(int id) {
...
return factory.createAgregate(rs.getString(1), rs.getString(2), rs.getString(3), rs.getString(4), rs.getInt("idA"), rs.getInt("idE1"), rs.getInt("idE2"));
...
}
}
Ett annat alternativ är förstås att strunta i att återanvända skapandet av objekt-strukturen från factory-klassen och istället skapa liknande struktur direkt inifrån repository:
public class RepositoryImplementationWithRawJdbc implements Repository {
public Aggregate findById(int id) {
...
Entity1 entity1 = new Entity1(rs.getInt("idE1"), rs.getString(1));
Entity2 entity2 = new Entity2(rs.getInt("idE2"), rs.getString(2));
ValueObject valueObject = new ValueObject(rs.getString(3), rs.getString(4));
return new Aggregate(rs.getInt("idA"), entity1, entity2, valueObject);
...
}
}
I praktiken är det antagligen nu för tiden ovanligt med detta problem (med att instansiera strukturerna inifrån ett repository) eftersom man sällan använder rå JDBC på detta sätt och explicit skapar sina persistena objekt utan istället är det vanligare att mappa sina objekt med JPA (t.ex. Hibernate) om man använder Java, eller med någon annan ORM-teknologi.
Jag kan även nämna att Eric Evans skriver (på sid 158, i samband med ett exempel där repository återanvänder en factory, dock endast med diagram och ej med kod) att det i praktiken är sällsynt att återanvända factory från repository.
/ TomasSv: Publika entitetsfactories eller åtkomst via repositoryn?
Jag kan acceptera åtkomst till factorien via applikationslagret/servicelagret men jag tycker att priset man betalar för detta bör tänkas igenom. För om detta kräver publika factories så är jag nog inte för det. Då ser jag att applikationslagret går via tex repositoryns create-metod för att nå den factory man vill åt.
Jag ska ta och läsa de sidor du hänvisar till...Sv:Publika entitetsfactories eller åtkomst via repositoryn?
Personligen tycker jag möjligheten är ypperlig, jag skulle inte vilja ha fler "wrapper"-metoder som jag måste skriva bara för att skapa ett objekt.
Problemet som jag ser det är att man inte kan ta bort möjligheten med new() och bara kräva att man gick genom factoryn. Om jag i min codebehind, skall anropa en applikationsservices eller en factory för att få mitt objekt, spelar inte mig någon roll, men att man kan skapa samma objekt med new() där jag inte har kontroll över hur objektet skapas är betydligt värre.
- M