Modelli spessi vs. Logica aziendale, dove si distingue?


16

Oggi ho avuto un acceso dibattito con un altro sviluppatore della mia organizzazione su dove e come aggiungere metodi alle classi mappate nel database. Usiamo sqlalchemy, e gran parte della base di codice esistente nei nostri modelli di database è poco più di un insieme di proprietà mappate con un nome di classe, una traduzione quasi meccanica dalle tabelle del database agli oggetti Python.

Nell'argomento, la mia posizione era che il valore principale dell'utilizzo di un ORM era che era possibile associare comportamenti e algoritmi di basso livello alle classi mappate. I modelli sono prima di tutto classi e secondariamente persistenti (potrebbero essere persistenti usando xml in un filesystem, non è necessario preoccuparsene). Il suo punto di vista era che qualsiasi comportamento è "logica commerciale", e appartiene necessariamente a qualunque luogo tranne che al modello persistente, che deve essere usato solo per la persistenza del database.

Penso certamente che ci sia una distinzione tra ciò che è la logica aziendale, e dovrebbe essere separato, dal momento che ha un certo isolamento dal livello inferiore di come viene implementato, e la logica del dominio, che credo sia l'astrazione fornita dalle classi del modello discusso nel paragrafo precedente, ma sto facendo fatica a mettere il dito su quello che è. Ho una migliore idea di quale potrebbe essere l'API (che, nel nostro caso, è HTTP "ReSTful"), in quanto gli utenti invocano l'API con ciò che vogliono fare , distinto da ciò che gli è permesso fare e da come viene fatto.


tl; dr: che tipo di cose possono o dovrebbero andare in un metodo in una classe mappata quando si usa un ORM, e cosa dovrebbe essere lasciato fuori, per vivere in un altro livello di astrazione?


Mi sembra che siano state due le questioni discusse contemporaneamente, la questione della persistenza I modelli sono i primi in classe e in secondo luogo persistenti (potrebbero essere persistenti usando xml in un filesystem, non è necessario preoccuparsene). e il motivo del codice. Di solito le cose si chiariscono, almeno per me, quando mi chiedo perché il codice è scritto, quale requisito impone questo codice. È il requisito dei clienti su come funzionerà il programma o è in che modo scegliamo di implementarlo. Per me il primo è la logica aziendale e il secondo ciò che tu chiami logica di dominio. Come questo aiuta.

Risposte:


10

Sono principalmente con te; sembra che il tuo collega stia discutendo del modello di dominio anemico antipattern o della duplicazione del modello in un "modello di persistenza" senza evidenti benefici (sto lavorando a un progetto Java in cui è stato fatto, ed è un enorme mal di testa di manutenibilità, come significa tre volte il lavoro ogni volta che qualcosa cambia nel modello).

Quali tipi di cose possono o dovrebbero andare in un metodo in una classe mappata quando si usa un ORM e cosa dovrebbe essere escluso per vivere in un altro livello di astrazione?

Regola empirica: la classe dovrebbe contenere una logica che descriva fatti di base sui dati che sono veri in ogni circostanza. La logica specifica di un caso d'uso dovrebbe essere altrove. Un esempio è la convalida, c'è un articolo interessante di Martin Fowler in cui sottolinea che dovrebbe essere considerato dipendente dal contesto.


+1, per la seconda parte della tua risposta. Per quanto riguarda la prima parte, sono sicuro del motivo per cui dici che il modello di dominio anemico richiede molto lavoro "extra" in caso di cambiamento. La mia comprensione è che il cambiamento avverrà comunque, ma in diverse classi (il che è un po 'cattivo) ma è quasi lo stesso ammontare di cambiamento; Suppongo?
NoChance,

1
@Emmad Kareem: il commento riguardava la perseveranza spearate e i modelli di dominio. L'aggiunta di una proprietà al modello richiede quindi l'aggiunta a due classi di modello (anziché a una), nonché a qualsiasi mappa tra loro (che potrebbe in teoria essere automatica, ma di solito l'assur che pensava che fosse una buona idea avere un separato il "modello di persistenza" decide di giustificare tale separazione rendendoli diversi, ad es. hanno tipi di dati che corrispondono maggiormente al modello del tipo di database), quindi è 2 + X volte la quantità di modifica, con X che varia tra 0 e ore di perdita di produttività a causa dell'oscuro problemi di mappatura.
Michael Borgwardt,

3

Questa è una chiamata di giudizio che dipende davvero dalle dimensioni e dalle dimensioni previste per ciò che stai sviluppando. L'approccio più rigido è limitare i tipi di ORM a un componente di accesso ai dati e utilizzare i POCO in una libreria comune come tipi referenziati e utilizzati da tutti i livelli. Ciò consentirebbe una futura separazione fisica nonché una separazione logica. È inoltre possibile decidere che dovrebbe esistere un livello aggiuntivo tra l'interfaccia utente e il livello della logica aziendale. Questo di solito viene chiamato strato Facciata o Interfaccia aziendale. Questo livello aggiuntivo è il luogo in cui vive il "codice del caso d'uso". Il singolo codice liberamente accoppiato viene chiamato dal livello Facade / BI (ad esempio Facade ha una funzione ProcessOrder () che chiama nella logica aziendale 1: M volte per eseguire tutte le fasi necessarie per elaborare effettivamente l'ordine).

Tuttavia, tutto ciò che viene detto: molte volte questa quantità di architettura è semplicemente inutile. Ad esempio, codificare specificamente per un semplice sito Web in cui non si intende imballare i componenti per il riutilizzo. È perfettamente valido creare un sito Web MVC e utilizzare oggetti EF per questo tipo di soluzione. Se il sito deve ridimensionarsi in un secondo momento, è possibile esaminare il clustering o un processo spesso perduto mischia chiamato "refactoring".


3

Ricorda al tuo collega che non è necessario architettare eccessivamente i modelli come se si trattasse di un progetto Java. Voglio dire, confrontare due oggetti persistenti è un comportamento, ma non è specificato dal livello di persistenza. Quindi la domanda sulla birra 6 è: perché le classi completamente non correlate descrivono qualcosa sulla stessa cosa? Certo, la persistenza è un aspetto abbastanza grande di un modello da trattare separatamente, ma non abbastanza da garantire che sia trattato distinto da tutto il resto. Se guidi la tua auto, la lavi o la rompi, stai manipolando la tua auto per tutto il tempo.

Quindi perché non comporre tutti questi diversi aspetti in una singola classe di modello? Hai bisogno di un sacco di metodi di classe che trattano oggetti persistenti - mettili in una classe; hai un sacco di metodi di istanza che si occupano di validazione - inseriscili in un altro. Infine, mescola i due in e voilà! Ti sei procurato una rappresentazione del modello intelligente, consapevole e completamente contenuta proprio lì.


1

Oltre ad altre risposte, prestare attenzione ai cavehat nascosti quando si utilizzano modelli di dominio avanzati con un ORM.

Ho avuto problemi con l'iniezione di servizi polimorfici nelle classi di modelli persistenti quando ho cercato di ottenere qualcosa di simile al seguente pseudocodice:

Person john = new Person('John Doe')
Organisation company = organisation_repository.find('some id')
Employee our_collegue_john = company.hire(john)

In questo caso, un'organizzazione può richiedere una HRServicedipendenza come costruttore (ad esempio). Di solito non è possibile controllare facilmente l'installazione delle classi del modello quando si utilizza un ORM.

Stavo usando Doctrine ORM e il contenitore dei servizi di Symfony. Ho dovuto monkeypatch l'ORM in un modo non così elegante e non avevo altra scelta che separare la persistenza e i modelli di business. Non ho ancora provato con sqlachemy, ho pensato. Python potrebbe rivelarsi più flessibile di PHP per queste cose.

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.