.NET

Rich domain objects with DivineInject

DivineInject is a .net dependency injection framework, designed to be simple to use and easy to understand.

You can find the source for DivineInject on github. In this second part in the series we’ll look at creating rich domain objects, the first part covers getting started with Divine Inject.

Simple Domain Objects

As an example, imagine I have a simple shopping basket in my application. The shopping basket is encapsulated by a Basket object, for which the interface looks like this:

interface IBasket
{
    IList<IBasketItem> GetBasketContents();
    void AddItemToBasket(IBasketItem item);
}

The contents of the basket are actually backed by a service to provide persistence:

interface IBasketService
{
    IList<IBasketItem> GetBasketContents(Guid basketId);
    void AddItemToBasket(Guid basketId, IBasketItem item);
}

I can create a very simple implementation of my Basket:

class Basket : IBasket
{
    private readonly IBasketService _basketService;
    private readonly Guid _basketId = Guid.NewGuid();
    public Basket(IBasketService basketService)
    {
        _basketService = basketService;
    }
    public IList<IBasketItem> GetBasketContents()
    {
        return _basketService.GetBasketContents(_basketId);
    }
    public void AddItemToBasket(IBasketItem item)
    {
        _basketService.AddItemToBasket(_basketId, item);
    }
}

Since I’m using dependency injection, IBasketService sounds like a dependency, so how do I go about creating an instance of Basket? I don’t want to create it myself, I need the DI framework to create it for me, passing in dependencies.

I want to do something with how the Basket is created, so let’s start with a simple interface for creating baskets:

interface IBasketFactory
{
    IBasket Create();
}

When I’m creating a basket I don’t care about IBasketService or other dependencies; calling code just wants to be able to create a new, empty basket on demand. How would we implement this interface? Well, I could do the following – although I shouldn’t.

class BadBasketFactory : IBasketFactory
{
    public IBasket Create()
    {
        // DON'T DO THIS - just an example
        return new Basket(
            DivineInjector.Current.Get<IBasketService>());
    }
}

Now I’d never suggest actually doing this, explicitly calling the dependency injector. The last thing I want from my DI framework is to have references to it smeared all over the application. However, what this class does is basically what I want to happen.

DivineInject however can generate a class like this for you; this is configured at the same time you define the rest of your bindings:

DivineInjector.Current
    .Bind<IBasketFactory>().AsGeneratedFactoryFor<Basket>();

This generates an IBasketFactory implementation, which can create new IBasket implementations on demand (they will all actually be instances of Basket); all without having references to the DI framework smeared across my code. If I want to use the IBasketFactory, for example from my Session class, I declare it as a constructor arg the same as I would any other dependency:

public Session(IAuthenticationService authService, 
               IBasketFactory basketFactory)
{
    _authService = authService;
    _basketFactory = basketFactory;
}

The DI framework takes care of sorting out dependencies and I get a Session class with no references to DivineInject. When I need a new basket I just call _basketFactory.Create(). Since I have nice interfaces everywhere, everything is easy to mock so I can TDD everything.

Rich Domain Objects

Now what happens as my domain object becomes more complex? Say, for example, I want to be able to pass in some extra arguments to my constructor. Returning to our basket example: as well as starting a new, empty basket – isn’t there a possibility that I want to continue using an existing basket? E.g. in case of load balanced servers or fail-over. What do I do then?

I start by changing Basket, to allow me to pass in an existing basket id:

private readonly IBasketService _basketService;
private readonly Guid _basketId;
public Basket(IBasketService basketService)
{
    _basketService = basketService;
    _basketId = Guid.NewGuid();
}
public Basket(IBasketService basketService, Guid basketId)
{
    _basketService = basketService;
    _basketId = basketId;
}

I now have two constructors, one of which accepts a basket id. Since all Basket instances are created by an IBasketFactory, I need to change the factory interface, too:

interface IBasketFactory
{
    IBasket Create();
    IBasket UseExisting(Guid id);
}

I now have a new method on my IBasketFactory, if I was hand-coding the factory class I’d expect this second method to call the second constructor, passing in the basket id.

What do we need to tell DivineInject to make it generate this more complex IBasketFactory implementation? Nothing! That’s right, absolutely nothing – DivineInject will already generate a suitable IBasketFactory. Our original declaration above, is still sufficient:

DivineInjector.Current
    .Bind<IBasketFactory>().AsGeneratedFactoryFor<Basket>();

This generates an IBasketFactory implementation, returning a Basket instance for each method it finds on the interface. Since one of these methods takes a Guid, it tries to find a matching constructor which also takes a Guid, plus any dependencies it knows about. DivineInject can automatically wire up the right factory method to the right constructor, using the arguments it finds in each. Now, when a session wants to re-use an existing basket it just calls:

_basketFactory.UseExisting(existingBasketId)

This creates a new Basket instance, with dependencies wired up, passing in the basket id. Everything is still using interfaces so all your collaborations can be unit tested. Behind the scenes DivineInject generates the IL code to implement your factory interfaces, leaving you free to worry about your design.

By following this pattern we can create rich domain objects that include both state and dependencies: it becomes possible to create stateful objects that have behaviours (methods) that make sense in the domain. Successfully modelling your domain is critical to creating code that’s easy to understand and easy to change. DivineInject helps you model your domain better.

Reference: Rich domain objects with DivineInject from our NCG partner David Green at the Actively Lazy blog.

Related Articles

Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button