It is Sunday and there is too much work to do, but Nicholas Blumhardt has just got me totally distracted with his latest post on Enabling Rich Domain Models with Services. Comments by Jeremy Gray have just added to that.
So, it seems that in addition to ruling the IoC coupling out of the component code (while keeping its resolution powers in) we can simply rule out the entire ORM out of the code as well (in my case DevExpress eXpress Persistent Objects go for the ORM, but there is no big difference).
Take a look at the code below:
public sealed class ApproveTask : ICommand
{
private readonly Session _session;
public ApproveTask(Session session)
{
_session = session;
}
public void Execute()
{
var entries = new XPCollection<AccountEntry>(_session,
CriteriaOperator.Parse("Approved=0"));
foreach (var entry in entries)
{
entry.Approved = true;
}
_session.Save(entries);
}
}
The code has some strong coupling with the XPO ORM, does not it? Now, what if we were to rewrite it like this:
public sealed class ApproveTask : ICommand
{
private readonly IRepositoryFactory<IAccount> _repository;
public ApproveTask(IRepositoryFactory<IAccount> repository)
{
_repository = repository;
}
public void Execute()
{
var filtered = _repository.GetFiltered("Approved=0");
foreach(var account in filtered)
{
account.Approved = true;
}
_repository.Save(filtered);
}
}
Now that feels to be much better in terms of re-usability and testability.
To implement that we just need to:
- Decouple our business objects from the ORM via the interfaces (if they are strongly coupled like it is done in XPO, where they have to inherit from the base class)
- write a generic IRepositoryFactory implementation for the XPO ORM that simply creates the wrapper for the XPCollection or XPServerCollection
- register the factory with the container scope to make it operate in the context of the Session or UnitOfWork of the current scope
Bonuses:
- I use the string-based criteria in these code examples, but chances are that it would be possible to write server-side Linq statements instead.
- Since the IRepository is the “IoC front” code, we can hand it the powers to IoC as well. For example, the domain objects might want to have that ILog interface or some IHashProvider. We can pass these down to the repositoryFactory same way it has been done with the IResolver: use “Action<T> injectProperties” that gets called after the domain object is retrieved by ORM (these will still have the current Session/Transation since they are resolved in the current scope). Additionally, since the IRepository implements “T Create()” member, we can present components with new objects that are already DI’d with the container.
But it gets more complicated if some business object holds a collection of aggregated objects as well…



Latest Comments