I would advice against exposing repositories to aggregates. Repositories are there to give you aggregates, that's it.
Look at it at that way: your domain is a "bubble" which only understands its own stuff. Meaning, it only understand its own value objects, domain services interfaces it declares, etc. I wouldn't include repositories in this set.
When your domain (an aggregate) needs something it should explicitly expose the dependency of what it needs, not just ask for some repository.
Services is what brings things together. For example, my command handler could look like:
public class ApproveOrderCommandHandler : IHandle<ApproveOrderCommand>
{
//this might be set by a DI container, or passed to a constructor
public IOrderRepository Repository { get; set; }
public ISomeFancyDomainService ServiceRequiredForOrderApproval { get; set; }
public void Handle(ApproveOrderCommand command)
{
var order = Repository.Get(command.OrderId);
order.Approve(command.Comment, ServiceRequiredForOrderApproval);
Repository.Save(order);
}
}
Note that although my command handler (app service in a way) deals with repositories, my domain (order aggregate) is persistence ignorant. It doesn't know anything about repositories of UnitOfWorks.
When I do need to spin up a UnitOfWork I can compose it using a Chain Of Responsibility pattern:
public class WithUnitOfWorkHandler<T> : IHandler<T> {
private readonly IHandler<T> _innerHandler;
public WithUnitOfWorkHandler(IHandler<T> innerHandler) {
_innerHandler = innerHandler;
}
public void Handle(T command) {
using(var ouw = new UnitOfWork()) {
_innerHandler.Handle(command);
uow.Commit();
}
}
}
Now I can "chain" any of my command handlers by "decorating" it with WithUnitOfWorkHandler
.
And some of the handlers may even touch more than one repository or aggregate. Still, aggregates don't know anything about persistence, unit of works, transactions, etc.