Reputation: 490
I'm designing a budgeting app using DDD and EF Core.
I have a Budget
aggregate root. It has many Transactions
and BudgetTransactionCategories
. Basically, a tree of interconnected child entities.
Now my Budget
has a Name
. Suppose I want to rename it. There is no complex invariant here. The business logic is not dependent on the Budget name and it's purely the UX feature.
To avoid partially initialized entities anti-pattern, I understand that the proper way to update the entity would be to load the whole entity tree from the database, perform some validation, update the property and save changes.
My concern is that I need to make performance concessions: either eager load, or introduce lazy loading which comes with its own set of issues. Seems an overkill for making a simple update like a rename with minimum validation (e.g. length).
If I understand the best practice correctly, I still should do this and design my aggregates in such a way that they don't get out-of-control and are not too big so I specifically don't have to worry about this.
What is the "correct" or "best" way to perform such updates? Perhaps a separate context / application for CRUD management (seems like a really bad idea though).
Upvotes: 2
Views: 511
Reputation: 1848
Your instincts were correct at the end of the question. There is nothing wrong with making a new aggregate with a new root to perform an action that does not fit well with your existing aggregate.
Let's go over what you have:
namespace Budgeting;
public class Budget : IAggregateRoot
{
public int Id {get;private set;}
public ICollection<Transaction> Transactions {get;private set;} // could be massive
}
public class Transaction : IEntity
{
// ...
}
I put the namespace there very specifically. I'm assuming that you have a bounded context that is all about budgeting.
But now it sounds like you might actually be dealing with a new context that requires a new aggregate. The reason I say this is because you now have a use case where you require 1% of your aggregate. The rest is waste. This implies your aggregate is not well crafted for this new use case.
So let's just define a new context with a new aggregate and a new root:
namespace BudgetConfiguration;
public class Budget : IAggregateRoot
{
public int Id {get;private set;}
public string Name {get;private set;}
public OperationResult SetName(string newName)
{
// validation and set name
}
}
This is trivial to map in EF.
So we have now defined a context that is dedicated to BudgetConfiguration. Or whatever you want to call it. Here you could spend all your effort on setting various settings and updating budgeting properties. Maybe user limits, preferences for this budget, etc.
The first thing you might think is that you are duplicating code. This is not true at all. The class happens to have the same name. But it is used for something entirely different. I touch on that topic here
You can think of the namespace as the name of your context and aggregate. Whatever is in that namespace is isolated from other contexts.
So in total you have 3 classes:
Now you have the following benefits:
Each root in each context loads EXACTLY what it needs. No less, no more.
Hope this helps.
Upvotes: 1