简体   繁体   中英

DDD encapsulation and the repository pattern

Say I have this simple class

public class MyEntity
{
    public DateTime DateUpdated { get; private set; }
    public string Author { get; private set; }
    public string Comment { get; private set; }

    public void AddComment(string comment, string author)
    {
        Author = author;
        Comment = comment;
        DateUpdated = DateTime.Now;
    }
}

I have made the setters private to encapsulate the class and added the AddComment method to add some behaviour to my class. This works perfectly fine when creating a new object but when I want to load the Entity from the db the DateUpdated is of course set to the current date which I would like to avoid.

Is there any patterns I could use to avoid making the DateUpdated setter public as that does seem to break my nice encapsulation and messing up the clean interface of the class? The class is of course just an example of a more generic problem.

The closest I have got to now without making more public constructors is creating a private constructor which I access through a public static method.

Use a constructor that takes parameters matching the fields of the object.

This will allow you to populate the objects on startup and keep them immutable.

public MyEntity(DateTime dateUpdated, string author, string comment)
{
  DateUpdated = dateUpdated;
  Author = author;
  Comment = comment;
}

Look into the Memento pattern for re-hydrating your object. Use the constructor only for creating a new instance.

You can overload the AddComment method like so:

public class MyEntity
{
    public DateTime DateUpdated { get; private set; }
    public string Author { get; private set; }
    public string Comment { get; private set; }

    public void AddComment(string comment, string author)
    {
        Author = author;
        Comment = comment;
        DateUpdated = DateTime.Now;
    }

    public void AddComment(string comment, string author, DateTime dateUpdated)
    {
        Author = author;
        Comment = comment;
        DateUpdated = dateUpdated;
    }
}

If you are using an ORM such as NHibernate to implement the repository, then it will assign values to properties based on data from the database even if the properties are private set. In other words, it bypasses the AddComment method and injects data directly. This makes sense because when reconstituting an entity, behavior doesn't repeat, only the data needs to be copied. NHibernate does require the entity to contain a protected parameter-less constructor. If using your own ORM implementation, then you can employ the constructor pattern as suggested by Oded because in that case your entity can truly remain persistence ignorant.

If the repository responsible for creating these objects lives in the same assembly, you should check out the internal access modifier . If this fits your project's needs, you can implement it in one of two ways...

  1. Change your setters from private to internal . The creator would then just set the values of the properties after instantiation.
  2. Add an internal constructor that accepts a value for all the properties and sets them up.

Either way, you can still allow change through public methods as you demonstrated in your example.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM