0

According to this article: Using Ids in Domain Models is not a good practice. We should reference the entire domain model instead.

In this case, when we want to provide only Getters for our properties, we have no choice but to use the constructor parameters to pass all the data.

example :

class Car {

    public Car(string name, Model model, Engine engine){
        Name = name;
        Model model = model;
        Engine = engine;
    }

    public string Name {get;}
    public Model Model {get;}
    public Engine Engine {get}
}

In a case where we receive a DTO from the front end for example:

class CarDto {
    public string Name {get;set;}
    public long ModelId {get;set;}
    public long EngineId {get;set;}
}

How to construct the domain model from this?

  • Should we fetch the Engine and Model from the database by their ids before building the domain model ?

Also in the case when in our Domain Model we have some complex properties that are not always set, is it enough to just pass null as a parameter, or they shouldn't appear at all in the constructor, and give them a Setter ?

Any other suggestion is welcome.

3

Q: Should we fetch the Engine and Model from the database by their ids before building the domain model ?

Yes, this's what repositories do. If Car is an aggregate, the repository should be capable of fetching whatever Car needs for its initialization. It might load Engine and Model directly from the data store or ask for them to other components. You also could use services to retrieve Engine and Model and send'em to the repository alongside with the attribute name.

Q: Also in the case when in our Domain Model we have some complex properties that are not always set, is it enough to just pass null as a parameter, or they shouldn't appear at all in the constructor, and give them a Setter ?

If any of the attributes are optional, it's important to make this as clear and explicit as possible. This said, it's preferable to take them off the constructor and define setters (or methods with the due parametrization) because it simplifies the code and improves readability.

To initialise entities in different states, you could overload constructors or create static factory methods.2

If you are concerned about the access to the setters you have different ways to control it. The first that comes to mind is through access modifiers. However, you could rely on well-known OOP design practices (inversion of control or interface segregation to mention any) or design patterns.1

S: Using Ids in Domain Models is not a good practice

That's fairly true, overall when we turn the persistence model into the domain one. In such cases, the implementation details of the persistence bypass the boundaries of the persistence and become part of the implementation detail of the domain too. Raw strings and numbers become identifiers when, from the DDD standpoint, they are most likely the worst representation for an identifier. In DDD, identifying entities and aggregates might take something else than a simple string.

Since @Casablanca has mentioned Vaughn Vernon, I will share another link where he speaks about correlating boundaries (aggregates) through identifiers.


1: Defensive programming as security is a matter of costs vs benefits. Don't go crazy making everything private (narrowing down scopes to the absurd) at expenses of simplicity and readability. Unless you have a good reason for it.

2: Some languages implement the notion of optional parameter. As soon the intention is clear (what's optional and what's not), these parameters should work too.

4

Contrary to what the article says, it is actually considered best practice to reference other aggregates by ID, and exactly to solve the kind of issue you are facing (having to load a bunch of related entities to construct the root object, which then leads to performance issues). There's an excellent series of articles by Vaughn Vernon that talks about this in detail.

Now on to specific issues with the article you linked:

Ids are essentially persistence logic implementation details; they have no relation to your domain.

This is false. An entity, by definition, has a unique identity and this has nothing to do with ORM or other persistence frameworks (even though they may use the same ID). Eric Evans even mentions in his book that an entity may have multiple representations in code as well as in persistence mechanisms, yet the only common thread across all representations is its identity.

... the use of Ids breaks entities’ encapsulation (aka Law of Demeter, aka Tell Don’t Ask):

// Seems nice
if (shipment1.Id == shipment2.Id)
{
}

// Still not bad
if (shipment1.Customer.Id == shipment2.Customer.Id)
{
}

This example misses the point of identity. If two shipment IDs are the same, they represent the same physical shipment. There is no need to "violate" the Law of Demeter and check anything else.

In this example, Ids can be encapsulated in a separate entity:

public void Deliver(Shipment shipment)
{
    if (_existingShipments.Contains(shipment))
    {
        // Attach to existing shipment
    }
    else
    {
        // Create new one
    }
}

The author does not explain why the whole entity is needed here. The "contains" check can be done just as well with only a shipment ID and it saves you from having to load a bunch of unnecessary data.

0

OK, so the article is weird and wrong. Erase it from your memory.

Secondly, don't use DTOs. serialise and deserialise your object.

Thirdly, just use public setters on domain objects. You really aren't doing yourself any good by trying not to have them.

You can then have:

Customer c = Json.DeserialiseObject(jsonstring)

You don't even have to write the serialiser! (thanks newtonsoft!)

Also, you don't even have to call the deserialiser! (thanks Asp.Net!)

This is the standard way. You should do this

If you put a gun to my head and told me no public setters, no reflection, no tricks with internal or protected no InternalsVisibleTo I would probably go with making everything partial classes.

or there is a trick with inner classes (this is a bad way, don't do this)

public partial class Customer
{
    public class CustomerJsonDeserialiser
    {
        public Customer From(string json)
        {
            //now i can set Customer's private properties and I'm in a different class and file
        }
    }
}

Or if you are deserialising from a database

public partial class Customer
{
    public class CustomerDatabaseDeserialiser
    {
        public Customer From(IDataSet ds)
        {
            //now i can set Customer's private properties and I'm in a different class and file
        }
    }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.