Reputation: 913
I've just finished reading "Domain-driven design: tackling complexity in the heart of software" by Eric Evans and I'm attempting to write my first domain-centric application (in C#).
The application will be used by our helpdesk to track the allocation of computers to users.
I've sketched a simple class diagram to reflect part of the domain. It looks like this...
I've also identified my first feature (to allocate a computer to a user) and have written a test for it...
[Test]
public void AllocateToUser()
{
var user = new Owner("user1");
var computer = new Computer("computer1");
computer.Allocate(user);
Assert.AreEqual(user.Username, computer.AllocatedTo.Username);
}
Lastly, I've written code to make the test pass.
public class Computer
{
public Computer(string name)
{
Name = name;
}
public string Name
{ get; private set; }
public Owner AllocatedTo
{ get; private set; }
public void Allocate(Owner owner)
{
AllocatedTo = owner;
}
}
public class Owner
{
public Owner(string username)
{
Username = username;
}
public string Username
{ get; private set; }
}
So far, so good (I think).
However, clearly none of this addresses persistence. I think I need to introduce a repository class for Computer. Something like this perhaps:
public class ComputerRepository
{
public void Store(Computer computer)
{
//Some persistence logic here (possibly using NHibernate?)
}
}
Now I'm stuck. How do ensure that changes made to a computer's allocated user are passed to the repository?
I seem to have the following options:
Modify the implementation of the Allocate method of the Computer class to instanstiate an instance of ComputerRepositry and invoke the Store method.
Create an interface IComputerRepository; modify the constructor of Computer to demand that an instance of a class implemeting IComputerRepository is supplied. Within the Allocate method, call Store against this injected instance.
Create a service (AllocationService) that will wrap up a call to Allocate and Store.
Pass resposibility to the client, forcing two steps for the calling code:
None of these seem satisfactory:
is hard to test, as I'd be instantiating the repository directly within the Computer class.
avoids this problem, by using dependency-injection. However it is still ugly, as I need to pass in some instance of IComputerRepository every time I want to instantiate Computer.
is too procedural, failing to encapsulate the behaviour within domain entity classes.
just seems ugly.
What should I be doing?
Upvotes: 3
Views: 692
Reputation: 33270
Usually I would treat behaviour and persistence as two different concerns and test them separately.
The Domain objects should remain oblivious to the existence of Repositories (though clearly not the other way round).
What we've done in this situation is to create a Controller (or a Service) that is responsible for loading the appropriate objects from their Repositories, invoking the behaviour on the objects then calling the Repositories to persist the updates.
You can then test the controller using a Mock repository to check that the controller is calling the repository with the updated objects.
Upvotes: 5