Alternativa agli oggetti di dominio anemici (semplice esempio fornito)

6

Voglio modellare eventi e promemoria (TV) e mi chiedo quale sia il modo migliore per modellarlo.

I requisiti sono approssimativamente

  • Quando un evento non ha promemoria, è possibile creare un promemoria
  • Quando un evento ha un promemoria
    • il promemoria può essere cancellato
    • viene mostrata un'icona sull'evento
  • Esiste una schermata separata che presenta tutti i promemoria
  • I promemoria sono memorizzati nel back-end e hanno proprietà multiple

Ovviamente, il nostro dominio reale è più grande e contiene anche canali, registrazioni, ...

Fondamentalmente, abbiamo bisogno di funzioni per

  • crea un promemoria
  • elimina un promemoria
  • ottieni l'elenco di tutti i promemoria
  • controlla se un evento ha un promemoria

Questo sembra un problema relativamente semplice, ma mi sto sforzando. Di seguito, ho raccolto una serie di opzioni possibili e apprezzerei molto il tuo feedback.

Opzione 1

L'evento crea promemoria

API eventi

Reminder createReminder() //adds the Reminder to the backend 
boolean  hasReminder()

API promemoria

void deleteReminder() //removes the Reminder from the backend
static List<Reminder> getAllReminders()

Ma

  • l'eliminazione e la creazione di un promemoria sono funzioni simmetriche e quindi mi aspetterei su una singola API (ora sia l'evento che il promemoria hanno un'interfaccia per il back-end)
  • Non sono un grande fan della funzione statica su Promemoria. Sembra indicare che un oggetto di ordine superiore dovrebbe esistere

Opzione 2

API promemoria

Reminder createReminder(Event event) //adds the Reminder to the backend
void deleteReminder() //removes the Reminder from the backend
boolean hasReminder(Event event)
static List<Reminder> getAllReminders()

Il vantaggio è che l'API Event non cambia quando si aggiunge la funzionalità Promemoria (= > buona estensibilità wrt, immagino). Ma

  • creare un promemoria per un evento (e verificare se un evento ha un promemoria) sembra essere più una funzione su Evento quindi mi aspetterei che siano presenti nell'API eventi

Opzione 3

API eventi

Reminder createReminder() //adds the Reminder to the backend
void deleteReminder() //removes the Reminder from the backend
boolean hasReminder()
static List<Reminder> getAllReminders()

Ma

  • getAllReminders () sembra più una funzione su Promemoria

Opzione 4

Crea un ReminderManager / ReminderService (singleton) con la seguente API

Reminder createReminder(Event) //adds the Reminder to the backend
void deleteReminder(Reminder) //removes the Reminder from the backend
boolean hasReminder(Event event)
List<Reminder> getAllReminders()

Questa è la mia opzione preferita ma

  • Ciò genera oggetti anemici di evento / promemoria (solo getter e setter ma nessuna logica). Sembra esserci una discussione se questo è un anti-pattern o not
posta Marc Van Daele 06.01.2016 - 10:10
fonte

3 risposte

2

In sostanza se osservi i tuoi requisiti pubblicati sopra:

  • create a Reminder
  • delete a Reminder
  • get the list of all Reminders
  • check if an Event has a Reminder

Questi sono classici CRUD semplici. Anche le parole chiave corrispondono quasi esattamente C reate, R ead (/ find / get / check), U pdate, D elete.

Come tale, non hai un dominio complicato o una logica aziendale in cui un modello di dominio ricco sarebbe un vantaggio. Ecco perché stai cercando di trovare i posti giusti per i tuoi metodi API.

Vedi qui per opinioni simili:

Modello di dominio ricco anemico vs

Sono tutto per i modelli di dominio ricchi, ma solo dove hanno senso. Cominciano ad avere senso quando devi implementare molte o complicate regole aziendali che operano nel tuo dominio e ti ritrovi a duplicare e diffondere la logica di business su tutti i tuoi servizi. Quando un nuovo requisito richiede modifiche in 3 servizi, e forse hai dimenticato un cambiamento nel quarto, allora è il momento di iniziare a pensare di utilizzare un modello di dominio ricco e di consolidare tutta la logica di business negli oggetti del modello di dominio.

Vedi qui un bel discorso su come refactoring un'app anemica modello di dominio a uno ricco modello di dominio uno. Jimmy Bogart inizia a parlare dicendo che spesso è difficile sapere dall'inizio quando un modello di dominio ricco sarebbe appropriato (poiché la maggior parte delle applicazioni è piuttosto semplice). Quindi ha senso iniziare il refactoring solo verso un modello di dominio ricco una volta che la complessità diventa evidente.

La mia raccomandazione

Usa semplicemente un EventService, ReminderService e così via per la maggior parte dei metodi, e metti solo quelli sugli oggetti del dominio che chiaramente appartengono a uno di essi. Proverò a dare esempi concreti di seguito.

Non è del tutto chiaro se vuoi modellare il tuo dominio sul front end o sul back-end, ma dal tuo testo presumo che sia front-end (correggimi se sbaglio e adatterò la risposta). In tal caso dovresti separare le tue preoccupazioni in modo pulito, ad es. tramite un'architettura MVP (o MVC, MVVM, ...).

Supponendo di avere quello, ecco come tutte le parti si adatterebbero insieme:

Visualizza

Non ha ovviamente alcun dominio o logica di servizio in esso. Gestisce problemi puramente di presentazione (ad esempio quale icona mostrare a seconda dello stato del modello).

Presenter / Controller

Gestisce le comunicazioni tra la vista e il modello e il codice "colla" per far funzionare tutto. Qui il controller potrebbe chiamare ReminderService.getAllReminders() e quindi compilare ViewModel con l'elenco.

Allo stesso modo hai EventService.getAllEvents() .

Si noti che ViewModel e il modello di dominio sono in genere separati, ma in casi semplici potrebbero essere la stessa cosa, specialmente se si tratta di un'applicazione CRUD in cui ogni proprietà del modello di dominio è direttamente rappresentata nella vista; allora avrebbe senso passare semplicemente il modello di dominio alla View come ViewModel.

Modello

Una volta che hai ViewModels (che potrebbe delegare al modello di dominio o essere la stessa cosa), puoi chiamare gli altri metodi direttamente sugli oggetti del modello:

class Event
    Reminder createReminder()
    void deleteReminder()
    boolean hasReminder()

Backend

Se la tua logica di business rimane così semplice e simile a CRUD, puoi semplicemente utilizzare i Servizi che delegano al livello di persistenza.

Sommario

Quindi sostanzialmente il mio suggerimento è come la tua Opzione 3, ma non avere paura di avere oggetti di servizio se hai davvero metodi che non si adattano a un oggetto modello. Ciò non rende automaticamente il tuo modello di dominio anemico. E anche se al momento non hai quasi nessuna logica nel tuo modello di dominio, potrebbe essere ok se davvero non hai una logica aziendale complessa da gestire.

    
risposta data 12.01.2016 - 14:00
fonte
2

Mi sembra che manchi un oggetto User. Ovviamente ci saranno milioni di promemoria, ma ti interesseranno solo quelli per un particolare utente.

Quindi

User.AddReminder(Reminder reminder)
User.DeleteReminder(string reminderId)
User.Reminders

Inoltre, come fai notare il promemoria, vorrai essere in grado di fornire all'utente un link all'evento

Reminder.Event

o forse:

Reminder.EventId

a seconda che tu abbia un elenco di eventi caricati altrove

    
risposta data 12.01.2016 - 14:27
fonte
0

Non vedo alcun design OO nella tua domanda, quindi mi sono preso la libertà di crearne uno.

-

public interface Event {
    public void setDate(java.util.Date date);
    public java.util.Date getDate();
    public void setDescription();
    public String getDescription();
    public boolean hasReminder();
    public void addReminder(Reminder reminder); 
}

Devi impostare Event per creare un Reminder , il Reminder si aggiungerà a Event con il callback addReminder(Reminder reminder); di Reminder .

public interface Reminder {
    public void setEvent(Event e);
    public Event getEvent();
    public void setDescription();
    public void setScheduleInfo(ScheduleInfo ei);
    public boolean existsInBackEnd();
    public void save();
    public void delete();
}

-

Le informazioni di pianificazione possono essere complesse, quindi ho fatto ricorso a una stringa simile a cron per semplificare questo

public interface ScheduleInfo {
    public void setPriority(Priority p);    
    public void setCronString(String cronstr);
}

-

public enum Priority {
    NONE,LOW,MEDIUM,HIGH,URGENT
}

-

Questo è un ListManager generico che funzionerà per eventi e promemoria.

import java.util.List;
public interface ListManager<T> {
    public void populateFromBackEnd();
    public void add(T e);
    public void remove(T e);
    public boolean exists(T e);
    public List<T> getAll();
    public void saveAllToBackend();
}

-

public interface EventListManager extends ListManager<Event> {
}

-

public interface ReminderListManager extends ListManager<Reminder>{    
}
    
risposta data 08.01.2016 - 14:20
fonte

Leggi altre domande sui tag