Why dependency injection rocks

May 19, 2008 at 12:59 PMAndre Loker

Since I started using IoC containers like Castle Windsor or Spring 18 months or so ago the way I write code has changed quite a lot. Let me give an example. Let's assume I am currently writing the code to create a user account that can be used to login into an application. Let us further assume that I am using the following domain driven architecture:

architecture

That is, we have a domain model that is surrounded by a service layer. The latter is accessed by the application. Data moved back and forth between application and service layer comes in the form of data transfer objects. The service layer and domain model have access to the repositories that can be used to access all entities in the domain.

Now that you have an overview of the architecture, let us assume that I am busy writing a class in the service layer that handles account related stuff. Let us call it AccountService. In this case I want to write a method that creates a new account in the system. This method has some additional constraints:

  • Only currently logged in administrators may create an account
  • The username and email must not already be in use
  • The code should run within a single database transaction. This constraint actually is a consequence of the previous one: checking for duplicate user names/email addresses and saving a new account should be atomic.
  • We would like to apply some general exception handling policies

Let's have a look at the method filled with pseudo code comments:

   1: namespace BeautyOfInjection.Service {
   2:     public class AccountService {
   3:         /// <summary>
   4:         /// Creates a new account.
   5:         /// </summary>
   6:         /// <param name="userName">User name for the new account.</param>
   7:         /// <param name="email">E-mail address for the new account.</param>
   8:         /// <remarks>
   9:         ///  Only administrators can create new accounts. 
  10:         /// </remarks>
  11:         public void CreateAccount(string userName, string email) {
  12:             // start transaction
  13:             // check whether current user has permission to create an account
  14:             // check whether user name and email address are unique
  15:             // create a new account instance
  16:             // save instance to db
  17:             // commit transaction
  18:             // on failure: rollback transaction and handle exception
  19:         }
  20:     }
  21: }

This should be straight forward so far. Let us begin to write code. First, we start off with the transaction handling, but let us try to think in terms of dependency injection. Personally I think that the burden of transaction handling should not lie on a single method in the service layer. Maybe we want CreateAccount to run within a larger transaction. What we need is the possibility to declare the requirement of a transaction. All transaction pluming should then be handled externally. This is a good example of separation of concerns. To achieve this, let us define an interface that allows us handle transaction requirements.

   1: using System;
   2:  
   3: namespace BeautyOfInjection.Persistence {
   4:  
   5:     /// <summary>
   6:     /// Allows the client to declare its requirement for a transaction.
   7:     /// </summary>
   8:     public interface ITransactionControl{
   9:         /// <summary>
  10:         /// Opens a new transaction context.
  11:         /// </summary>
  12:         /// <returns>A new transaction context</returns>
  13:         ITransactionContext EnsureTransaction();
  14:     }
  15:  
  16:     /// <summary>
  17:     /// A (possibly nested) transaction scope.
  18:     /// </summary>
  19:     public interface ITransactionContext : IDisposable {
  20:         /// <summary>
  21:         /// Considers this transaction context a success.
  22:         /// </summary>
  23:         /// <remarks>
  24:         /// If <see cref="VoteRollback"/> is not called by this transaction context or 
  25:         /// any parent or child context, the database transaction will be committed.
  26:         /// </remarks>
  27:         void VoteCommit();
  28:  
  29:         /// <summary>
  30:         /// Votes for a rollback on this transaction.
  31:         /// </summary>
  32:         /// <remarks>
  33:         /// Calling this method will finaly cause a rollback of the database transaction.
  34:         /// </remarks>
  35:         void VoteRollback();
  36:     }
  37: }

The actual working of those interfaces is not that important here. Let us just assume that we can declare the requirement for a transaction using EnsureTransaction and the returned ITransactionContext can be used to decide whether the transaction should commit or rollback. This design by the way resembles the Active Record transaction management. So, how do we gain access to a ITransactionControl instance? Remember that we are looking for inversion of control with dependency injection. We should therefore declare the need for an ITransactionControl object somewhere the IoC container recognizes. Two common approaches are Constructor Injection and Setter Injection. I chose for setter injection. With the new C# 3.0 auto property syntax it's a snap to declare the property.

   1: public class AccountService {
   2:     // This dependency will be injected by the container
   3:     public ITransactionControl Transactions { get; set; }
   4:  
   5:     /// <summary>
   6:     /// Creates a new account.
   7:     /// </summary>
   8:     /// <param name="userName">User name for the new account.</param>
   9:     /// <param name="email">E-mail address for the new account.</param>
  10:     /// <remarks>
  11:     ///  Only administrators can create new accounts. 
  12:     /// </remarks>
  13:     public void CreateAccount(string userName, string email) {
  14:         // start transaction
  15:         using (var txn = Transactions.EnsureTransaction()) {
  16:             // check whether current user has permission to create an account
  17:             // check whether user name and email address are unique
  18:             // create a new account instance
  19:             // save instance to db
  20:             // commit transaction
  21:             txn.VoteCommit();
  22:             // on failure: rollback transaction 
  23:             // (will be done automatically if VoteCommit is not called before dispose)
  24:         }
  25:         // ... and handle exception
  26:     }
  27: }

This was easy. We simply do not care about were we get our ITransactionControl instance from. We simply assume that it will be resolved by the IoC container or explicitly set during a unit test.

Next step, check that the current user has the permission to create the account. I like to have this kind of code in a separate class. It simplifies unit testing a lot. So let us define a new dependency interface:

   1: using System.Security;
   2:  
   3: namespace BeautyOfInjection.Security {
   4:     /// <summary>
   5:     /// 
   6:     /// </summary>
   7:     public interface IPermissions {
   8:         /// <summary>
   9:         /// Checks that the current user may create a new account.
  10:         /// </summary>
  11:         /// <exception cref="SecurityException">Permission was not granted.</exception>
  12:         void TryCreateAccount();
  13:     }
  14: }

Before I return to CreateMethod, I'll define another dependency interface, regarding exception handling. Resemblance with the Exception Handling Block is no coincidence:

   1: using System;
   2:  
   3: namespace BeautyOfInjection.Service {
   4:     /// <summary>
   5:     /// Handles exceptions
   6:     /// </summary>
   7:     public interface IExceptionHandler {
   8:         /// <summary>
   9:         /// Handles the exception and returns whether the original exception should be rethrown.
  10:         /// </summary>
  11:         /// <param name="exception">The exception.</param>
  12:         /// <returns></returns>
  13:         bool HandleAndCheckForRethrow(Exception exception);
  14:     }
  15: }

Let us see how far we are with CreateUser:

   1: using System;
   2: using BeautyOfInjection.Persistence;
   3: using BeautyOfInjection.Security;
   4:  
   5: namespace BeautyOfInjection.Service {
   6:     public class AccountService {
   7:  
   8:         #region dependencies
   9:         public ITransactionControl Transactions { get; set; }
  10:         public IPermissions Permissions { get; set; }
  11:         public IExceptionHandler ExceptionHandler { get; set; }
  12:         #endregion
  13:  
  14:         /// <summary>
  15:         /// Creates a new account.
  16:         /// </summary>
  17:         /// <param name="userName">User name for the new account.</param>
  18:         /// <param name="email">E-mail address for the new account.</param>
  19:         /// <remarks>
  20:         ///  Only administrators can create new accounts. 
  21:         /// </remarks>
  22:         public void CreateAccount(string userName, string email) {
  23:             try {
  24:                 // start transaction
  25:                 using (var txn = Transactions.EnsureTransaction()) {
  26:                     // check whether current user has permission to create an account
  27:                     Permissions.TryCreateAccount();
  28:  
  29:                     // check whether user name and email address are unique
  30:                     // create a new account instance
  31:                     // save instance to db
  32:  
  33:                     // commit transaction
  34:                     txn.VoteCommit();
  35:                 }
  36:             } catch (Exception e) {
  37:                 // on error, handle exception
  38:                 if(ExceptionHandler.HandleAndCheckForRethrow(e)) {
  39:                     throw;
  40:                 }
  41:             }
  42:         }
  43:     }
  44: }

Not bad, if you ask me. Throw in some more external depencies: we want to access entities using the repository pattern, so let's define a account repository interface. Furthermore, we want accounts to be created using a factory. I think that the factory would also be a useful place to check for uniqueness of user name and email. I am always a bit struggling on this topic: on the one hand I want the domain model to use the repositories as little as possible. On the other hand I do not like it when business rules (i.e. uniqueness of email and user name) are not checked in the domain model. As I consider the factory to be part of the model, checking those rules in the factory seems reasonable to me. Be aware though that if you support changing a user name/email address you will have to check for uniqueness again and this time not in the factory. So you might as well put the uniqueness check into the model class (and skip the factory altogether). Anyway, this is just an example, so let's stick to the factory/repository pair.

Here are the required interfaces:

   1: namespace BeautyOfInjection.Model {
   2:     /// <summary>
   3:     /// An account
   4:     /// </summary>
   5:     public interface IAccount {
   6:         string UserName { get; }
   7:         string EmailAddress { get; }
   8:     }
   9:  
  10:     /// <summary>
  11:     /// 
  12:     /// </summary>
  13:     public interface IAccountFactory {
  14:         /// <summary>
  15:         /// Creates a new account instance.
  16:         /// </summary>
  17:         /// <param name="userName">Name of the user.</param>
  18:         /// <param name="email">The email.</param>
  19:         /// <returns>
  20:         /// The newly created account.
  21:         /// </returns>
  22:         /// <remarks>
  23:         /// The method validates the user name and email and ensures that both are unique.
  24:         /// </remarks>
  25:         /// <exception cref="ValidationException">
  26:         /// The user name and/or email were not valid or not unique.
  27:         /// </exception>
  28:         IAccount Create(string userName, string email);
  29:     }
  30:  
  31:     /// <summary>
  32:     /// 
  33:     /// </summary>
  34:     public interface IAccountRepository {
  35:         void Save(IAccount account);
  36:         IAccount GetAccountByUserName(string userName);
  37:         IAccount GetAccountByEmail(string email);
  38:     }
  39: }

ValidationException would be a custom exception type that is used for business rule violations.

Now we are ready to present the final version of CreateAccount:

   1: using System;
   2: using BeautyOfInjection.Model;
   3: using BeautyOfInjection.Persistence;
   4: using BeautyOfInjection.Security;
   5:  
   6: namespace BeautyOfInjection.Service {
   7:     public class AccountService {
   8:  
   9:         #region dependencies
  10:         public ITransactionControl Transactions { get; set; }
  11:         public IPermissions Permissions { get; set; }
  12:         public IExceptionHandler ExceptionHandler { get; set; }
  13:         public IAccountFactory Factory   { get; set; }
  14:         public IAccountRepository Repository{ get; set; }
  15:         #endregion
  16:  
  17:         /// <summary>
  18:         /// Creates a new account.
  19:         /// </summary>
  20:         /// <param name="userName">User name for the new account.</param>
  21:         /// <param name="email">E-mail address for the new account.</param>
  22:         /// <remarks>
  23:         ///  Only administrators can create new accounts. 
  24:         /// </remarks>
  25:         public void CreateAccount(string userName, string email) {
  26:             try {
  27:                 // start transaction
  28:                 using (var txn = Transactions.EnsureTransaction()) {
  29:                     // check whether current user has permission to create an account
  30:                     Permissions.TryCreateAccount();
  31:  
  32:                     var account = Factory.Create(userName, email);
  33:                     // the previous line would have thrown an exception
  34:                     // if userName and/or email had been invalid/not unique
  35:  
  36:                     // add instance to repository
  37:                     Repository.Save(account);
  38:  
  39:                     // vote for transaction commit
  40:                     txn.VoteCommit();
  41:                 }
  42:             } catch (Exception e) {
  43:                 // on error, handle exception
  44:                 if(ExceptionHandler.HandleAndCheckForRethrow(e)) {
  45:                     throw;
  46:                 }
  47:             }
  48:         }
  49:     }
  50: }

You see that the service layer class becomes a mediator between all the external dependencies. What are the advantages of this design?

  • Separation of concerns to the max, every aspect is neatly extracted to a separate interface
  • High (unit-) testability. As we can mock/stub any of the external dependencies we have full control over their behavior. In this case we can easily test whether the method correctly requests a transaction, votes commit on success, handles exceptions, checks the required permissions etc. by simply passing mock objects with the right expectations.
  • The code in the service layer gets simplified. We can concentrate on the core functionality of CreateAccount instead of cluttering the code with loads of unrelated code.
  • We do not need to worry about how dependencies get injected. Just add the dependencies as e.g. properties (or constructor arguments) and assume that they will be provided externally. Of course you ultimately have to setup your IoC container or provide mocks/stubs. But while writing AccountService you just don't care.

Note: you will want to extract an interface from AccountService (say: IAccountService). This can then be used to define dependencies on this service in other layers, for example in your controllers (if you use an MVC framework like MonoRail).

Posted in: Snippets | Patterns

Tags: , , , ,

Access to MonoRail service instances

May 6, 2008 at 10:18 PMAndre Loker

In my current MonoRail based project - which is in fact my first MonoRail project - I needed access to some of the services provided by the MonoRail container, for example, IEmailTemplateService and IEmailSender. Furthermore the MonoRail app had Windsor Integration enabled, so I wanted them to be injected into the client component by the Windsor IoC container. Unfortunately, the services are not registered as components at the Windsor container.

Luckily, I was pointed at the solution (e.g. here and here) by the kind users of the Castle Project Users mailing list. The key is to let your HttpApplication class implement IMonoRailContainerEvents, which will provide you with a IMonoRailContainer instance as soon as it is created and again when it is initialized.

   1: // Implements IContainerAccessor to notify MonoRail that we provide our own IWindsorContainer
   2: // Implements IMonoRailContainerEvents to be notified when the container was created
   3: public class Global : HttpApplication, IContainerAccessor,  IMonoRailContainerEvents {
   4:     private static Container container;
   5:  
   6:     #region IContainerAccessor Members
   7:     public IWindsorContainer WebContainer {
   8:         get { return container; }
   9:     }
  10:     #endregion
  11:  
  12:     #region IMonoRailContainerEvents Members
  13:     public void Initialized(IMonoRailContainer mr){
  14:         // here we register the services as components
  15:         container.Register(
  16:             Component.For<IEmailTemplateService>().Instance(mr.EmailTemplateService),
  17:             Component.For<IEmailSender>().Instance(mr.EmailSender)
  18:             // additional services here...
  19:         );
  20:     }
  21:  
  22:     public void Created(IMonoRailContainer mr){
  23:         // ignore..
  24:     }
  25:     #endregion
  26:  
  27:     protected void Application_Start(object sender, EventArgs e) {
  28:         // create container
  29:         container = new WebContainer();
  30:         container.Init();
  31:     }
  32: }

The important part is the fact that we implement IMonoRailContainerEvents. In the Initialized method, we can than access the service instances of the MonoRail container. The WebContainer class is a class derived from WindsorContainer that handles the Windsor integration for MonoRail.

   1: public class WebContainer : WindsorContainer {
   2:     public WebContainer()
   3:         : base(new XmlInterpreter(new ConfigResource())) {
   4:     }
   5:  
   6:     public void Init() {
   7:         AddFacility("rails", new MonoRailFacility());
   8:     }
   9: }

Simple like that. With all these pieces of code set up, I can now have the services be injected into my components, for example:

   1: public class DefaultMailService : IMailService {
   2:     private const string defaultFrom = "my application <mail@localhost>";
   3:     private const string defaultMailLayout = "Mail.vm";
   4:  
   5:     #region dependencies - Injected by Windsor
   6:     public IEmailTemplateService Templates { get; set; }
   7:     public IEmailSender Sender { get; set; }
   8:     #endregion
   9:  
  10:     #region IMailService Members
  11:     public void SendActivationMail(string name, string email, string ticket) {
  12:         log.InfoFormat("Sending activation mail to {0}", email);
  13:         var msg = Templates.RenderMailMessage("Activate", defaultMailLayout, new {name, ticket});
  14:         msg.To = email;
  15:         msg.From = defaultFrom;
  16:         Sender.Send(msg);
  17:     }
  18:     #endregion
  19: }

Thanks to the guys from the Castle mailing list.

Posted in: Castle | Snippets

Tags: , , , ,