2015-06-12 10 views
5

Ho un immutabile User un'entità:Come evitare un modello anemico di dati? I repository possono essere iniettati in entità?

public class User { 
    final LocalDate lastPasswordChangeDate; 
    // final id, name, email, etc. 
} 

ho bisogno di aggiungere un metodo che restituisce informazioni se la password dell'utente deve essere cambiato I.D. non è stato modificato per oltre l'impostazione di sistema passwordValidIntervalInDays.

L'approccio attuale:

public class UserPasswordService { 
    private SettingsRepository settingsRepository; 

    @Inject 
    public UserPasswordService(SettingsRepository settingsRepository) { 
    this.settingsRepository = settingsRepository; 
    } 

    public boolean passwordMustBeChanged(User user) { 
    return user.lastPasswordChangeDate.plusDays(
     settingsRepository.get().passwordValidIntervalInDays 
    ).isBefore(LocalDate.now()); 
    } 
} 

La domanda è come rendere il codice di cui sopra più orientato agli oggetti ed evitare il modello di dominio anemico antipattern? Se il metodo passwordMustBeChanged essere spostato User se così come accedere SettingsRepository, dovrebbe essere iniettato nel costruttore User s', o dovrebbe un'istanza Settings essere fornita al ctor, o qualora il metodo passwordMustBeChanged richiedere un'istanza Settings da fornire?

Il codice di Settings e SettingsRepository non è importante, ma per completezza, eccolo:

public class Settings { 
    int passwordValidIntervalInDays; 
    public Settings(int passwordValidIntervalInDays) { 
    this.passwordValidIntervalInDays = passwordValidIntervalInDays; 
    } 
} 

public class SettingsRepository { 
    public Settings get() { 
    // load the settings from the persistent storage 
    return new Settings(10); 
    } 
} 
+0

Ho letto http://stackoverflow.com/questions/5694241/ddd-the-rule-that-entities-cant-access-repositories-directly e http://stackoverflow.com/questions/827670/is-it-ok-per-entity-to-access-repository ma nessuno di loro sembra rispondere alla mia domanda –

+0

Dai un'occhiata a [this] (https://blog.inf.ed.ac. uk/sapm/2014/02/04/the-anemic-domain-model-is-no-anti-pattern-its-a-solid-design /) –

risposta

4

Per una politica di scadenza della password a livello di sistema il tuo approccio non è così male, a patto che il UserPasswordService è un servizio di dominio, non un servizio di applicazione. L'incorporamento della politica di scadenza della password nell'Utente sarebbe una violazione di SRHO IMHO, che non è molto meglio.

Si potrebbe anche prendere in considerazione qualcosa di simile (dove la fabbrica è stata inizializzata con le impostazioni corrette):

PasswordExpirationPolicy policy = passwordExpirationPolicyFactory().createDefault(); 
boolean mustChangePassword = user.passwordMustBeChanged(policy); 


//class User 
public boolean passwordMustBeChanged(PasswordExpirationPolicy policy) { 
    return policy.hasExpired(currentDate, this.lastPasswordChangeDate); 
} 

Se alla fine la politica può essere specificato per i singoli utenti, allora si può semplicemente memorizzare gli oggetti di politica su User.

È inoltre possibile utilizzare l'ISP con il progetto corrente e implementare un'interfaccia PasswordExpirationPolicy sul servizio UserPasswordService. Questo ti darà la flessibilità di refactoring in oggetti di policy reali in seguito senza dover cambiare il modo in cui lo User interagisce con il criterio.

Se si ha un oggetto Password valore che si può anche fare le cose un po 'più coesa, per avere qualcosa di simile (la data di creazione della password verrebbe incorporata la password VO):

//class User 
public boolean passwordMustBeChanged(PasswordExpirationPolicy policy) { 
    return this.password.hasExpired(policy); 
} 
+0

grazie, ho scelto il "tuo approccio non è così male" soluzione :) –

+1

@AdamSiemion Per un design più OOP basta implementare un'interfaccia PasswordExpirationPolicy sul tuo servizio e poi puoi fare 'user.passwordMustBeChanged (yourDomainSer Vice) '. Avrai meno attriti se il tuo progetto cambierà alla fine e non sarà davvero un enorme refactoring. – plalx

+0

Creare oggetti VALORE espliciti simili a predicati per scopi specializzati. Una SPECIFICA è un predicato che determina se un oggetto soddisfa o meno alcuni criteri. –

1

solo per buttare fuori un altro la soluzione possibile sarebbe quella di implementare un processo di lunga durata che potrebbe eseguire il controllo di scadenza e inviare un comando a un PasswordExpiredHandler che potrebbe contrassegnare l'utente con una password scaduta.

+0

Volevo solo aggiungere che non è un'opzione che si escluda a vicenda con ciò che abbiamo proposto. Anche con un processo di lunga durata, è necessario un modo pulito per controllare la politica. – plalx

+0

@plax concordato. La cosa bella del processo a lungo termine è che puoi cuocere altri tipi di cose all'interno del flusso di lavoro, come inviare un promemoria amichevole quando la loro password sta per scadere. – Marco

0

ho inciampato su un documento che fornisce una risposta alla mia domanda:

Un problema comune per l'applicazione DDD è quando un'entità richiede l'accesso ai dati in un archivio o di altro gateway, al fine di effettuare una operazione di business. Una soluzione consiste nell'iniettare le dipendenze del repository direttamente nell'entità, tuttavia questo spesso è disapprovato. Una ragione per questo è che richiede che gli oggetti plain-old- (C#, Java, ecc.) Implementino le entità come parte di un grafico di dipendenza dell'applicazione.Un'altra ragione è che rende più difficile ragionare sul comportamento delle entità dal momento che il Principio della singola responsabilità è violato. Una soluzione migliore consiste nell'avere che un servizio applicativo recuperi le informazioni richieste da un'entità, configurando in modo efficace l'ambiente di esecuzione e fornendolo all'entità.

http://gorodinski.com/blog/2012/04/14/services-in-domain-driven-design-ddd/