2009-04-21 10 views
22

Quindi ho questa cosa generica DAO in corso e al valore nominale sembra essere ok. È fondamentalmente modellato dopo l'applicazione di esempio CaveatEmptor dei ragazzi di Hibernate.Pattern DAO: dove rientrano le transazioni?

Oltre a questo ho un livello aziendale ... il coraggio dell'applicazione. È completamente inconsapevole di qualsiasi implementazione DAO specifica.

Tutto a questo punto sembra soddisfacente, finché non comincio a pensare alle transazioni. Se le transazioni sono lasciate al cliente da implementare, allora in che modo nel mondo mantengo la bella separazione che ho avuto tra i miei strati? Cioè, sto usando Hibernate al momento, e non mi sembra molto di aggiungere transazioni specifiche per l'ibernazione al mio codice del livello aziendale.

ho potuto creare una semplice interfaccia transazione con iniziare, commit e rollback metodi e passare un'implementazione al mio livello di business ... ma ... non sono sicuro ...

Così qui è la sfida: mi puoi consigliare un modo per farlo senza usare la parola Spring (o EJB, o qualsiasi altro framework aggiuntivo)?

risposta

12

Ricordo che i consigli Martin Fowler mantengono il controllo della transazione nel livello aziendale perché la transazione è un problema aziendale. (Se si progetta una classe BankAccount, una transazione fa parte della lingua del dominio).

Si può cercare di implementare un TransactionScope come in .NET funziona qualcosa di simile

using (TransactionScope ts = new TransactionScope()) 
{ 
    ... 
} 

E 'la stessa cosa che (non esattamente, ma se sei un ragazzo Java, è più esplicito a voi)

TransactionScope scope = new TransactionScope(); 
try 
{ 
... 
scope.Commit(); 
} 
catch(Exception ex) 
{ 
    scope.Rollback(); 
    throw; 
} 

disaccoppiare le nostre livello di business da eventuali tecnologie di DAO è possibile aggiungere un TransactionFactory nella tua lingua dominio, che restituiscono un ITransactionScope (un'interfaccia), che è stato definito con un commit e rollback metodi. In questo modo il tuo livello di dominio non è legato al tuo livello DAO, solo una concreta implementazione di TransactionFactory è.

ITransactionScope scope = transactionFactory.CreateTransaction(); 
try 
{ 
... 
scope.Commit(); 
} 
catch(Exception ex) 
{ 
    scope.Rollback(); 
    throw; 
} 
+0

Bene, questo funziona, ma si finisce rapidamente con migliaia di linee di codice duplicate, per qualsiasi app di business realistica. –

+0

@Rogerio, non tutti i metodi in un oggetto business sono necessariamente una transazione. Se si finisce con la duplicazione, allora forse il tuo dominio non è ben espresso, oppure puoi trovare un modo migliore per esprimerlo. –

+3

Ho appena riletto le pagine pertinenti sulle transazioni nel libro PoEAA (71-77), e l'autore non consiglia alcuna cosa sulle transazioni che sono un problema di business (sono * non * parte della lingua aziendale, ma uno strumento per * controllo della concorrenza *). Inoltre, in pratica, quasi tutti i metodi coinvolti in un'operazione commerciale * devono * essere eseguiti nel contesto di una transazione di sistema, anche se in genere non si dispone di una transazione separata per ciascun metodo. Il punto rimane, un'applicazione ben progettata dovrebbe * non * avere un codice di demarcazione delle transazioni esplicito ovunque, ma solo in uno o due punti centrali. –

1

Il tuo diritto è che l'applicazione è un buon posto per ordinare le transazioni in quanto ciò consente la composizione di azioni più complesse implementate da vari servizi/gestori/o qualsiasi altra cosa tu voglia chiamarle.

Una soluzione semplice consiste nel definire un'interfaccia ITransaction e utilizzare un tipo di factory o DI per nascondere l'implementatore ITransaction effettivo dall'applicazione. Ho eseguito il rollover in questo modo in .net usando nHibernate ed essenzialmente ho una classe base che tutti i miei manager (un manager in questo caso contiene la logica di business per un insieme logico di entità come Membership, Order che potrebbe utilizzare uno o più repository). La mia classe base ha un ITransaction BeginTransaction(), che crea dinamicamente un tipo basato su un file di configurazione.

Questa classe quindi funziona con la sessione di nibernato per iniziare e registrare le transazioni.

1

In passato ho inserito la logica di transazione nel DAO radice per una gerarchia di DAO che corrisponde a una gerarchia di oggetti nel modello che rappresentano un'unica entità solida nel sistema.

Cioè, se hai e X che ha molti Y, e vuoi memorizzare e recuperare Xs e il loro Y allo stesso tempo di un singolo oggetto composto, allora il tuo DAO per X dovrebbe anche chiamare DAO per Y. Quindi puoi inserire una transazione su tutto nei metodi add() e update() nel DAO per X - e persino rendere privato il pacchetto Y DAO per nasconderlo dalla tua logica aziendale principale. Vale a dire, invece della logica di business:

XDAO xDAO = new XDAO(conn); 
xDAO.startTransaction(); 
boolean success = xDAO.add(x); 
if (success) 
    for (Y y : x.getYs()) { 
     success = YDAO.add(y); 
     if (!success) break; 
    } 
if (success) 
    xDAO.commit(); 
else 
    xDAO.rollback(); 

Si potrebbe solo avere:

XDAO xDAO = new XDAO(conn); 
xDAO.add(x); 

(con il successo/commit/rollback logica interna che DAO)

Tuttavia questo non lo fa copre ogni situazione, e la tua può essere diversa (per esempio il mio funziona con JDBC, non so come funziona Hibernate o se è possibile lì).

6

In una web app, quello che faccio per delimitare le operazioni è quello di sfruttare il ciclo di richiesta/risposta HTTP, dove ogni operazione di business atomico eseguito nell'ambito di uno di questi cicli, in un unico thread dedicato .

Qualsiasi sia il framework Web utilizzato (Struts, JSF, GWT, ecc.), Esiste in genere una "cucitura" in cui è possibile eseguire la demarcazione delle transazioni. In Struts, può essere una classe Action di base. In GWT, può essere una classe RemoteServiceImpl di base.

Quindi, utilizzare quel punto di accesso centrale per aprire la transazione (prima di consentire l'esecuzione del codice specifico dell'applicazione) e terminarlo con un commit se non sono state generate eccezioni o un rollback in altro modo (dopo l'applicazione specifica codice è stato eseguito).

Ho applicato questa strategia ampiamente in un'applicazione web aziendale ampia e complessa e ha dimostrato di funzionare molto bene.

3

Forse è un po 'troppo tardi per una risposta, ma che ne direste di creare un'altra classe per transazioni specifiche, che si trova tra il livello aziendale e il livello dao? Per esempio. se i metodi a() e b() di un DAO devono essere eseguiti in una transazione per un determinato metodo di business foo(), quindi creare qualcosa come fooInTransaction() che avvia una transazione e chiama a() e b() in esso . Il metodo commerciale foo() lo delega.

Ciò manterrà il codice aziendale pulito e la duplicazione può essere evitata mediante il ri-factoring.