2014-10-27 3 views
5

Ich arbeite derzeit mit einer Codebase, die das Anemic Domain Model verwendet, und ich versuche, mehr Logik in die Domänenmodelle in Richtung Domänenmodell und Domäne zu verschieben Driven Design, aber ich habe mit dem folgenden Problem zu kämpfen.So legen Sie private Felder in einem Domänenmodell im Repository fest

Ich habe ein Domain-Modell namens Jobs, die wie folgt aussieht,

public class Job 
{ 
    private DateTime _someOtherDate; 
    private DateTime _lastUpdated; 

    // this may be called from many different services 
    public void SetLastUpdated() 
    { 
    _lastUpdated = DateTime.UtcNow; 
    } 
} 

An einem gewissen Punkt in der Zeit, während der Bearbeitung ein Job, den ich die Aufgabe der zuletzt aktualisiert Datum zu diesem bestimmten Zeitpunkt festlegen möge. Um dies zu tun, habe ich einen öffentlichen Setter dafür geschaffen, wie Sie oben sehen können.

Ein Problem tritt auf, wenn ich den Job aus der Datenbank in meinem Repository zurückziehe, da ich jetzt keinen öffentlichen Setter für dieses Feld habe, weil ich das auf SetLastUpdated() beschränkt habe.

Kann jemand bitte beraten, wie ich diese Eigenschaft in der Repository-Implementierung beim Abrufen des Jobs festlegen kann, aber nicht von dem Dienst, wo es auf Aufruf SetLastUpdated() beschränkt ist.

Update 1) Ich habe die Frage aktualisiert, da die Verwendung des Startdatums ein schlechtes Beispiel war.

Update 2) Aus den Antworten, die einzige Art, wie ich dies, indem sie nicht mit AutoMapper im Repository getan zu sehen ist, einen Konstruktor auf die Jobklasse Hinzufügen _lastUpdated für die Einstellung, und diese zu verwenden, wenn der Job Konstruktion in der Job-Abrufmethode des Repositorys zurückgegeben werden.

+0

Welche Art von Muster verwenden Sie, um Ihre Domänenmodelle zu hydratisieren? Verwenden Sie ein ORM oder Erinnerungsstücke? Wie sehen Ihre Konstrukteure aus? – arootbeer

+0

Ich verwende AutoMapper, um die Entity Framework-Modelle den Domänenmodellen zuzuordnen. Hier wird das Startdatum festgelegt. Auf den Domänenmodellen gibt es keine Konstruktoren, sie sind vom Design anämisch. – Jonathan

+0

Im Gegensatz zu Forum-Sites verwenden wir nicht "Danke" oder "Jede Hilfe geschätzt" oder Signaturen auf [so]. Siehe "[Sollen 'Hallo', 'Danke', 'Slogans' und 'Anrede' aus Posts entfernt werden?] (Http://meta.stackexchange.com/questions/2950/should-hi-thanks-taglines-and-salutations-be -removed-from-posts). –

Antwort

1

Ein gemeinsamer Ansatz ist Konstrukteure zu verwenden für dieses

public class Job 
{ 
    private DateTime _startDate; 

    public void Job() 
    { 
    _startDate = DateTime.UtcNow; 
    } 

    public void Job(DateTime dt) 
    { 
    // check DateTime kind, this could be source of a bug 
    if(dt.Kind != DateTimeKind.Utc) throw new ... 
    _startDate = dt; 
    } 
} 

Sie haben diese Methode/Eigenschaft eine oder andere Weise zu belichten. Wenn das Repository es einrichten kann, können Sie es von anderen Standorten aus festlegen. Wenn Sie versuchen, schlau zu sein (Sie könnten zum Beispiel mit Hilfe von Interfaces), würden Sie in ein Risiko der Overengineering geraten.

+0

Hallo oleksii, interessante Überprüfung mit .Kind, danke dafür! Ich denke mit Startdatum Im Beispiel war eine schlechte Idee. Überprüfen Sie mein Update. – Jonathan

+0

@ Jonathan C# DateTime ist Schmerz in einem Hintern zu arbeiten. Sie würden sehen, diese Ausnahme wird viel feuern. Wir hatten ein paar Bugs mit diesem. Wenn ORM (Automapper) erstellt eine neue DateTime, standardmäßig ist die Art unspezifiziert, wenn ich mich richtig erinnere.Dieses Objekt akzeptiert gerne das UTDatum, wird aber "vergessen", die UTC Art zu setzen. – oleksii

1

Einer der Hauptgründe, warum Sie DDD tun, ist die Nutzung der verwalteten Mutabilität, die durch die Kapselung bereitgestellt werden kann. Das ist immer Beginnen mit dem Konstruktor.

In der Regel ist dies die Art der Arbeit ein ORM speziell auszuführen ausgelegt ist, aber es ist auch die Vorteile von bestehenden DTOs nehmen können, die mithilfe des memento pattern auf das Domänenobjekt zugeordnet worden wäre:

public Job() { 
    _lastUpdated = DateTime.UtcNow; 
    // ... 
} 

public Job(JobData data) { 
    _lastUpdated = data.LastUpdated; 
    //... 
} 

re: AutoMapper - es ist seit einer Weile seit ich es benutzt habe, aber es sollte possible for you to instruct it to access fields using reflection in the configuration sein.

+0

Während Sie gute Punkte haben, tue ich nicht Einige Dinge verstehen: Erstens hat der Code von OP keinen Getter oder Setter für die Datumszeit Feld. Es gibt nur eine Methode, um es jetzt auf utc zu setzen. Daher kann externer Code das nicht sehen oder einstellen. Daher scheint "der aktuelle Code nicht tatsächlich zu verhindern, dass _startDate zu irgendeinem Zeitpunkt in der Zukunft geändert wird", fehlerhaft zu sein. Zweitens, das Memento-Muster ist gut für die Wiederherstellung des Zustands eines Objekts in vorherigen Zuständen. Ich kann nicht sehen, wie es in diesem Zusammenhang relevant ist. – oleksii

+0

@oleksii Die Methodensignatur, auf die ich Bezug nahm, war 'public void SetStartDate()', die das OP seither in 'public void SetLastUpdated()' editiert hat, auf die meine Aussage viel weniger anwendbar ist. Ich werde diesen Teil bearbeiten. Was das "Memento" -Muster betrifft, das gut dafür ist, den Zustand eines Objekts in seinen vorherigen Zustand zurückzusetzen - was sind die Daten in der Datenbank, wenn nicht der * vorherige Zustand * des Objekts? – arootbeer

+0

Es könnte als vorheriger Zustand betrachtet werden, wenn alle Jobs tatsächlich das gleiche Objekt wären - nur einfach verschiedene Zustände desselben Objekts aufzuzeichnen. Aber eine Tabelle würde normalerweise viele Jobs speichern, die höchstwahrscheinlich nicht die gleiche Arbeit sind.Memento speichert Mutationen desselben Objekts, aber es kann kaum dazu verwendet werden, Mutationen verschiedener Objekte auf diese Weise zu speichern. Sinn ergeben? – oleksii

2

Wie ich es sehe, haben Sie verschiedene Möglichkeiten.

Option 1

Unter der Annahme, dass Ihr Repository zwei Methoden hat:

public IEnumerable<Job> ReadAll() { ... } 
public int CreateJob(Job job) { ... } 

Sie können die Job Klasse zwei Konstruktoren geben, eine, die eine DateTime und eine, die nicht der Fall ist erfolgt.

public class Job 
{ 
    public Job(DateTime startDate) 
    { 
     this.StartDate = startDate; 
    } 

    public Job() : this(DateTime.UtcNow) 
    { 

    } 

    public DateTime StartDate { get; private set; } 
} 

verhindern dadurch nicht den Dienst der „falschen“ Konstruktor von Aufrufen, aber zumindest kommuniziert es die Möglichkeit, es ohne die startDate an den Anrufer zu rufen.

Option 2

Arbeit mit zwei verschiedenen Job Klassen.

Ihr Repository könnte wie diese Stelle aussehen:

public IEnumerable<Job> ReadAll() { ... } 
public int CreateJob(NewJob newJob) { ... } 

Und die NewJob Klasse könnte wie folgt aussehen:

public class NewJob 
{ 
    public NewJob() 
    { 
     this.StartDate = DateTime.UtcNow; 
    } 

    public DateTime StartDate { get; private set; } 
} 

Diese Absicht besser in Verbindung steht, weil die einzigen Create Methode Repository eine Instanz von NewJob akzeptiert Der Benutzer des Modells wird gezwungen, NewJob statt Job zu erstellen.

Option 3

die StartDate in dem Create Methode des repostory ignorieren und es immer DateTime.UtcNow innerhalb des Verfahrens eingestellt. Oder gehen Sie sogar so weit, einen Insert Trigger in der Datenbank zu erstellen, die es setzt.

+0

Danke für die Antwort auf Klaus. Ich denke, das Startdatum im Beispiel war eine schlechte Idee. Überprüfen Sie mein Update. – Jonathan