David Nordvall
David Nordvall

Reputation: 13202

Persisting only parts of large entities though repository

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

Answers (2)

MikeSW
MikeSW

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

Eben Roux
Eben Roux

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

Related Questions