5

I am trying to follow the DDD approach to constructing entities where the properties have private setters and public getters and assignment is done through the constructor. The problem with this approach seems to be when you hit a table that has alot of columns, we have one that has 40 columns at least. It becomes a nightmare quickly. I found some articles that seem to point to either a Fluent Interface or Factory Pattern. It seems like 40 columns, though cleaner could still get out of control even with these patterns. The columns are relevant to the table design and don't violate SRP. Also, in trying to maintain the table size and maybe break the entity into smaller value objects based upon a logical grouping it still seems as if some of the value objects are going to be large. Could someone point me in the right direction of how to handle this situation without breaking DDD?

user1790300
  • 2,143
  • 10
  • 54
  • 123
  • 1
    DDD is very hard (maybe sometimes impossible) to retrofit to badly designed, database-centric systems. – Adrian Thompson Phillips Feb 16 '15 at 16:55
  • Are you saying that DDD is a bad fit for tables with large columns? That logic kind of seems limiting. It seems like a table with large number of columns is not all that unrealistic from a database standpoint. – user1790300 Feb 16 '15 at 18:02
  • 1
    In DDD, your chosen persistence mechanism is an after-thought. You would model a specific domain, encapsulating the domain knowledge in entities, value objects and services. I worry that modelling your new DDD system on an existing system with 40 column tables would be the wrong way to go about things. I can't imagine if I had a legacy data-centric system with a 40 column `Customer` table, how maybe 25 to 30 of those column should probably not live in my `Customer` entity. To take 40 cols into my new `Customer` entity would be a design based on building upwards from the database. – Adrian Thompson Phillips Feb 16 '15 at 22:45
  • 1
    I really agree with Adrian in his comment. You should really look into designing your domain, NOT THINKING about the database. How you save your data and convert it to the existing database tables should be done later. If you have the option (no other systems using the database) of migrating and changing the database, that would also help. – Eugène Feb 17 '15 at 07:55

1 Answers1

2

It is not to say that DDD is a bad fit for wide columns. Hiding a setter is also not a specific requirement of DDD, but plain old Encapsulation. (Protecting the modification of your data.) If you have to set a lot of these values and you find yourself muddling your code, then yes as you mentioned, move the construction to a Factory.

The secret of course is to not have any consuming code just assign any value to your domain object without following the proper "Domain Logic", hence the hiding of the setters. Sometimes of course you have to set all those values to your domain object, and the values come from some dto or mvc model or something, then using a Mapper class that can map/assign the values are a great way of keeping your consuming code clean.

You could even look into using something like AutoMapper by Jimmy Bogard: http://automapper.org/ which would also have no problem assigning values to private setters by the way.

If you are loading data using an ORM, some of them support private setters and/or backing fields. NHibernate for example allows this in your mapping file:

this.Property(x => x.Description, mapper => mapper.Access(Accessor.Field))

As per my comment, use something like AutoMapper to alleviate the writing of lots of mapping code. Wrap the implementation in an injectable class such as below which would allow you to replace the implementation in future without performing shotgun surgery.

public class ViewMapper<TModel, TDomain> : IViewMapper<TModel, TDomain>
{
    public TDomain MapToDomain(TModel dataItem)
    {
        return Mapper.Map<TModel, TDomain>(dataItem);
    }

    public List<TDomain> MapToDomain(IEnumerable<TModel> dataItems)
    {
        return dataItems.Select(this.MapToDomain).ToList();
    }

    public TModel MapToData(TDomain domainItem)
    {
        return Mapper.Map<TDomain, TModel>(domainItem);
    }

    public void MapToOriginalData(TDomain domainItem, TModel dataItem)
    {
        Mapper.Map(domainItem, dataItem);
    }

    public List<TModel> MapToData(IEnumerable<TDomain> domainItems)
    {
        return domainItems.Select(this.MapToData).ToList();
    }
}

AutoMapper is highly configurable, and should be able to handle most cases. Setup a mapper profile where you can tell it exactly what to do during mapping:

public class ViewItemProfile : AutoMapper.Profile
{
    protected override void Configure()
    {
        Mapper.CreateMap<Domain, View>()
            .ForMember(x => x.ErrorRequestId, y => y.MapFrom(z => z.ErrorTypeId))
            .ForMember(x => x.Irrelevant, y => y.Ignore());
    }
}
Eugène
  • 676
  • 5
  • 10
  • That is a good point. The majority of the information is coming from user input via a ViewModel. In this case, should I include all of the fields from the database in the constructor parameter list or should I be selective about what is in the parameter list? Is just seems like does not truly represent the database. – user1790300 Feb 16 '15 at 18:56
  • Yes, you should not be trying to mimic the database. the point of DDD is to model the business solution, how you persist that to a DB/File/MemoryStream or whatever is an outside concern. Constructors demand things that makes the object construct in a valid state; not necessarily just all the data it carries. I'd say be more selective in what you pass in the constructors. If most your cases is via ViewModel, use a mapper, and even try a framework that will make things easier. i'll add a mapper to my answer. – Eugène Feb 16 '15 at 19:22
  • Nice. Up to this point I was using mapper the bad way. This is very clean alternative. Thank you, this is really clean. I have some refactoring to do :-). Just thought about something. Would I need to add a zero parameter constructor for AutoMapper if I am going to use this approach? – user1790300 Feb 16 '15 at 19:58
  • Yes, AutoMapper does need a default constructor. -yet another proof that you sometimes gotta give a little here and there. Also you will note that sometimes your ORM will leak a little of it's implementation into your domain -like Attributes, or an Interface or something. All tradeoffs... – Eugène Feb 16 '15 at 20:03
  • Yeah, there are always tradeoffs. I doesn't seem like I am going too far out of DDD wheel house, while simplifying an otherwise daunting task. Thank you this is a huge help. – user1790300 Feb 16 '15 at 20:13