I cannot give a good code answer because I don't understand the semantics of the domain.
- What is a child without parent?
- Why does addchild both adding a parentless child and moving an
existing childs
- Are child and parent both aggregateroots
- I guess this is not really about human parents and children, but
about some specific domain. Using
that in the example can make it
possible to provide better answers.
When I knew all those things, I would suggest you to remove all public setters from the domain, and add methods with descriptive name for all domain actions. Users don't manipulate data, but they do things. The method name should be something of your domain semantics and contain all business logic to do that thing.
When parent is an aggregate root for children, I would implement adding a new child to a parent like this:
public class Child
{
protected Child() { } // Constructor to please NHiberante's "Poco" implementation
// internal to prevent other assemblies than the domain assembly from constructing childs
internal Child(string somethingElse, Parent parent)
{
SomethingElse = somethingElse;
Parent = parent;
}
// Parent can not be changed by the child itself, because parent is the aggregate root of child.
public Parent Parent { get; private set; }
public string SomethingElse { get; private set; }
}
public class Parent
{
private readonly ISet<Child> children;
public Parent()
{
children = new HashedSet<Child>();
}
public IEnumerable<Child> Children
{
// only provide read access to the collection because manipulating the collection will disturb the domain semantics.
get { return children; }
}
// This method wouldn't be called add child, but ExecuteSomeBusinessLogic in real code
public void AddChild(string somethingElse)
{
// child constructor can only be called here because the parent is the aggregate root.
var child = new Child(somethingElse, parent);
children.Add(child);
}
}
If you give us some more information, I can give an answer for the other requirements you have.
This answer summarized in a oneliner: "For all state manipulations in your application, you need one method with a name telling what it does."
EDIT: Reaction on comments.
I have a problem with one of your specs: only parent is the aggregate root. When that is the case, you would never use the child without the parent. That makes a bidirectional relation useless, because you already know the parent when you access it's child. Bidirectional relations are hard to manage in DDD, so the best solution is to avoid them. I know there are several reasons why you can't avoid them when using NHibernate.
The following code has a bidirectional relation and solves the problem of the domain becoming in a temporary invalid state by using internal helpers methods. The internal methods can only be called from the domain assembly, and are not exposed to the outside. I don't really like this approach, but it is the least worse solution I think.
public class Client : AggregateRoot
{
private readonly ISet<Contact> contacts;
public Client()
{
contacts = new HashedSet<Contact>();
}
public IEnumerable<Contact> Contacts
{
get { return contacts; }
}
public void LogCall(string description)
{
var contact = new Contact(description, this);
AddContact(contact);
}
internal void AddContact(Contact contact)
{
contacts.Add(contact);
}
internal void RemoveContact(Contact contact)
{
contacts.Remove(contact);
}
}
public class Contact : AggregateRoot
{
protected Contact { }
public Contact(string description, Client client)
{
if (description == null) throw new ArgumentNullException("description");
if (client == null) throw new ArgumentNullException("client");
Client = client;
Description = description;
}
public Client Client { get; private set; }
public string Description { get;private set; }
// I assumed that moving a contact to another client would only be done by the user to correct mistakes?
// Isn't it an UI problem when the user frequently makes this mistake?
public void CorrectMistake(Client client)
{
Client.RemoveContact(this);
Client = client;
client.AddContact(this);
}
}