DDD (Domain Driven Design), how to handle entity state changes, and encapsulate business rules that requires large amount of data to be processed

后端 未结 4 887
萌比男神i
萌比男神i 2021-01-31 04:47
public class Person
{
    public IList SpecialBirthPlaces;
    public static readonly DateTime ImportantDate;
    public String BirthPlace {get;set;}

             


        
4条回答
  •  佛祖请我去吃肉
    2021-01-31 05:17

    The way I've encapsulated this problem, which is modification tracking, is with the Unit of Work pattern. I have my DDD repositories associated with a unit of work, and I can query the unit of work for any set of entities which I get from any of the repositories to see which are modified.

    As for the large collection, it appears to be a read-only set. One way to handle this is to preload and cache this locally if it is ever accessed, then the repositories can run queries against the in-memory version. I use NHibernate, and it is easy to handle this case with it. If it is way too big to store in RAM (like 100s of MB or more), you'll probably need to special case repository queries against it, so that the SpecialBirthPlaces.Contains(BirthPlace) query is executed on the database (perhaps in a stored proc, ha!). You'd probably want to express SpecialBirthPlaces as a repository of entities, rather than just a big collection of strings, which would allow the "Query" pattern to free you from needing to load the entire thing.

    After this lengthy narrative, here's some example:

    public class BirthPlace
    {
        public String Name { get; set; }
    } 
    
    public class SpecialBirthPlace : BirthPlace
    {
    }
    
    public class Person 
    {
        public static readonly DateTime ImportantDate;
        public BirthPlace BirthPlace { get; set; } 
    
        public DateTime BirthDate 
        { 
            get; private set;
        } 
    
        public void CorrectBirthDate(IRepository specialBirthPlaces, DateTime date)
        {
            if (BirthPlace != null && date < ImportantDate && specialBirthPlaces.Contains(BirthPlace)) 
            { 
                BirthPlace = specialBirthPlaces.GetForDate(date); 
            }
        }
    } 
    

    Having a method where you pass in the corrected birth date is a better design since it tells you via the parameters what is needed to actually correct the birth date: a repository (i.e collection) of SpecialBirthPlace entities and the correct date. This explicit contract makes it clear what the domain is doing, and makes the business needs clear just by reading the entity contracts, where putting the whole collection in the state of the entity hides it.

    Now that we've made BirthPlace into an entity, we can see that there may be one more optimization to make the domain model a bit flatter. We don't really need to specialize BirthPlace but we do need to indicate if it is special. We can add a property to the object (some people begrudge properties on domain objects, but I don't, since it makes queries easier, especially with LINQ) to indicate if it is special. Then we can get rid of the Contains query altogether:

    public class BirthPlace
    {
        public BirthPlace(String name, Boolean isSpecial = false)
        {
            Name = name;
            IsSpecial = isSpecial
        } 
    
        public String Name { get; private set; }
        public Boolean IsSpecial { get; private set; }
    }
    
    public class Person 
    {
        public static readonly DateTime ImportantDate;
        public BirthPlace BirthPlace { get; set; } 
    
        public DateTime BirthDate 
        { 
            get; private set;
        } 
    
        public void CorrectBirthDate(IRepository birthPlaces, DateTime date)
        {
            if (BirthPlace != null && date < ImportantDate && BirthPlace.IsSpecial) 
            { 
                BirthPlace = birthPlaces.GetForDate(date); 
            }
        }
    } 
    

提交回复
热议问题