Question

1) In most cases each Aggregate Root should define its own transactional boundary, in which case we don't need to expose IUnitOfWork interface in Domain Layer.

a) I assume in this situation a good option would be for a repository ( used by aggregate to enforce invariants applied within it ) to contain its very own instance of UoW ( if using EF, then this UoW instance could simply be of type DbContext )?

2)

a) But if for whatever reason transaction spans several aggregates ( thus more than one aggregate needs to be changed at one time ), then won't Domain Layer also need to contain IUnitOfWork interface?

b) Won't exposing IUnitOfWork interface in Domain Layer violate persistence ignorance rule?

c) If yes to b), doesn't then exposing IUnitOfWork defeat the purpose of having repositories?

Replying to Alexey Raga:

1) I would advice against exposing repositories to aggregates. Repositories are there to give you aggregates, that's it.

a) Though I assume that majority of ddd architects don't have a problem with exposing repos to aggregates ( I'm only asking because I read several articles on repos and DDD and the impression I got is that authors ain't against exposing repos to aggregates - but now I'm not so sure anymore )?

b) So you're also against exposing repositories to domain services?

c) Judging by your answer I'm guessing that you consider exposing IUnitOfWork as a violation of PI?

2)Note that although my command handler (app service in a way)...

Do you normally implement command handlers as app services?

3)

public void Handle(ApproveOrderCommand command)
{
    var order = Repository.Get(command.OrderId);
    property.Approve(command.Comment, ServiceRequiredForOrderApproval);
    Repository.Save(order);
}

Is property.Approve(...) a typo and you actually meant order.Approve(...)?

Thanx in advance

Was it helpful?

Solution

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.

OTHER TIPS

Persistence ignorance means: The business layer has no knowledge and no dependency whatsoever on the concrete persistence system that is used under the hood (e.g. MS SQL Server, Oracle, XML files, whatever).

Thus, exposing an interface that abstracts away the concrete type of the datastore can never violate this principle.

Persistence Ignorance is a guideline, it is almost impossible to reach with actual languages and technologies. The Repository pattern and the Unit Of Work abstract the persistence related stuff and "hide" the Data Access Layer to the business code, but it is more a trick (a clean one) than an absolute solution. The presence or the need for something (an interface, a base class, an attribute...) that says "heyyy, there is something in here we want to hide..." violates PI. But for the moment, there is no better solution.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top