An architectural question. I’ve got a nicely de-coupled MVC3 solution with a few projects that’s working rather well.
Proj.Core - interfaces for data classes and services Proj.Services - interfaces for model services Proj.Data.NH - implementations of the data interfaces Proj.Services.NH - implementations of the data / model services Proj.Infrastructure - setting up Ninject and NHibernate Proj.Tests - unit tests Proj.Web - the MVC project
I’ve set up NHibernate to be session per request in the infrastructure project, so Proj.Web doesn’t need to reference NHibernate (or Ninject, for that matter). I’m now introducing SignalR, which works really nicely for a quick chat app. I’ve put a SignalR hub in the web project. I now want to persist the chat messages in the database, which has confused me somewhat.
I want to use a service (let’s call it PostService), so the SignalR hub doesn’t have a dependency on NHibernate. My other services are injected into the controllers’ constructors, and the session is injected into the services’ constructors.
As the SignalR hub hangs around, unlike controllers, PostService (injected into the constructor as an IPostService) can’t have a session injected into its constructor, as there won’t be a session. If there was, it would hang around forever, which would be far too long a time for a transaction.
I could inject the session factory into the PostService, and each method could use a transaction, e.g.
private void WithTransaction(Action<ISession> action)
{
using (var session = _sessionFactory.OpenSession())
using (var tx = session.BeginTransaction())
{
action(session);
tx.Commit();
}
}
public IPost Post(string message)
{
WithTransaction(session =>
{
session.Save(new Post(message));
});
}
The hub will then call _postService.Post(message);
However, once the Post method does more things, I’ll want to use some of my existing services to do the things, as they’ve already been written and unit tested. As the session is created in the method, I can’t have the services injected into the PostService constructor, as they accept a session into their constructors.
So, I guess I have the following options, but I’m not sure if a) this is a complete list, or b) which is the best option:
I. Inject an IDependencyResolver into the PostService constructor, and create the services I need in the Post method, passing in the session to the constructor. There’s an IDependencyResolver in System.Web.Mvc and in SignalR, so this would (depending on which project PostService resides) introduce a dependency on either library.
II. Modify the services so each method that uses a session has one passed in as a parameter. Overload this without the session parameter, and call the new one. The MVC service calls would use the first, and the PostService would use the second e.g.
public void SaveUser(IUser user)
{
Save(_session, user);
}
public void SaveUser(ISession session, IUser user)
{
session.Save(user);
}
III. Don’t use the existing services. Have the PostService do it’s own thing, even if there is a bit of duplication (e.g. getting user details etc.)
IV. Remove the ISession from the services’ constructors, and pass it in to each method (and deal with the Controllers’ accordingly.
V. Something else.
I guess I’m leaning towards the first one, but I’m not sure where PostService would live. If it goes in Proj.Services.NH, then I’d have to introduce a dependency on System.Web.Mvc or SignalR, which I don’t want to do. If it lives in Proj.Web, then I’d have to introduce a dependency on NHibernate, which I also don’t want to do. It doesn’t belong in Proj.Infrastructure, as it is application code. Should it have it’s own project with dependencies on everything, or is there a better way?
I would use some sort of auto factory for the additional services you need. So you would write your
PostServiceconstructor as:and then use this extension to have these factory automatically working ( by querying the container ).