Understanding rich domain models and dependencies

1.3k Views Asked by At

I'm trying to get my head around rich domain models and how to build semantic functionality into domain entities, where the domain entities are not tightly coupled to objects that provide implementations for semantic behaviour

For example, I want to build a User entity into my domain model, but I want it's implementation to be driven by identity framework

class User
{
    public string Email { get; set; }
    ... All of the other IdentityUser properties...

    public void DisableUser()
    {
        ...behaviour to disable a user, most likely requires UserManager
    }

    public void AddToRole(Role role)
    {
        ... most likely requires RoleManager
    }
}

So now that I have a domain model that behaves according to the business rules, and is ignorant to persistence and implementation.

But how exactly are DisableUser() and AddToRole() supposed to work when they have no dependencies and aren't in any way coupled to UserManager and RoleManager?

  • Generally, what am I missing?
  • Should domain entities have dependencies on objects that provide behavior?
  • How should I decouple my domain model from implementation providers?
2

There are 2 best solutions below

1
On BEST ANSWER

A well crafted domain model should have no dependencies on any other architectural layers or services. With respect, domain model objects should be (in my case) POCOs (Plain Old CLR Objects). Services and layers such as business logic or persistence layers should then depend on these objects and return instances of them.

There are several keys to building a domain model that respects low coupling, high cohesion and persistence ignorance. In one statement, the secret to this is "write the code you wish you had".

Domain Model Example

public class Student
{
    // Collections should be encapsulated!
    private readonly ICollection<Course> courses;

    // Expose constructors that express how students can be created.
    // Notice that this constructor calls the default constructor in order to initialize the courses collection.
    public Student(string firstName, string lastName, int studentNumber) : this()
    {
        FirstName = firstName;
        LastName = lastName;
        StudentNumber = studentNumber;
    }

    // Don't allow this constructor to be called from code.
    // Your persistence layer should however be able to call this via reflection.
    private Student()
    {
        courses = new List<Course>();
    }

    // This will be used as a primary key. 
    // We should therefore not have the ability to change this value. 
    // Leave that responsibility to the persistence layer.
    public int Id { get; private set; }

    // It's likely that students names or numbers won't change, 
    // so set these values in the constructor, and let the persistence 
    // layer populate these fields from the database.
    public string FirstName { get; private set; }
    public string LastName {get; private set; }
    public int StudentNumber { get; private set; }

    // Only expose courses via something that is read-only and can only be iterated over.
    // You don't want someone overwriting your entire collection.
    // You don't want someone clearing, adding or removing things from your collection.
    public IEnumerable<Course> Courses => courses;

    // Define methods that describe semantic behaviour for what a student can do.
    public void Subscribe(Course course)
    {
        if(courses.Contains(course))
        {
            throw new Exception("Student is already subscribed to this course");
        }

        courses.Add(course);
    }

    public void Ubsubscribe(Course course)
    {
        courses.Remove(course);
    }
}

Granted, this domain model object was written with Entity Framework in mind, but it's a far cry from the usual Entity Framework examples (which are anemic domain models by contrast). There are a few caveats that need to be considered when crafting domain model objects in this way, but Entity Framework will persist them (with a little jiggery-pokery), and you get a domain model object that defines a clean, semantic contract to layers that depend on it.

2
On

What I do is that I have each of my rich domain model entities receive a reference to the central domain object as a constructor parameter, and store it as a readonly member.

This is easy because the domain acts as the factory of its entities, so whenever it news one of them, it passes this as the first constructor parameter. (Entities are supposed to have assembly-internal constructors so that they cannot be instantiated by anyone but the domain itself.)

And if you really dig into the documentation of ORM frameworks you will usually find that they tend to allow you to supply a factory for your entities, so you can do things like that.

So, since each entity has a reference to the domain, it can obtain from it whatever it needs to do its job. (Presumably, your domain object will contain a reference to a UserManager and to a RoleManager, no?) This is essentially taking a pragmatic step back from dependency injection: you inject the domain object with its dependencies, but you have each entity of the domain fetch its dependencies from the domain object.

Here is an example in java:

package ...
import ...

public final class StarWarsDomain extends Domain
{
    private static final Schema SCHEMA = ...

    public StarWarsDomain( LogicDomain logicDomain, S2Domain delegeeDomain )
    {
        super( logicDomain, SCHEMA, delegeeDomain ); //these get stored in final members of 'Domain'
    }

    public UnmodifiableEnumerable<Film> getAllFilms()
    {
        return getAllEntitys( Film.COLONNADE ); //method of 'Domain'
    }

    public Film newFilm( String name )
    {
        assert !StringHelpers.isNullOrEmptyOrWhitespace( name );
        Film film = addEntity( Film.COLONNADE ); //method of 'Domain'
        film.setName( name );
        return film;
    }
}