Logica aziendale al di fuori dei modelli ORM condivisi

6

Ho una serie di modelli ORM condivisi tra l'applicazione aziendale principale e un paio di applicazioni secondarie (come un'interfaccia web amministrativa).

Non voglio mettere la logica di business dell'oggetto per l'applicazione principale all'interno delle classi del modello ORM perché l'interfaccia web non ne usa nessuna (e sarebbe troppo gonfia).

Questo mi lascia il problema di avere due classi per ogni "oggetto" reale (la classe del livello aziendale e la classe del modello ORM) e mi chiedo come dovrei collegare i due. Sia la composizione che l'ereditarietà funzionerebbero, ma entrambi si sentono sbagliati . Ad esempio, ho una classe utente e una classe modello ORM DBUser. L'utente "non è" un DBUser e un utente "non ha" un DBUser.

Esiste una soluzione standard o una procedura ottimale per affrontare questa situazione? O è un caso in cui non c'è una grande risposta e devo solo scegliere quella che mi rende meno a disagio?

Ecco un esempio di codice, nel caso in cui quanto sopra non fosse chiaro:

class DBUser(SQAlchemyBase):

    __tablename__ = 'users'

   user_id = Column(Integer, primary_key=True)
   username = Column(String, nullable=False)
   # ...


class User(object):

    def __init__(self, user_id):
        self.dbuser = db.query(DBUser).filter(DBUser.user_id == user_id).first()

    @property
    def username(self):
        return self.dbuser.username

    @username.setter
    def username(self, username):
        self.dbuser.username = username

    def connect_to_server(self, server):
        ...

    def save(self):
        db.add(self.dbuser)
        db.commit()
        db.detach(self.dbuser)

    def disconnect_from_server(self):
        ...

    def handle_incoming_action(self, action):
        ...
    
posta user35358 13.01.2017 - 03:53
fonte

2 risposte

3

Non conosco il tuo ORM, ma quello che assomigli a Data Transfer Objects, o DTO: fondamentalmente solo i recordset di dati mappati in oggetti, con poca o nessuna ulteriore logica in essi (qui: DBUser). È possibile accedere ai propri valori di dati dal modello di business. Nel migliore dei casi, attraverso un'interfaccia, quindi è possibile utilizzare diversi livelli di accesso ai dati.

A seconda dell'ORM, si può anche usare direttamente gli oggetti business per il mapping dei dati (come con (N) Hibernate in .NET / Java). Sarebbe male implementare codice separato per copiare i dati tra DTO e campi / proprietà degli oggetti business, ad es. come parte delle routine di caricamento / salvataggio / aggiornamento (vedi sotto)!

Vedo un grave errore di progettazione nel tuo modello di business, che si verifica frequentemente dai principianti di ORM (probabilmente c'è ancora la possibilità di correggerlo ora):

Avendo Carica (o inizializzatore per ID) e Salva funzioni su singoli oggetti, anche una transazione DB Commit. Nella maggior parte degli ORM, le entità devono essere progettate come Persistence Ignorant, nel senso che gli oggetti non dovrebbero avere codice correlato alla persistenza del DB, in particolare non per singoli oggetti, nessun tentativo di controllare l'accesso al DB e nessuna proprietà per transitori (non persistente), sporco (modificato) o stantio (modificato nel DB nel frattempo).

Invece, ORM di solito ha una unità di lavoro (ho visto in una breve ricerca su Google che è anche chiamata "sessione" in SQAlchemy). L'applicazione funziona solo con le entità all'interno di una sessione attiva, la sessione tiene traccia delle modifiche e le persiste tutte alla fine (Risciacquo / Conferma, diverse impostazioni possibili).

I nomi di funzioni di sessione come Carica, Salva o Aggiorna sono spesso fuorvianti, poiché non significano istruzioni di inserimento, aggiornamento, eliminazione, selezione di DB effettive per le entità. Questi saranno solo programmati e si verificano quando ORM decide che è il momento giusto. In (N) Ibernazione, le modifiche verranno mantenute anche senza alcuna chiamata Salva / Aggiorna sulle entità e le entità non modificate non verranno affatto aggiornate, anche dopo il salvataggio / l'aggiornamento.

Con i tentativi di forzare l'ORM alle singole istruzioni CRUD nel DB, l'utente viola l'ORM e distrugge molte funzioni utili!

Risulterà anche un numero enorme di singoli roundtrip DB per le singole istruzioni (chiamate SELECT N + 1 quando si attraversano i grafici degli oggetti), invece delle query e dei batch ottimizzati dall'ORM.

Mantenimento di identità di oggetti / dati:

Inoltre, se si utilizzano oggetti business wrapping, accedendo a DTO, è necessario assicurarsi che le istanze dell'oggetto siano univoche per riga dati, proprio come il DTO. Generalmente, ORM consente solo un'istanza DTO per elemento di origine del database, anche se interrogato o utilizzato in più posizioni.

    
risposta data 15.01.2017 - 01:02
fonte
1

Esamina il tuo problema attraverso gli occhiali di Modelli di Enterprise Application Architecture di Fowler suggerisce quanto segue:

  • la logica dell'origine dati è implementata in DBUser , che è un Gateway dati riga : è un semplice titolare di dati responsabile di tenere in memoria una singola riga di una tabella ma senza alcuna logica di dominio.
  • la logica del dominio è implementata con User prendendo un approccio Modello di dominio . L'oggetto dominio deve essere associato al suo titolare di dati, ad esempio mantenendo un riferimento ad esso.

Mantenere tale riferimento può essere tecnicamente implementato come una composizione. Semanticamente questo collegamento non è necessariamente una relazione "ha una"; su un modello UML utilizzi una associazione etichettata " ha un titolare di dati "o" usa per la conservazione ". Alcuni potrebbero obiettare che dovrebbe essere rappresentato da un'aggregazione (dove DBUser è la parte di dati persistenti dell'oggetto). Tuttavia, la composizione UML non verrebbe utilizzata, poiché un DBUser non è necessariamente di proprietà di User (ad esempio , potrebbe essere di proprietà di una mappa di identità che mantiene una vista su tutte le righe db in memoria).

L'utilizzo di un'eredità sarebbe la scelta sbagliata qui, a mio parere: bloccherebbe le tue classi di dominio in un progetto specifico (dove l'oggetto dominio sarebbe un proxy dell'oggetto db). Questo non è un suono separazione delle preoccupazioni . Ad esempio, se in seguito decidessi di utilizzare un'altra implementazione dell'origine dati (ad esempio un ORM che utilizza la mappatura dinamica basata sui metadati ) Dovresti quindi ridisegnare completamente i tuoi oggetti di dominio.

    
risposta data 14.01.2017 - 12:42
fonte

Leggi altre domande sui tag