TransWikia.com

DDD: delete tree "Node" should check for children

Software Engineering Asked on March 4, 2021

I have a tree of Installations: Aggregate Installation has a property ParentInstallationId.

I also have a RemoveInstallationCommand –> repo.GetInstallation(x).Remove()

Now, I want to throw a domain error if I receive a RemoveInstallationCommand for an Installation that has children. But, the Installation aggregate doesnt know about its children, its the other way around.

So, is it ok to query the command model for children, and throw if count > 0?

If not, how would you solve this?

2 Answers

The root of the problem here is the misunderstanding that an aggregate is somehow able to "remove" itself. Can an Installation "add" itself? You are missing a concept:

// within RemoveInstallationCommandHandler

var wizard = wizards.Find(cmd.InstallationId);

wizard.RemoveInstallation(); // could throw

wizards.Save(wizard);

Above we have the concept of an InstallationWizard that "knows" about a given Installation and all of it's children (and possibly any other dependencies). This makes it the ideal place to both add and remove installations as it is privy to all of the information necessary to coordinate the above activities.

The broader message here is that domain objects do not just appear/disappear! Especially if there are domain rules mediating either process.

Answered by king-side-slide on March 4, 2021

If I understand your idea correctly then what you currently aiming is something like this:

var installation = repo.GetInstallation(id);
var installationChildren = repo.GetChildren(id);

if(installationChildren.Any()){
    throw new InvalidOperationException();
}
else{           
    installation.Remove();
}

So you make a query and based on result you make a decision.

Aggregate represents boundary of consistency. If you say that Installation is an aggregate, but it doesn't know about its children, then those children are out of your aggregate boundary.

Problem is that in order to preserve consistency in that 'Remove' operation, those children need to be part of your aggregate.

var installationRoot = repo.GetInstallation(id);
installationRoot.Remove();

class InstallationRoot {
    private List<Installation> children = new List<Installation>();
    private bool isRemoved = false;

    void Remove(){
        if(children.Any()){
            throw new InvalidOperationException();
        else{
            isRemoved = true;
        }
    }
}

If you decide not to redraw your aggregate boundaries (which might be quite difficult in first place) then you risk with situation like:

// Joe attempts to 'Remove' installation
// Installation doesn't have any children at this point
var installation = repo.GetInstallation(id);
var installationChildren = repo.GetChildren(id);

if(installationChildren.Any()){
    throw new InvalidOperationException();
}
else{           
    // Meanwhile new nodes(children) are added by Jane
    installation.Remove();
    //Now you have orphans
}

If consistency is needed, then only way to preserve it is to redraw the boundary.
How? It depends how it is currently implemented.

Answered by Siim Haas on March 4, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP