Reputation: 13202
I have an entity which is an aggregate root and contains a lot of sub-entities. Basically, loading it from the database and persisting it all is a very expensive operation. Most of the time, I only change small parts of the entity so there really is no need to load and persist the entire entity anyway. I am, however, not sure how to implement this using DDD principles and the repository pattern.
I have been considering something like:
interface IAggregateRoot {
string Id { get; }
ISubEntityA EntityA { get; }
IList<ISubEntityB> EntityB { get; }
}
interface ISubEntityA {
string Id { get; }
int Foo { get; set; }
}
interface ISubEntityB {
string Id { get; }
string Bar { get; set; }
}
interface IRepository {
IAggregateRoot Get(string rootId);
ISubEntityA Get(string rootId);
IList<ISubEntityB> Get(string rootId, int offset, int pageSize, out int numPages);
ISubEntityB Find(string rootId, some specification to select a single ISubEntityB);
// I can update either the entire aggregate root or an individual sub entity using
// these methods.
void AddOrUpdate(IAggregateRoot root);
void Update(string rootId, ISubEntityA a);
void Update(string rootId, ISubEntityB b);
}
Are there any problems with this approach? Is there a best practice regarding this "problem"?
Upvotes: 1
Views: 238
Reputation: 16348
The repository deals only with aggregates roots and not with parts of it. SO I suggest this
interface IRepository {
IAggregateRoot Get(string rootId);
//ISubEntityA Get(string rootId); <- this you get from the AR
//IList<ISubEntityB> Get(string rootId, int offset, int pageSize, out int numPages);
//for the method above I suggest a different repository and model dedicated for view
// ISubEntityB Find(string rootId, some specification to select a single ISubEntityB);
// I can update either the entire aggregate root or an individual sub entity using
// these methods.
void Save(IAggregateRoot root); // repository should know if to insert or update, by comparing to the existing instance, use of an OR\M recommended
// void Update(string rootId, ISubEntityA a); <- this should be handled by the method above
// void Update(string rootId, ISubEntityB b); <- this too
}
Upvotes: 0
Reputation: 13246
I use a similar technique. For instance, say I activate an account:
var account = repository.Get(accountId);
account.Activate();
repository.AccountActivated(); // or repository.SaveActivationDetails()
Since you don't want your repository involved with domain behaviour your wouldn't do the following:
repository.ActivateAccount(account);
But you could do this:
account.Activate(repository); // in the Activate method the relevant repository
// persistence method will be invoked.
So you can choose a mechanism you are comfortable with.
But getting back to your repository: I wouldn't do querying. A lightweight query layer can return something as simple as a DataTable
for your front-end to consume; else DTOs. This relates to the paging you have going there.
Upvotes: 1