Oggetti business - Contenitori o funzionali?


19

Questa è una domanda che ho posto qualche tempo fa su SO, ma potrebbe essere discussa meglio qui ...

Dove lavoro, siamo andati avanti e indietro su questo argomento diverse volte e stiamo cercando un controllo di integrità. Ecco la domanda: gli oggetti business dovrebbero essere contenitori di dati (più simili ai DTO ) o dovrebbero contenere anche una logica in grado di eseguire alcune funzionalità su quell'oggetto.

Esempio: prendi un oggetto cliente, probabilmente contiene alcune proprietà comuni (Nome, Id, ecc.), Quell'oggetto cliente dovrebbe includere anche funzioni (Salva, Calc, ecc.)?

Una linea di ragionamento dice che separa l'oggetto dalla funzionalità (unico responsabile della responsabilità) e inserisce la funzionalità in un livello o oggetto di business logic.

L'altra linea di ragionamento dice, no, se ho un oggetto cliente voglio solo chiamare Cliente. Salvare e averlo fatto. Perché devo sapere di un'altra classe per salvare un cliente se sto consumando l'oggetto?

I nostri ultimi due progetti hanno separato gli oggetti dalla funzionalità, ma il dibattito è stato nuovamente sollevato su un nuovo progetto.

Il che ha più senso e perché ??


1
Potresti dirci di più sul tuo progetto? La natura del tuo progetto determinerà quale soluzione è migliore.

Risposte:


5

Se si considera che un cliente fa parte del modello di dominio, ha senso (soprattutto nel contesto di DDD, ma non limitato ad esso) avere proprietà e operazioni per quell'oggetto.

Detto questo, tuttavia, penso che l'esempio che hai usato sia scarso ed è una causa dell'argomento. Se parli di persistenza, i clienti generalmente non si "salvano" da soli; qualunque cosa tu stia usando per la persistenza. Ha senso che qualsiasi tipo di persistenza debba appartenere al livello / partizione di persistenza. Questo è generalmente il pensiero alla base del modello di repository. ** Un metodo come Customer.UpgradeWarranty () o Customer.PromoteToPreferred () rende più chiaro l'argomento.

Anche questo non toglie la possibilità di avere DTO . Considera la situazione in cui, ad esempio, passerai le informazioni sui clienti a un servizio remoto. Potrebbe non avere senso per un cliente creare un DTO di se stesso per il trasporto, questo è un problema architettonico, ma un caso potrebbe essere fatto per esso nella persistenza o nel livello di rete / partizione / codice / what-have-you. In tal caso, un oggetto del genere potrebbe avere metodi simili a questo

public static CustomerDTO GetDTO(Customer c) { /* ... */ }

public static Customer GetCustomer(CustomerDTO cdto) { /* ... */ }

Quindi, in sintesi, ha perfettamente senso avere operazioni su un oggetto di dominio che sono congruenti con le operazioni logiche nel dominio.

Google per "Persistence Ignorance" per una serie di buone discussioni sull'argomento ( questa domanda SO e la sua risposta accettata è un buon punto di partenza).

** Questo diventa un po 'confuso con alcuni software OR / M in cui sei costretto ad ereditare da una base persistente che ha un metodo' Salva '.


3

Di recente ho rielaborato del codice per separare gli oggetti dai dati. Il motivo è che i dati sono ottenuti attraverso un servizio separato ed è molto più semplice passare i dati grezzi al / dal server invece di passare l'intero oggetto avanti e indietro e dover gestire gli errori di convalida sul server.

Per i piccoli progetti, avere oggetti che contengono la loro logica aziendale e i dati probabilmente va bene, ma per i grandi progetti, o progetti che potrebbero espandersi nel tempo, separerei sicuramente i livelli.


3

Penso che entrambi i modi di farlo possano avere i loro benefici, ma lo guarderei da una visione orientata agli oggetti o guidata dal dominio: quali sono le responsabilità delle diverse classi e oggetti.

Quindi, mi chiederei questo, nel tuo caso concreto: un cliente dovrebbe sapere come salvarsi?

Per me, la risposta sarebbe no: logicamente per me non ha senso, e un cliente non dovrebbe sapere nulla di alcun quadro di persistenza (separazione delle responsabilità).

Per quanto riguarda gli esempi di SnOrfus con Customer.UpgradeWarranty () o Customer.PromoteToPreferred (), sono ovviamente più orientati alla logica aziendale rispetto a Customer.Save (). Esistono diversi approcci a questo, ma di nuovo se ti chiedi se un cliente dovrebbe essere in grado di aggiornare la sua garanzia, la risposta potrebbe essere sia sì che no, a seconda di come la vedi:

  • sì: un cliente può ovviamente aggiornare la sua garanzia
  • no: un cliente potrebbe chiedere di essere aggiornato, ma l'aggiornamento viene eseguito da qualcun altro

Torna alla tua domanda originale; quale modo ha più senso dipenderà probabilmente da chi chiedi e dal loro metodo preferito, ma la cosa più importante è probabilmente attenersi a un modo di farlo.

Nella mia esperienza, tuttavia, separare i dati e la logica (aziendale) rende un'architettura più semplice, sebbene non così entusiasmante.


2

Penso che tu stia parlando in particolare della differenza tra lo ActiveRecordschema e lo Repositoryschema. Nel primo, le entità sanno come persistere, e nel secondo il repository conosce la persistenza. Penso che quest'ultimo offra una migliore separazione delle preoccupazioni.

In senso lato, se le entità si comportano più come una struttura di dati, allora non dovrebbero avere comportamenti, ma se hanno comportamenti, non dovrebbero essere usati come una struttura di dati. Esempio:

Struttura dati:

class CustomerController
{
    public int MostRecentOrderLines(Customer customer)
    {
        var o = (from order in customer.Orders
                orderby order.OrderDate desc
                select order).First();
        return o.OrderLines.ToList().Count;
    }
}

Struttura non dati:

class Customer
{
    public int MostRecentOrderLines()
    {
        // ... same ...
    }
}

Nel primo caso, puoi navigare senza problemi nella struttura della struttura dei dati del tuo modello, ovunque nel tuo codice di lavoro, e va bene, perché lo stai trattando come una struttura di dati. In quest'ultimo caso, il cliente è più simile a un servizio per un determinato cliente, quindi non dovresti chiamare metodi sul risultato. Tutte le cose che vuoi sapere sul Cliente dovrebbero essere disponibili chiamando un metodo sull'oggetto Cliente.

Quindi vuoi che le tue entità siano strutture o servizi di dati? Per coerenza, sembra meglio attenersi a uno. Nel primo caso, metti la tua logica di tipo "servizio" da qualche altra parte, non nell'entità.


1

Non posso davvero rispondere alla tua domanda, ma trovo divertente che stiamo discutendo anche in uno dei nostri progetti a scuola. Io stesso vorrei separare la logica dai dati. Ma molti dei miei compagni di classe dicono che l'oggetto deve contenere tutta la logica E i dati.

Cercherò di estendere i fatti che mettono in evidenza:

  • Un oggetto di business class rappresenta una cosa, quindi tutti i dati e la logica devono essere contenuti. (ad esempio se vuoi aprire una porta, lo fai da solo, non lo chiedi a qualcun altro. Cattivo esempio lo so)

  • Più facile da capire il codice e la funzionalità di un oggetto bu.

  • Meno complessità nella progettazione

Sto dicendo loro che sono pigri e lo farei così:

  • Sì, le business class rappresentano cose, quindi contengono dati. Ma è sbagliato salvare te stesso o persino copiarlo. Non puoi farlo in rl.

  • Rendere l'oggetto responsabile di tutto non lo rende buono per la durabilità e la manutenibilità in futuro. Se hai una classe separata responsabile del salvataggio, se aggiungi un nuovo oggetto al design, puoi facilmente implementare la sua funzione di salvataggio. invece di ricodificare tutto in quella classe.

  • Avendo un oggetto in grado di conservare i dati, quell'oggetto può contenere una connessione al database e tutto il traffico del database viene guidato su quell'oggetto. (sostanzialmente è il livello di accesso ai dati), altrimenti tutto l'oggetto Business dovrebbe contenere una connessione. (che aggiunge complessità)

Beh, in un modo o nell'altro, chiederò a un insegnante. Quando lo avrò, pubblicherò qui anche la sua risposta, se lo desideri. ;)

modificare:

Ho dimenticato di menzionare che questo progetto riguardava un negozio di libri.


I tuoi compagni di classe hanno ragione, tranne per il fatto che stanno confondendo la logica aziendale con altre forme di logica (in questo caso, logica architetturale o di persistenza).
Steven Evers,

1

La risposta dipenderà davvero da quale sia la tua architettura / progettazione: un'architettura DDD con un modello di dominio sarà molto diversa da un modello CRUD incentrato sui dati quando si tratta di progettazione di oggetti.

In generale, tuttavia, tenere presente che nella progettazione orientata agli oggetti si sta cercando di incapsulare lo stato ed esporre il comportamento. Ciò significa che di solito proverai ad associare lo stato a un comportamento il più vicino possibile a quel comportamento - quando non lo fai, sei costretto a esporre lo stato in una forma o nell'altra.

In un modello di dominio vuoi separare il modello di business dai problemi di infrastruttura, quindi vuoi assolutamente evitare metodi come '.Save'. Non ricordo l'ultima volta che ho "salvato" un cliente nella vita reale!

In un'applicazione CRUD, i dati sono i cittadini di prima classe, quindi un ".Save" è del tutto appropriato.

La maggior parte delle applicazioni del mondo reale avranno una miscela di questi paradigmi: modello di dominio in cui vi sono regole aziendali complesse o in rapido cambiamento, DTO per il trasferimento di dati oltre i confini, CRUD (o qualcosa tra CRUD e un modello di dominio, come activerecord) altrove. Non esiste una regola unica per tutte le regole.


0

Ho riflettuto sulla stessa domanda per un po '- penso che il componente dati e il componente logico debbano essere separati. Lo credo perché ti porta nel giusto stato d'animo per quanto riguarda la logica di business come un'interfaccia per i dati che fornisce significato.

Modificherei anche il punto di Scott Whitlock dall'alto (tranne per il fatto che non ho punti per essere un nuovo membro), i dati o le classi di business logic non dovrebbero davvero preoccuparsi di come l'oggetto viene archiviato in modo persistente.

Detto questo, se hai a che fare con un prodotto esistente, purché tu abbia interfacce contrattuali pulite, va bene lo stesso e anche gestibile ...

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.