Arkej
Arkej

Reputation: 23

How to model entity composed of other entities in DDD?

Let's have a machine(entity) that is composed by several devices(entity) of different types. We have a method on the machine to start it up and that method shall be calling Initialize method of the devices that make up the machine. I'm having problems how to model this. If I look from the startup perspective it is clearly a machine's behavior and it belongs to the machine entity. But implementation needs to delegate to the various device entities. I might be modeling machine as an aggregate root and have a collection of devices in there. But each device will probably be an aggregate root too for the behaviors it perform. As for what I've found so far I can use double dispatch in the startup method and pass in the collection of devices. In this case the caller gets the responsibility of providing this collection. But why since the machine entity does know what devices is composed of? Injecting a repo for every single device into machine is obviously not ok. Delegating the startup method into a domain service would also be an option but isn't that a clear machine's entity behavior? Any other idea? How would be best to model such case following DDD guidelines?

class Machine
{
   DeviceKey[] Devices{ get;}

   void Startup()
   {
      foreach(var dvcKey in Devices)
      {
        //how to get the entity for the given dvcKey?
        //so we can call init on it? like
        dvc.Initialize();
      }     
   }
}

Further info: First, thanks for answering and reading this. The code is just proto code since the question is conceptual, if I write the invariants, use cases, transactional analysis etc it will become a huge specification irrelevant to the patterns? Of course the domain is complex, we are modeling a physical machine with various devices, each device will perform different logic, some of logic, validation of state of the machine for example is performed across all the devices(each device is though a candidate for an ar?). We have several use cases, as one user would operate the machine, like startup, shutdown, other users will use the machine(use sessions etc...) The domain is full of entities, none of them dumb. How would you approach such aggregate(s)?

I'm adding more details here although I feel we'll have to just go one way and eat what we cook. Our first attempt was to have machine as the aggregate root and devices are part of it like so:

class Machine
{
  Device1Type Device1{}
  Device2Type[] Devices2{}
  void Startup()
  {
    Device1.Initialize();
    foreach(var dvc in Devices2)
    {
      dvc.Initialize();
    }
    //etc...
  }
}

But this would cause concurrency(consistency) problems since every device would have been taken from the machine's repo to perform its behaviors. Also the states of various devices are persisted with the root(machine) and for a device to perform its behavior the whole machine's state needs to be loaded - might have performance issues too. So we modeled the devices away from the machine as aggregates. We now have the problem from the question. How to access and delegate to other aggregates(devices) from one aggregate(machine) without leaking into anemia? I guess we'll just go with a domain service for the startup operation since it might come out it is a process and not just an operation/method. Thanks again for reading.

Upvotes: 0

Views: 1720

Answers (2)

Alexey Zimarev
Alexey Zimarev

Reputation: 19610

Your aggregate boundaries are not clear. Since we don't know much about your domain, I can only guess. You say that a Machine consists of Devices. You call them both entities but it looks like you are aggregating it all under the Machine. But you also say devices operate independently, so they need to be started when the machine is started.

Have you though well about your aggregate boundaries? For me it looks like devices are aggregates on their own. Then, your command to start up the machine, would also include sending commands to each device to initialise, but this is not necessarily done synchronously, it looks more like a long running process, where we try to initialise all devices and collect responses, then observe if any failure has occurred, then change the start up status of the machine.

In your current approach the Machine has too much responsibility. What will happen if in your foreach one device crashes on start up? In which state your Machine will be? Will it be a valid or invalid state? What will you do with devices that have started already? What is one device is not critical but you halt the whole process if it does not initialise? You see, there are too many questions, your process seems to be oversimplified. As I mentioned, I more lean towards seeing this as a orchestration between independent aggregates.

Check this article by Udi Dahan called Race Conditions Don't Exist, it might help.

Upvotes: 2

Paweł Hemperek
Paweł Hemperek

Reputation: 1180

Like @guillaume31 stated in comment - it's impossible to really answer this question without knowing anything about your domain. I have however have few possible helpful tips, which may point you in right direct direction.

Use domain service

Whenever you feel the need to perform some operation which doesn't really fit into one of your aggregates, you should consider using domain service - one of DDD building blocks. I can't say this will solve all your problems - you know your domain best, so you can decide, whether usage of domain service will be helpful or not.

Do some actual modelling!

How would be best to model such case following DDD guidelines?

There is no universal answer to this question. Refining which aggregates should exist in your code and drawing responsibility boundaries between them is not a trivial task. You should be following ubiquitous language, watch for aggregates to be cohesive and really think about invariants these aggregates should protect. I would say that thinking about all those things at once is no one-man job - whole development team should participate in the modelling phase.

One of the most powerful and fun technique is Event Storming, invented by Alberto Brandolini. Event Storming allows you to quickly explore difficult business processes and also do some modelling with DDD in mind - see Big Picture Event Storming and Design Level Event Storming. Few Event Storming sessions may help you to answer questions about designing your aggregates.

Those tips may not answer your question directly, but I hope there will serve as useful guidance.

Upvotes: 1

Related Questions