Gli ORM consentono la creazione di modelli di dominio avanzati?


21

Dopo aver utilizzato Hibernate sulla maggior parte dei miei progetti per circa 8 anni, sono approdato in un'azienda che ne scoraggia l'utilizzo e desidera che le applicazioni interagiscano con il DB solo tramite stored procedure.

Dopo averlo fatto per un paio di settimane, non sono stato in grado di creare un ricco modello di dominio dell'applicazione che sto iniziando a costruire e l'applicazione sembra solo uno (orribile) script transazionale.

Alcuni dei problemi che ho riscontrato sono:

  • Impossibile navigare nel grafico degli oggetti poiché le procedure memorizzate caricano solo la quantità minima di dati, il che significa che a volte abbiamo oggetti simili con campi diversi. Un esempio è: abbiamo una procedura memorizzata per recuperare tutti i dati da un cliente e un altro per recuperare le informazioni sull'account più alcuni campi dal cliente.
  • Molta parte della logica finisce nelle classi helper, quindi il codice diventa più strutturato (con entità usate come vecchie strutture C).
  • Codice di scaffolding più noioso, poiché non esiste un framework che estrae i set di risultati da una procedura memorizzata e li inserisce in un'entità.

Le mie domande sono:

  • qualcuno si è trovato in una situazione simile e non era d'accordo con l'approccio della procedura del negozio? che cosa hai fatto?
  • C'è un reale vantaggio nell'utilizzare le procedure memorizzate? a parte il punto sciocco di "nessuno può emettere una tabella di rilascio".
  • C'è un modo per creare un dominio ricco utilizzando le procedure memorizzate? So che esiste la possibilità di utilizzare AOP per iniettare DAO / repository in entità per poter navigare nel grafico degli oggetti. Non mi piace questa opzione perché è molto vicina al voodoo.

Conclusione

Innanzitutto, grazie a tutti per le risposte. La conclusione che sono arrivato è che gli ORM non consentono la creazione di modelli Rich Domain (come menzionato da alcune persone), ma semplifica la quantità di lavoro (spesso ripetitivo). Quella che segue è una spiegazione più dettagliata della conclusione, ma non si basa su dati concreti.

La maggior parte delle applicazioni richiede e invia informazioni ad altri sistemi. Per fare ciò, creiamo un'astrazione nei termini del modello (ad esempio un evento aziendale) e il modello di dominio invia o riceve l'evento. L'evento di solito richiede un piccolo sottoinsieme di informazioni dal modello, ma non l'intero modello. Ad esempio in un negozio online, un gateway di pagamento richiede alcune informazioni utente e il totale per addebitare un utente, ma non richiede la cronologia degli acquisti, i prodotti disponibili e tutta la base clienti. Quindi l'evento ha un set di dati piccolo e specifico.

Se prendiamo il database di un'applicazione come un sistema esterno, allora dobbiamo creare un'astrazione che ci consenta di mappare le entità del modello di dominio al database ( come menzionato da NimChimpsky , usando un mappatore di dati). L'ovvia differenza è che ora è necessario creare una mappatura per ogni entità modello sul database (uno schema legacy o procedure memorizzate), con il dolore aggiuntivo che, poiché i due non sono sincronizzati, un'entità dominio potrebbe mappare parzialmente a un'entità database (ad es. una classe UserCredentials che contiene solo nome utente e password è mappata a una tabella Users che ha altre colonne), oppure un'entità modello di dominio potrebbe essere mappata a più di un'entità database (ad esempio se esiste un one-to- una mappatura sulla tabella, ma vogliamo tutti i dati in una sola classe).

In un'applicazione con poche entità, la quantità di lavoro extra potrebbe essere piccola se non è necessario attraversare le entità, ma aumenta quando c'è una necessità condizionale di attraversare le entità (e quindi potremmo voler implementare un tipo di 'pigro Caricamento in corso'). Man mano che un'applicazione cresce per avere più entità, questo lavoro aumenta (e ho la sensazione che aumenti in modo non lineare). La mia ipotesi qui è che non proviamo a reinventare un ORM.

Un vantaggio del trattamento del DB come un sistema esterno è che possiamo codificare situazioni in cui vogliamo che vengano eseguite 2 versioni diverse di un'applicazione, in cui ogni applicazione ha un mapping diverso. Ciò diventa più interessante nello scenario delle consegne continue alla produzione ... ma penso che ciò sia possibile anche con ORM in misura minore.

Ho intenzione di respingere l'aspetto della sicurezza, sulla base del fatto che uno sviluppatore, anche se non ha accesso al database, può ottenere la maggior parte se non tutte le informazioni memorizzate in un sistema, semplicemente iniettando codice dannoso (ad es. Non posso credere di aver dimenticato di rimuovere la riga che registra i dettagli della carta di credito dei clienti, caro signore! ).


Piccolo aggiornamento (06/06/2012)

Le procedure memorizzate (almeno in Oracle) impediscono di fare qualcosa di simile alla consegna continua con tempi di inattività pari a zero, poiché qualsiasi modifica alla struttura delle tabelle invaliderà le procedure e i trigger. Pertanto, durante il periodo di aggiornamento del DB, anche l'applicazione sarà inattiva. Oracle fornisce una soluzione per questa Redefinizione basata sull'edizione , ma i pochi DBA che ho chiesto su questa funzionalità hanno affermato che era mal implementata e non la inserivano in un DB di produzione.


Beh, ovviamente si può fare ciò che fa Hibernate e usare l'ereditarietà per la generazione di un oggetto proxy dinamica, che consentono di recuperare l'oggetto grafico. Questo è estremamente confuso con SP però: D
Max

Quindi finirei per reinventare metà dell'ibernazione, senza gli oltre 10 anni di esperienza che il team dell'ibernazione ha :).
Augusto

1
Qualsiasi DBA dovrebbe impedire l'eliminazione di determinate tabelle da parte di determinati utenti. Non dovrebbe importare come si tenta di farlo.
JeffO,

1
Puoi dare un'occhiata a Mybatis - potrebbe fornire la funzionalità di cui hai bisogno. È meno di un ORM di un framework di mappatura. Puoi scrivere SQL come preferisci e dire a Mybatis dove inserirlo nel tuo modello a oggetti. Gestirà grafici di oggetti di grandi dimensioni con più query, il che suona come la situazione che hai (un sacco di sottili procedure memorizzate).
Michael K,

1
@Augusto: mi sono trovato in una situazione simile, non per l'uso degli SP, ma per l'uso di un framework di mappatura proprietario che non supportava le relazioni con gli oggetti. Abbiamo trascorso giorni a scrivere codice che poteva essere scritto in pochi minuti usando un ORM appropriato. Non ho mai risolto quel problema.
Kevin Cline,

Risposte:


16

L'applicazione deve comunque essere modellata sui principi di progettazione basati sul dominio. Se si utilizza un ORM, dritto JDBC, chiamando SP (o altro) non dovrebbe importare . Si spera che in questo caso uno strato sottile che sottragga il tuo modello dagli SP dovrebbe fare il trucco. Come affermato da un altro poster , è necessario visualizzare gli SP e i relativi risultati come un servizio e mappare i risultati al modello di dominio.


Martijn, sono d'accordo che l'app dovrebbe essere modellata usando i principi DDD, ma il problema che sto affrontando (e per favore dimmi se c'è una soluzione !!) è che alcuni processi memorizzati restituiscono pochissime informazioni a instantiante un'entità DDD. Si prega di vedere questo commento dove ho spiegato un po 'di più sulle informazioni che le procedure memorizzate restituiscono. Potrei aggirare questo, invocando più di un proc memorizzato e, ad esempio, recuperare tutti i dettagli dell'utente e quindi invocarne un altro per recuperare tutte le informazioni sull'account, ma sembra sbagliato :).
Augusto

1
@Augusto Bene ... sei uno sviluppatore di app, quindi devi decidere se ha senso che esista un determinato oggetto con determinati campi impostati su NULL. Se ha senso (per un determinato compito, ad esempio), allora lascia che sia. In caso contrario, chiedi all'autore di SP di fornire più dati, in modo da poter creare i tuoi oggetti.
Jacek Prucia,

E aggiungendo al commento di Jacek - in realtà è perfettamente accettabile chiamare 2+ proc memorizzati, di nuovo pensali come due servizi remoti che devi chiamare per creare il tuo modello di dominio, niente di sbagliato in questo :-).
Martijn Verburg,

@Martijn: nella mia esperienza, uno strato sottile non è sufficiente. Il codice di mappatura può essere considerevolmente più lungo della logica aziendale sottostante.
Kevin Cline,

@Kevin cline - Buon punto, ho inserito 'si spera' nella risposta :-)
Martijn Verburg

5

C'è un reale vantaggio nell'utilizzare le procedure memorizzate?

Nel mondo finanziario (e nei luoghi in cui è richiesta la conformità Sarbanes-Oxley ), è necessario essere in grado di controllare i sistemi per assicurarsi che facciano quello che dovrebbero fare. In questi casi, è molto più semplice garantire la conformità quando tutto l'accesso ai dati avviene tramite procedure memorizzate. E quando tutto l'SQL ad-hoc viene rimosso, è molto più difficile nascondere le cose. Per un esempio del perché questa sarebbe una "buona cosa", vi rimando al classico documento di Ken Thompson Reflections on Trusting Trust .


sì un milione di volte sì! Devi anche assicurarti che gli utenti non possano fare nulla che non dovrebbero fare, incluso il fatto di non avere diritti diretti su tabelle e processi archiviati che aiutano enormemente.
HLGEM,

1
Lavoro per una società pubblica e siamo SOX semplicissimi. Potrebbe essere la mia scarsa conoscenza del controllo, ma non vedo la differenza tra fare il controllo a livello di DB (tramite processi memorizzati) o a livello di applicazione. Ogni applicazione dovrebbe avere il proprio schema DB e tale schema è accessibile solo dall'applicazione, piuttosto che condiviso tra applicazioni diverse.
Augusto,

collegamento interrotto ...
Alex R

@AlexR, collegamento fisso
Tangurena,

2

Le stored procedure sono molto più efficienti rispetto al codice SQL lato client. Pre-compilano SQL nel DB che gli consente anche di eseguire alcune ottimizzazioni.

Dal punto di vista architettonico, un SP restituirà i dati minimi richiesti per un'attività, il che è positivo in quanto significa che vengono trasferiti meno dati. Se hai una tale architettura, devi pensare al DB come a un servizio (pensalo come un servizio web e ogni SP è un metodo da chiamare). Non dovrebbe essere un problema lavorare con esso in questo modo, mentre un ORM ti guida nel lavorare con i dati remoti come se fosse locale, inducendoti così a introdurre problemi di prestazioni se non stai attento.

Sono stato in situazioni in cui abbiamo usato completamente SP, il DB ha fornito un'API di dati e l'abbiamo usata. Quella particolare app era su larga scala e funzionava incredibilmente bene. Non avrò niente di negativo da dire sugli SP dopo quello!

C'è un altro vantaggio: i DBA scriveranno tutte le tue query SQL per te e gestiranno felicemente tutta la gerarchia relazionale nel DB, quindi non è necessario.


3
gbjbaanb, la maggior parte di ciò che hai detto è vero per i database più vecchi. La maggior parte dei database più recenti ricompila le query abbastanza spesso per decidere quali nuove ottimizzazioni utilizzare (anche se sono prodotte in archivio). Sono d'accordo con quello che hai detto sull'utilizzo del DB come sistema esterno, ma vedo anche che molto lavoro, poiché l'applicazione possiede il database ed entrambi dovrebbero essere sincronizzati il ​​più possibile. Ad esempio con la denominazione di tabella / classi e campi / colonne. Inoltre, l'approccio di lasciare che i DBA scrivano le procedure puzza di silos di sviluppo, piuttosto che avere un team multidisciplinare.
Augusto

7
Gli SP non sono necessariamente sempre più efficienti e penso che passare da SQL a DBA sia una brutta strada da percorrere. Come esperto di dominio, lo sviluppatore dovrebbe sapere quali dati vogliono ottenere e come ottenerli
Martijn Verburg,

1
Questa è una buona risposta, ma nella mia esperienza la maggior parte dei clienti non ha effettivamente bisogno dei miglioramenti delle prestazioni per il controllo dell'accesso ai dati attraverso procedure memorizzate rispetto all'inconveniente di sfruttare appieno gli strumenti ORM a livello di applicazione. Il più delle volte vedo queste decisioni architettoniche prese nei negozi di software in cui hanno bisogno di giustificare gli stipendi gonfiati dei programmatori di procedure memorizzate "barba grigia" che non hanno altre competenze.
maple_shaft

1
@Augusto the approach of letting the DBAs write the procedures smells like development silos+100 Internet a te per questo gioiello di verità. Ho sempre visto questo come il caso in cui l'accesso ai dati era controllato attraverso procedure memorizzate.
maple_shaft

1
@maple_shaft: perché, i DBA che scrivono SP non sono considerati parte del team di sviluppatori? Dove funziona, sono programmatori specializzati che conoscono davvero bene questo aspetto del sistema, molto meglio della maggior parte degli sviluppatori generici. Questo potrebbe essere il problema che ha dato origine alla popolarità degli ORM. Voglio dire, nessuno ci penserebbe due volte a fare in modo che un designer esegua la GUI, quindi perché l'odio per un architetto di dati che fa lo schema?
gbjbaanb

2

Quello che succede spesso è che gli sviluppatori usano erroneamente i loro oggetti ORM come modelli di dominio.

Ciò non è corretto e collega il dominio direttamente allo schema del DB.

Ciò che dovrebbe davvero avere è modelli di dominio separati ricchi quanto vuoi e utilizzare il livello ORM separatamente.

Ciò significa che sarà necessario mappare tra ogni set di oggetti.


1
Questa è una buona idea, ma per i progetti più piccoli inizia davvero a sembrare eccessivo. Questo approccio richiede anche un livello di traduzione tra il livello di persistenza ORM e il modello di dominio.
maple_shaft

@maple_shaft è d'accordo, ed è quello che intendevo per "mappatura" :-)
ozz

@Ozz, il modo in cui ho lavorato è esattamente questo, le classi di entità SONO il modello di dominio (e potrei aggiungere con parecchio successo). Sono d'accordo che lega il modello di dominio allo schema, ma è esattamente quello che voglio, poiché uso la convenzione sulla configurazione e il piacevole effetto collaterale è che se vedo un campo su un'entità, non ho bisogno di pensarci molto sul nome della tabella e della colonna in cui sono memorizzate tali informazioni.
Augusto

@Augusto L'ho fatto anche io! e come dice maple_shaft, va bene per le piccole app in stile CRUD, ma ci sono molti problemi che l'OP sta scoprendo. Un esempio potrebbe essere quello in cui hai una tabella di mappatura da molte a molte, ad esempio: StudentClasses, che mappa gli studenti alle loro classi e contiene solo StudentID e classID, non vorrai necessariamente mappare questo nel tuo dominio. Questo è solo un breve esempio della mia testa.
ozz,

2
@Ozz: il tuo commento sembra contraddire l'idea stessa di un ORM. Un ORM non "lega il tuo dominio direttamente al tuo schema DB". L'ORM mappa il tuo dominio su uno schema DB, senza la necessità di un livello DAO separato. Questo è il punto centrale di un ORM. E la maggior parte degli ORM gestisce bene i mapping molti-a-molti, senza che sia necessario un modello di dominio per la tabella di mapping.
Kevin Cline,

1

I tuoi oggetti di dominio possono essere popolati come preferisci, non è necessario usare Hibernate. Penso che il termine corretto sia data-mapper . È molto probabile che i tuoi dati persistenti saranno una struttura completamente diversa dagli oggetti del tuo dominio.


Attualmente stiamo utilizzando i mappatori di dati, ma il problema è che i proc memorizzati restituiscono un set minimo di dati, che a volte non è sufficiente per riempire un oggetto (forse dovremmo consentire alle procedure memorizzate di restituire ulteriori informazioni). Ad esempio, un proc del negozio potrebbe restituire un'e-mail dell'utente, nome, cognome; mentre un altro aggiunge userid e un indirizzo. Poiché i dati sono diversi, stiamo utilizzando oggetti diversi per archiviare i dati, il che significa che abbiamo diverse classi "Utente". Sto cercando di evitare di usare l'eredità qui, perché penso che sia un uso sbagliato di essa.
Augusto

@Augusto: interfacce?
Kramii Ripristina Monica il

@Karmii, non credo che le interfacce aiutino qui, dato che allora dovremmo duplicare la logica in diverse classi. Oppure potremmo usare le interfacce e quindi delegare l'elaborazione a una classe di supporto, ma non è proprio OO :(.
Augusto

1
@Augusto Non capisco il problema: "i proc memorizzati restituiscono un set minimo di dati, che a volte non è sufficiente per riempire un oggetto" Quindi modifichi lo sproc o ne crei un altro e poi lasci che il mapper di dati esegua il mapping
NimChimpsky
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.