Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to set private fields on a domain model in the repository

I am currently working with a codebase that uses the Anaemic Domain Model, and I am trying to move more logic into the domain models in a move towards a Domain Model and Domain Driven Design, but I am struggling with the following problem.

I have a domain model called Job which looks like this,

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

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

At some point in time, during the processing a job I want to set the job's last updated date to that specific point in time. To do this I have created a public setter for it as you can see above.

An issue arises when I am pulling back the Job from the database in my repository, as I now have no public setter for this field because I have restricted that to SetLastUpdated().

Can someone please advise on how I could allow this property to be set in the repository implementation when retrieving the job, but not from the service where it is restricted to calling SetLastUpdated().

Update 1) I have updated the question as using start date was a bad example.

Update 2) From the answers given, the only way I can see this being done is by not using AutoMapper in the repository, adding a constructor onto the Job class for setting _lastUpdated, and using this when constructing the Job to be returned in the repository's job retrieval method.

like image 424
Jonathan Avatar asked Oct 27 '14 15:10

Jonathan


2 Answers

As I see it, you have various options.

Option 1

Assuming that your repository has two methods:

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

You can give the Job class two constructors, one that takes a DateTime and one that does not.

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

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

    }

    public DateTime StartDate { get; private set; }
}

This does not prevent the service from calling the "wrong" constructor, but at least it communicates the option of calling it without the startDate to the caller.

Option 2

Work with two different Job classes.

Your repository could look like this instead:

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

And the NewJob class could look like:

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

    public DateTime StartDate { get; private set; }
}

This communicates intent better, because the repository's Create method only accepts an instance of NewJob so the user of the model will be forced to create a NewJob instead of a Job.

Option 3

Ignore the StartDate in the repostory's Create method and always set it to DateTime.UtcNow within the method. Or even go so far as to create an Insert trigger in the database that sets it.

like image 59
Klaus Byskov Pedersen Avatar answered Oct 28 '22 01:10

Klaus Byskov Pedersen


A common approach is to use constructors for this

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;
   }
}

You have to expose this method/property one way or another. If repository can set it, you can set it from other locations. If you try to be smart about this (you could, for example by using interfaces), you'd get into a risk of overengineering.

like image 1
oleksii Avatar answered Oct 28 '22 01:10

oleksii