How to decouple your code from ORM (XPO) while granting it the power to IoC?

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:

  1. 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)
  2. write a generic IRepositoryFactory implementation for the XPO ORM that simply creates the wrapper for the XPCollection or XPServerCollection
  3. 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…

10 Responses to “How to decouple your code from ORM (XPO) while granting it the power to IoC?”


  1. 1 Jeremy Gray

    Thanks for posting up your take on this issue. I have to say, though, that the examples I’ve seen being posted have been rubbing me in a way that until now I couldn’t quite put my finger on. I think I could best sum up my feeling thusly:

    1. Nicholas’ perennial Shareholding example is always seen being some kind of weird “I’m a service component registered in the container except that I’m not because I’m an entity carrying business data” thing. He fires it up like a component, which allows it to get access to other components just fine, except that in any system I’ve worked on, that object would have been fired up by the ORM as an entities, not by the calling code as a component.

    2. The example you have presented in this post works just fine on its own, as I’m all for treating commands as transient components so that their needs can be serviced by the container, except that yet again the example isn’t an entity.

    Perhaps this will make things a bit clearer: between you and Nicholas (and perhaps even Udi), what I would love to see is a take on the perennial Shareholding entity making use of injected services, except that I would like it to be one where it actually is an entity, as it should be, and preferrably one instantiated by an ORM. ;)

  2. 2 Rinat Abdullin

    Jeremy,

    Basically, in this example that kind of entity is the “account” that has been serviced by the IoC-powered ORM repository.

    I think I’ve got your idea and will do my best to expand on the topic later this week.

    Rinat

  3. 3 Jeremy Gray

    Rinat,

    Until I see an Account class making use of an injected component, the account has not “been serviced by the IoC-powered ORM repository.” Hopefully an upcoming example from one of you guys will reveal how you would go about doing this, because the problem with all of the examples I’ve seen so far is that they drive the codebase towards the anemic domain model anti-pattern.

    To be very clear, though, I really appreciate you humouring my comments appearing in a few places and am glad that you are taking the time to think about the issue.

  4. 4 Rinat Abdullin

    Jeremy,

    I may not post right away, since the logic behind ORM+IoC repository needs some thought, but my next post in the weblog will be on the subject.

    I usually try to stick to my words and if you notice any big statement of mine that feels fallible or simply wrong, feel free to tell me. I will really appreciate that.

    Rinat

    PS: The joke about the hours in day comes from the Google group on autofac where Nick wished to increase the default values on the weekends))
    http://groups.google.com/group/autofac/msg/a7fa1e26dc4b1767

  5. 5 Alex Hoffman

    Thanks for an interesting post.

    Just a small point, but from a manager’s perspective I would question whether you are going down a slippery slope of continually trying to abstract away everything from everything. When does adding “just one more” layer of abstraction end? In a large organization with a diverse developer base, such an approach leads to less maintainable code given average developer skills, and that is less desirable than testability or re-usability. The journey to a “silver bullet” framework that is loosely coupled in all circumstances, too often ends up in a complex framework that only the author(s) can use.

  6. 6 Jeremy Gray

    Alex,

    So many of these techniques and technologies are mutually supporting and have so many great network effects that it can often be hard to see which angle someone might be coming at it from, and as such I can totally understand your comment.

    My primary “angle” is that I need testable code, in this case specifically testable entities. I would also like to avoid the anemic domain model anti-pattern. To avoid it, I need entities that can make use of services. To make those entities testable, I need their service dependencies injected. It can be hard to do that, however, when instantiation of the entities is out of my control, eg. when an ORM instantiates and populates the entity objects.

    For me, at least, it is this testability that is key. Abstraction is useful, but as you point out must always be treated solely as a means to a reasonable end. For me, abstraction is secondary to testability, though it often can be achieved in good support of testability.

  7. 7 Jeremy Gray

    PS to all - I know that I could easily create service-utilizing entities through the use of things like singleton service accessors, use of service locators, etc., but given that we’re all talking in the context of IoC, with a focus on DI, I’m sure you can see that I would love to be able to achieve these goals without breaking out the singletons, (also singleton) service locators, “provider” models rife with config, backwards hacks for testability, etc.

  8. 8 Rinat Abdullin

    Alex,

    I absolutely agree with you on the importance of the resources in any given project. Obviously, the time, money and available development pool are the constraints that affect any development process, architectural and logical decisions (they’d better or the project will be inefficient).

    And frequently due to the resource pool limitations one has to “stay low” in the code and trade away some ease of change and extension for maintainability in order to deliver project successfully.

    However, in xLim implementations I have the luxury of trying to get as much extensibility and abstraction as possible while keeping it logical, just to see how things work out together and what “silver bits” are the most rewarding.

  9. 9 Rinat Abdullin

    Jeremy,

    You have a great point on the network effect in systems.

    It is amazing to see how some small logical and architecture bits can stack up if you keep them in mind while working within the scope of subsystems (i.e. Automation Engine, Data Server, Web UI, Desktop UI).

    Basically the components and their relations start turning into higher level meta-programming language that you can use to assemble sub-solutions dealing with business objects, processes and workflows.

    But in order to get that synergy effect, a certain degree of logical flexibility and abstraction is needed in the right spots. Spots that do not gain enough from being flexible are better to be hardcoded.

  1. 1 How to inject ORM with some IoC? at Rinat Abdullin

Leave a Reply