I’m working on a project which involves versioning entities, so that the whole history of modifications by users is archived.
Basically, the idea is that when an entity is created, a version 1 of its contents is also saved in an archive table. Whenever the entity is modified, an incremental version is stored as well.
Saving the state of an entity to the archive table is handled by an ArchiveService.
When an entity is persisted, the ArchiveService needs to be called to create the version 1, so the most logical way seems to be to call it from the repository, and thus pass the service as a dependency to the repository:
public class Repository {
private ArchiveService archiveService;
public Repository(ArchiveService service) {
this.archiveService = service;
}
public void add(Entity entity) {
// ... (persist the entity)
this.archiveService.createVersion(entity);
}
}
Is this a good practice, or are there any drawbacks?
What I’ve seen so far, is services depending on repositories, not the other way round.
If modelled it as described, you’d make domain knowledge implicit and hide it in the repository, which itself isn’t part of the domain. I don’t think that this would be a good idea.
You could model it using Domain Events, i.e.
EntityCreated. An event listener could then pick up the event and create the respective archive entry.Update to answer a question that was brought up in the comments:
Apart from the formal definition: A repository knows how to store and retrieve the objects. To be able to accomplish this, it needs to know about the persistence mechanism or at least a data access layer underneath.
The domain model on the other hand should not depend on anything on the outside. In the Ports and Adapters architecture (which is a much better fit for DDD than the classical layered approach) the repository would be one of the adapters.
Your domain only knows the interfaces of its repositories, the actual implementation is on the outside. This way you only have inward-pointing dependencies. Basically this is object-orientation while the layered architecture with its downward pointing, cascading dependencies is more of a procedural approach.