3

My question is similar to this one: Repository pattern and mapping between domain models and Entity Framework.

I have done a lot of reading on here about the following:

  1. Mapping the ORM directly to the domain model

  2. Mapping the ORM to a data model and then mapping the data model to a domain model (and vice versa)

I understand the benefits and limitations of both approaches. I also understand the scenarios where one approach is favoured over the other.

There are plenty of examples online, which show how to do option 1. However, I cannot find any example code, which shows how to do option 2. I read questions on here about option two like the one referenced on the first line of this post i.e. the question is about option two but the answer is about option one - and there are comments that state that option two may be more appropriate.

Therefore my question is specifically about how to do option one from a mapping and validation perspective:

Mapping

I believe I can do this when mapping the Domain Model to the Data Model:

public PersonDomain GetById(Guid id)
{
    return AutoMapper.Mapper.Map<PersonDomain>(Session.Get<PersonData>(id)); 
}

I believe I have do this when mapping the Data Model to the Domain Model in the repository (to protect the invariants):

protected PersonDomain ToPersonDomain(PersonData personData) 
{
    return new PersonDomain(personData.ID, personData.Name, personData.DateOfBirth);
}

Validation

I want to do this in the PersonDomain class:

public class PersonDomain
{
   public Guid ID{ get; private set; }
   public DateTime DateOfBirth { get; private set; }
   public string Name { get; private set; }

   public PersonDomain(Guid id, DateTime dateOfBirth, string name)
   {
       if (id == Guid.Empty())
           throw new ArgumentException("Guid cannot be empty");
       if (name =="")
           throw new ArgumentException("Name cannot be empty");
       ID = id;
       Name = NAME;
       DateOfBirth = dateOfBirth;
   }
}

However, every example I find tells me not to put validation in the constructor. One idea I had was to avoid primitive obsession as follows:

public class PersonDomain
{
   public ID ID{ get; private set; }
   public DateOfBirth DateOfBirth { get; private set; }
   public Name Name { get; private set; }

   public PersonDomain(ID id, DateOfBirth dateOfBirth, Name name)
   {
       if (id == null)
           throw new ArgumentNullException("ID cannot be null");

       if (name ==null)
           throw new ArgumentNullException("Name cannot be null");

       ID = id;
       Name = name;
       DateOfBirth = dateOfBirth;
   }
}

However, in this case; there is still validation in the constructor.

Questions

My two questions are:

  1. Have I understood the mapping between the Domain Model and Data Model (and vice versa) correctly or is there a more elegant way of approaching this (the mapping between the data model and domain model and vice versa)?

  2. Should I be putting any validation logic in the constructor of the PersonDomain Entity in this case?

Update 27/02/18

This link helped me most: http://www.dataworks.ie/Blog/Item/entity_framework_5_with_automapper_and_repository_pattern

Amit Joshi
  • 15,448
  • 21
  • 77
  • 141
w0051977
  • 15,099
  • 32
  • 152
  • 329
  • 1
    You might want to have a look at https://vaughnvernon.co/?p=879 – guillaume31 Feb 20 '18 at 13:15
  • @guillaume31, Thanks. Does he discuss this approach in his book? What chapter? May have to buy the book. – w0051977 Feb 20 '18 at 15:49
  • As far as I remember, he doesn't. The code in the book is in Java + Hibernate. – guillaume31 Feb 20 '18 at 16:20
  • @guillaume31, thanks. Do you know any real world examples - perhaps on Github? – w0051977 Feb 20 '18 at 16:26
  • Not really. I've used variants of what he explains in his blog post and it works well. Note that with NHibernate you may not have the same technical limitations as Entity Framework and might prefer option 1). – guillaume31 Feb 20 '18 at 16:46
  • @guillaume31, you lose some of the OO benefits when mapping NHibernate to the domain model e.g. you cannot seal classes; you have to make members virtual; you have to have zero argument constructors etc. Do you know of any more code samples (maybe not full examples)? Not sure if I like having a separate object for state as a member of a domain object. – w0051977 Feb 20 '18 at 16:51
  • Older versions of Entity Framework used to impose much worse than that on your entity classes - until not long ago NHibernate was a [better option](https://lostechies.com/jimmybogard/2014/05/05/missing-nhibernate-features-in-entity-framework/). EF Core improved a lot of things but I don't know if the tables have turned completely. I also don't know if you're able to use the latest versions or must do with legacy code. – guillaume31 Feb 20 '18 at 18:29
  • And yes - backing state object is not a silver bullet. It basically requires to add a state field and a hydration method on the domain object that takes the state object as input. You can "hide" them in a partial class with a narrow access modifier but they're still there. There's no magic here. – guillaume31 Feb 20 '18 at 18:32
  • @guillaume31, please see the hyperlink on the first line of my post. What do you think of the approach described in the question? – w0051977 Feb 20 '18 at 20:01
  • I think it's getting a bit complicated just to avoid some shortcomings of an ORM, especially if Automapper joins the party. – guillaume31 Feb 21 '18 at 09:08

2 Answers2

4

every example I find tells me not to put validation in the constructor.

I think you need to find more examples.

It may help to think about what's going on at a deeper level. Fundamentally, what we are trying to do is ensure that a precondition holds. One way to do this is to verify the precondition "everywhere"; but the DRY principle suggests that we would prefer to capture the precondition at a choke point, and ensure that all code paths that require that precondition must pass through that choke point.

In Java (where DDD began) and C#, we can get the type system to do a lot of the heavy lifting; the type system enforces the guarantee that any use of the type has gone through the constructor, so if we establish in the constructor that the precondition holds, we're good to go.

The key idea here isn't "constructor", but "choke point"; using a named constructor, or a factory, can serve just as well.

If your mapping code path passes through the choke point, great.

If it doesn't..., you lose the advantage that the type checking was providing.

One possible answer is to make your domain model more explicit; and acknowledge the existence of unvalidated representations of domain concepts, which can later be explicitly validated.

If you squint, you might recognize this as a way of handling inputs from untrusted sources. We explicitly model untrusted data, and let our mapping code produce it for us, and then within the domain model we arrange for the untrusted data to pass through the choke points, and then do work on the sanitized variants.

Domain Modeling Made Functional covers this idea well; you can get a preview of the main themes by watching Scott Wlaschin's talk Domain Driven Design with the F# type System

VoiceOfUnreason
  • 52,766
  • 5
  • 49
  • 91
  • If you start seeing your database as an untrusted source (which you could, depending on the criticality of your software), you have to consider the cost of having a second invariant enforcement layer in addition to the public facing one, because data is not always assigned the same way during rehydration and during the normal course of a use case. Maybe having an immutable event-sourced data store and hydrating your aggregates by replaying all events through their public interface instead is both more secure and more maintainable then. – guillaume31 Feb 21 '18 at 15:33
0

1) Have I understood the mapping between the Domain Model and Data Model (and vice versa) correctly or is there a more elegant way of approaching this (the mapping between the data model and domain model and vice versa)?

I would say that the ORM should map the Domain Model (Entity) to the database while you would use a Data Model for representing data to the outside world (UI, REST...).

2) Should I be putting any validation logic in the constructor of the PersonDomain Entity in this case?

It's ok to put domain validation logic into the domain object constructor. But if you want to do validation that is UI specific it should probably be done in some validation class mapped to the Data Model so that you can return a nice error to the user.

Yitzchok
  • 691
  • 3
  • 18