Dovrei modificare un'entità con molti parametri o con l'entità stessa?

7

Abbiamo un sistema basato su SOA. I metodi di servizio sono come:

  1. UpdateEntity(Entity entity)

Per piccole entità, va tutto bene. Tuttavia, quando le entità diventano sempre più grandi, per aggiornare una proprietà dovremmo seguire questo modello nell'interfaccia utente:

  1. Ottieni parametri dall'interfaccia utente (utente)
  2. Crea un'istanza dell'entità, utilizzando questi parametri
  3. Ottieni l'entità dal servizio
  4. Scrivi il codice per riempire le proprietà invariate
  5. Fornisci l'entità risultato al servizio

Un'altra opzione che ho sperimentato in precedenti esperienze è la creazione di metodi di aggiornamento semantico per ogni scenario di aggiornamento. In altre parole, invece di avere un unico metodo di aggiornamento globale, avevamo molti metodi parametrici ad-hoc. Ad esempio, per l'entità User , invece di avere il metodo UpdateUser (User user) , abbiamo avuto questi metodi:

  1. ChangeUserPassword(int userId, string newPassword)
  2. AddEmailToUserAccount(int userId, string email)
  3. ChangeProfilePicture(int userId, Image image)
  4. ...

Ora, non so quale metodo sia veramente migliore, e per ogni approccio, incontriamo problemi. Voglio dire, ho intenzione di progettare l'infrastruttura per un nuovo sistema, e non ho abbastanza ragioni per scegliere uno di questi approcci. Non sono riuscito a trovare buone risorse su Internet, a causa della mancanza di parole chiave che potrei fornire. Quale approccio è meglio? Quali sono le insidie che ciascuno ha? Quali benefici possiamo ottenere da ciascuno di essi?

    
posta Saeed Neamati 24.06.2012 - 14:03
fonte

3 risposte

8

La tua domanda equivale a scegliere il giusto livello di astrazione per l'interfaccia del tuo servizio. Ogni sistema del mondo reale ha requisiti diversi, ma come regola generale preferisco strongmente le interfacce di servizio con una vista di alto livello. Cioè, il secondo approccio che descrivi, non il primo.

Tuttavia, questa è solo una linea guida e per fare una scelta corretta dovrai essere in grado di giustificarlo con più di "qualcuno ha detto che era meglio così" o "è il solito approccio".

Quindi analizziamo qui i problemi rilevanti.

Accoppiamento

Il tuo design dovrebbe consentire l'implementazione del servizio e i suoi clienti in modo flessibile. Il cliente non dovrebbe aver bisogno di sapere come è implementato il servizio, o come memorizza (spesso, anche come rappresenta) i suoi dati. Dovrebbe essere possibile (molto) modificare l'implementazione del servizio senza dover ricostruire o modificare i client. L'uso della parola "Entità" nella descrizione sembra implicare che gli oggetti esposti tramite l'interfaccia di servizio corrispondano esattamente alle entità in cui i dati dell'applicazione sono stati scomposti. In tal caso, ciò potrebbe indicare che esiste un accoppiamento stretto tra l'interfaccia client e l'implementazione del servizio. A volte questo è comunque consentito, laddove il servizio stesso utilizza definizioni di dati definite nella propria interfaccia (questo può aiutare con la decomposizione, che menzionerò più avanti).

Pensa a come implementare una modifica alle strutture dati utilizzate nell'interfaccia; cioè, se le strutture di dati trasportate attraverso l'interfaccia del servizio devono cambiare, devi eseguire un cambiamento radicale (scomodo!) o il tuo servizio di progettazione può accogliere clienti che si aspettano la nuova rappresentazione e alcuni vecchi? (il controllo delle versioni dell'interfaccia è un modo comune per consentire ciò)

decomposizione

La progettazione dell'interfaccia del servizio dovrebbe generalmente consentire una flessibilità tale da consentire la suddivisione dell'implementazione del servizio, al fine di consentire a parte dell'implementazione del servizio di migrare verso un design diverso. Ad esempio, dovrebbe essere possibile mantenere gli attributi delle persone spostati fuori dalla vecchia implementazione del servizio e in un database LDAP. Mentre suppongo che questo sia sempre possibile, voglio dire che il design dovrebbe funzionare in modo tale che i client non necessariamente debbano preoccuparsi che ciò sia successo, e che il l'implementazione di solo una quantità ragionevole delle interfacce del servizio dovrebbe essere modificata per consentire ciò. Ad esempio, in questo caso, questo tipo di refactoring non dovrebbe richiedere modifiche da apportare a parti non correlate dell'implementazione del servizio.

Indirection

Il livello di astrazione del tuo servizio dovrebbe essere scelto in modo tale che interagisca in un modo facile da spiegare con i sistemi a strati. Ad esempio, dovrebbe essere semplice creare un servizio di caching che front-end questo servizio e memorizza nella cache i dati per ridurre il carico sul back-end. Allo stesso modo con modifiche allo schema di autorizzazione. Allo stesso modo, dovrebbe essere ragionevolmente semplice creare un finto back-end (spesso offrendo solo un sottoinsieme di interfacce) per facilitare la verifica dei client. Allo stesso modo, i client artificiali per il test del carico.

Altri problemi

Ci sono altri problemi da tenere a mente per alcuni sistemi che non sono sempre importanti, ma possono rendere la vita molto difficile se non li si pensa prima del tempo. Un grande qui è la questione di come tagliare il servizio. Ossia, dividere il sistema esistente in più parti, ciascuna delle quali tratta alcuni degli oggetti dati nel sistema. Ad esempio se il tuo sistema accetta la posta elettronica, potrebbe essere necessario suddividerlo in più backend, ciascuno accettando la posta elettronica per un sottoinsieme di utenti (ad esempio "a" - "f" su shard 1, "g" - "p" su shard 2, "q" - "t" su shard 3, "u" - "z" su shard 4, "Â" - "Ͱ" su shard 5, e così via). Al fine di consentire ai client di rimanere invariati, questo tipo di cambiamento verrebbe di solito accompagnato dall'introduzione di un livello proxy, il cui unico compito è quello di inoltrare le richieste al back-end pertinente (dovrebbe essere possibile distribuire quanti più proxy ci sono necessario per far fronte al carico, il loro compito non è computazionalmente difficile e non dovrebbe richiedere di colpire un disco per identificare il back-end giusto.

    
risposta data 24.06.2012 - 16:54
fonte
2

Il secondo approccio agli aggiornamenti fornisce un significato più semantico per l'aggiornamento. È simile a CQRS ( Separazione di responsabilità della query di comando ) in cui si fornisce un servizio diverso per i comandi (modifiche al modello) rispetto a Domande contro il modello. Utilizzando questo approccio, puoi mitigare la necessità di gestire la concorrenza ottimistica perché invece di un'operazione globale "questo oggetto è stato aggiornato", fornisci operazioni distinte che modificano specifiche informazioni.

Questo approccio fornisce anche un buon punto di ingresso per un controllo più dettagliato. Invece di vedere solo l'utente X aggiornato, puoi dire Utente A cambiato l'indirizzo di fatturazione primario del Cliente X.

Raccomando anche di utilizzare un O / RM che fornisce il rilevamento delle modifiche fuori dalla scatola in modo tale da caricare l'entità, apportare la modifica e salvarla. Puoi anche sfruttare il design basato sul dominio e mettere queste operazioni direttamente sull'entità. Quindi, invece di avere una ServiceOperation che manipola l'entità, il Service Layer diventa una facciata che delega direttamente l'operazione all'entità.

Utilizzando questo approccio, i dettagli del modello sottostante possono cambiare senza richiedere la modifica del Service Layer o dei suoi client.

    
risposta data 24.06.2012 - 16:43
fonte
0

A meno che non si stia utilizzando un ORM per tenere traccia delle modifiche, sarebbe difficile passare solo le colonne modificate semplicemente perché ci sono molte permutazioni dei parametri. Dovresti scrivere un metodo separato per ogni permutazione. Questo chiaramente non è possibile.

Esempio:

Cliente (id, firstName, lastName)

Per aggiornare un record del cliente, potresti avere i seguenti parametri passati a UpdateCustomer (e ai relativi sovraccarichi):

A-firstName

B-firstName, LastName

C-Cognome

Questo non è un approccio accettabile specialmente con un gran numero di colonne.

Il passaggio di tutta l'entità ha anche i suoi svantaggi, tuttavia, quelli possono essere tollerati:

  1. È soggetto a errori. Devi assicurarti che ogni colonna contenga i dati corretti.

  2. Trasmette i dati non necessari e richiede un'istruzione di aggiornamento con istruzioni SET non necessarie. Inoltre, potrebbe essere necessario eseguire ricerche non necessarie su altre tabelle per popolare valori in FK e fare validazioni non necessarie.

  3. In alcune applicazioni, non è OK aggiornare tutti i dati in un'entità, poiché alcuni utenti non hanno il privilegio di farlo. Questo è vero per le applicazioni bancarie, per esempio. In questo caso, devi passare solo le colonne modificate altrimenti l'aggiornamento per gli utenti non privilegiati non verrà eseguito.

Quindi, a meno che tu non sia disposto a scrivere un livello che rilevi le modifiche e generi SQL per questo, l'approccio migliore (secondo me) è utilizzare un ORM che tenga traccia delle modifiche per te.

    
risposta data 24.06.2012 - 18:07
fonte

Leggi altre domande sui tag