Say I have an aggregate root Entity
with some flags which are represented by an encapsulated object EntityFlags
:
class Entity
{
/** @var EntityFlags */
private $flags;
...
}
I have a repository for this entity.
My goal is to modify flags in the DB. There are two ways I see:
- Get entity from the repository, modify flags like
$entity->getFlags()->set($name, true)
and save it:$repository->save($entity)
. - Create an additional method in the repository, e.g.
modifyFlags(EntityId $id, EntityFlags $flags)
I think the first way is redundant. But it also seems wrong to use repository for partial entity updates like in the 2nd way. Which way is the correct one? Maybe I missed something?
You’re placing too much responsibility into the Repository here, drifting away from the original pattern. A Repository is just supposed to be a conceptual set of domain objects, a collection you can query from or add to without minding about the underlying persistent storage.
modifyFlags(EntityId $id, EntityFlags $flags)
could typically be the signature of a use case, i.e. a method in one of your Application layer Services, but not a Repository method. A repo isn’t supposed to contain object modification logic.
save($entity)
may also not fit very well into the repo, because knowing how to flush things to the database is a separate responsibility in itself. The Unit of Work API in most object relational mapping tools typically allows you to do that. It doesn’t make much sense to have a Save(object)
method on an array or a collection type – likewise on a Repository.
6
Both are possible, but the question is why would you want to use one over another. First case allows you to do additional work when setting flags. Maybe you want to modify something else or notify other piece of code. This is especially true for aggregate roots, whose reason for existence is primarily to ensure business rules over multiple entities. But pulling whole entity from database, just to change single property does seem wasteful. Second case removes this waste, but also removes your ability to run any business rules during the change. Also, any performance optimizations should be preceded by profiling, so you don’t complicate your code for “percieved” performance gain.
If it was on me, I would use the first option, and if I figure out through profiling this is a performance problem, I would change it to second option.
4