3

I'm adding functionality to our website which performs long-running processes asynchronously using MSMQ. Doing this ansynch, however means we need to notify users when their requests are completed. Using the command pattern, I created an interface* called INotify and composed that into the message class, so the message processing class can simply call GiveNotice() on the message's INotify object. The first implementation, EmailNotify, was more difficult than expected, as I was surprised to discover MailMessage isn't serializable, but got it going.

Now I'm working on a new concrete notifier, DBNotify, which will call a SP of some sort and update a status in the main transactional database. I'm tripped up in that I would like to reuse the DAL architecture we've already created, but INotify is a member of the Model project, which is more fundamental than the DAL.

Our hierarchy looks like this: Common > Model > DAL > BAL

Here's more detail about our particular n-tiers. Bear in mind, I inherited this, so I'm not looking for suggestions on improving it; I'm wondering how to "break the rules."

Common is responsible for all "utility" functions which are used many places in the application, things like accessing configuration settings, parsing strings, non-business related functionality.

Model is the repository of business objects, what some folks call data transfer objects, collections of getters and setters. I've added some "smarts" at this layer, but only business rules internal to that object, such as "An item's name must begin with an alphanumeric character."

DAL is the data access layer, in theory, all that happens here is model objects are moved into and out of the database.

BAL is the Business layer; it encapsulates business rules that govern the interaction of objects (i.e. "A form must have at least two items.").

So the INotify interface is defined an abstraction to allow the method of notification to vary independently (i.e. email, TXT, twitter, etc) from the message processing. I have created it at the Model tier, which is independent of the DAL tier, so I can't reuse that code without creating a circular dependency.

Has anyone else dealt with a business object whose purpose is to interact with a database, and how do you situate that in your N-tier architecture?

Before you tell me to use Linq to Sql, great thanks. This is not a technical question (how do I do this), it's a design question (how should I do this).

I think the answer is dependency injection, but I haven't ever used it.

2 Answers 2

6

You are correct that dependency injection solves this problem cleanly. However you don't need to necessarily know what that is or have a framework to solve this problem.

What you need is a way to register an implementation of INotify as an available implementation to use. (Register where, you say? I say, "somewhere, anywhere, whatever makes sense.") And a way to say, "Give me implementation X, please." So now you can have a class in the DAL that registers itself as the DBNotify implementation of INotify. And another class in the Model layer can ask that lookup service for the DBNotify implementation, and be handed back that implementation. Now the Model layer does not need to know about the fact that DBNotify happened to be implemented at the DAL layer. Of course any class that wants to actually use those Model classes would need to load DBNotify, because your Model layer does not know where that implementation comes from. This is not a particularly bad limitation, but it is one that classes using your models need to know about. You've avoided having an explicit dependency within your Model layer, but at the cost of leaking the information about the dependency to all DAL classes that might want to use those Model classes.

This is where a dependency injection framework comes in. The dependency injection framework is responsible for being the place where concrete implementations are registered and found. Better yet, the information about what classes provide what, and what other classes need what, is usually moved into some sort of configuration file. Therefore the dependency injection framework winds up with the responsibility for saying, This kind of object needs that kind of INotify, so here is what needs to be loaded and here is how to put them together. And now the dependency is not visible in your Model, and doesn't have to be leaked to other DAL classes. But the dependency injection framework itself winds up being a dependency for everything.

So should you roll your own solution, or use a dependency injection framework? My personal opinion is that if you learn the framework, you want to amortize that effort over as much code as possible. So if you have a number of places that can benefit from this feature, then use the framework, and plan to start using it in lots of places in your code. But if you don't have other places that will use it, then rolling your own, with the ugly dependency, is not a horrendous approach.

However if you see this as an opportunity to learn dependency injection, I would not fault you for using it. Even if you later decide to not use it much.

1
  • 1
    Thanks a lot, you really helped me understand what the DI framework does. I just joined Programmers, and my Stack Overflow rep doesn't carry over, or I would have voted you up. :) Commented Apr 1, 2011 at 15:06
2

Hmm, your INotify is at the model layer but the functionality you need for a particular implementation of it is in a layer higher up. There is nothing preventing you from creating a class implementing the interfaces of your notification system in that layer (DAL or even BAL) while still having the mechanics declared in the model layer. Just be watchful not to drag stuff upstream (Liskov Substitution Principle).

Then to fire it up you could use some sort of factory to instantiate the implementation you need at the right level that is then interpreted as just another notifier. To achieve this injection frameworks are indeed a nice way to go, especially so if the configuration, once set, is not dynamic. This said most can also deal with dynamic re-configuration, it's just a bit more work.

I hope I understood your problem correctly and that helps. If I'm out in the fields just edit and the question I'll see if I can give it another go.

1
  • No, this is very helpful, thank you! I'd upvote you if I could carry my rep over from SO! :) Commented Apr 1, 2011 at 15:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.