4

Given a project architecture as follows, where each box represents an assembly (all are class libraries), and each arrow stands for a dependency:

architecture

Might be worth nothing that this project is massive (well, it will become!), the complexity incurred by such decoupling is absolutely warranted.

Business Logic Layer

The BLL library defines and organizes various sets of POCO interfaces in its Abstract namespaces, and implements various functionality logic in its Concrete namespaces; the functionality logic refers to and uses the abstract POCO interfaces.

Data Access Layer

The DAL library implements the POCO interfaces from the BLL using partial classes that "extend" Linq to SQL (dbml) classes, so if there's a Customer L2S class, it implements the BLL ICustomer interface. Sometimes the L2S class has different types, or the BLL abstraction uses enum types, but most often the partial class code looks like this:

public partial class SomeEntity : ISomeEntity { }

This assembly provides the Model, by exposing classes named "xxxxModel", which are constructor-injected with repositories regrouped into services, but there's a twist: the "xxxxxModel" classes implement an interface called IUnitOfWork which encapsulates a database transaction that must be able to operate on all repositories at its disposal... possibly with a dynamic (runtime-determined) connection string. To achieve this, I created "model factories" which create a single DataContext of the appropriate type, and inject the same instance in each repository.

Presentation Logic Layer

Some of the BLL POCO interfaces need a "ViewModel" implementation (e.g. to display as items in a list) - the PLL library defines them; however the goal of this library is more focused around, well, the actual presentation logic, although there is no reference to WPF whatsoever (other than a single reference to WindowBase but I'll get to that). So this assembly provides the ViewModel.

One thing to note is that the ViewModel here, completely controls the View - each ViewModel is injected with an implementation of IViewFactory that creates an IView implementation - technically the ViewModel is blissfully unaware of the implementations, so I'm not sure how deeply it violates MVVM (to be honest I don't really care about that point - especially after reading a blog post like this one), but the thing to point out is that it's the ViewModel that litterally creates the View and assigns the DataContext. This makes it very simple to deal with things that should be simple, such as showing and closing a window.

So all ViewModel classes inherit ViewModelBase, which is an abstract class that takes a IViewFactory in its protected constructor, effectively instantiating the View. Here's the offending code:

/// <summary>
/// An interface for a ViewModel.
/// </summary>
public interface IViewModel : INotifyPropertyChanged
{
    /// <summary>
    /// Notifies listener that the value of the specified property has changed.
    /// </summary>
    void NotifyPropertyChanged<TProperty>(Expression<Func<TProperty>> property);

    /// <summary>
    /// Notifies listener that the value of the specified property has changed.
    /// </summary>
    void NotifyPropertyChanged(string propertyName);

    /// <summary>
    /// Closes the view.
    /// </summary>
    void Close();

    /// <summary>
    /// Displays the view.
    /// </summary>
    void Show();

    /// <summary>
    /// Displays the view (modal) and returns a <see cref="DialogResult"/> value indicating how the view was closed.
    /// </summary>
    DialogResult ShowDialog();
}

So a ViewModel essentially controls a View up to the point of encapsulating it, which makes it easy to implement my own DialogResult enumeration (much closer to the way WinForms had it).

Views Assembly

Because this library references and consumes ViewModels from the PLL assembly which implement interfaces defined in the BLL assembly, an arrow is missing here - there's also a dependency upon the BLL, but it's only used indirectly. This library provides the View, by defining WPF/XAML windows and controls.

Apart from IValueConverter and other MarkupExtension implementations, there's not much C# code in this library, the hard references to ViewModel implementations are all in XAML d:DataContext {d:DesignInstance...} declarations.

So, what's wrong?

I definitely like the direction of the dependencies, including the PLL/Views one. The fact that the BLL has zero dependencies mean I can reuse that library and come up with a WebUI if I ever need one (and that WILL happen... eventually!), and the DAL/BLL decoupling means parts of the data model could be coming from Web services, some cloud, or whatever - business logic couldn't care less and that's good.

The main issue I'm having, is that I find myself writing LOTS of abstract factories, and implementing even more of them, and I'm not always reusing these abstractions (hence the smell), but it seems to me like whenever I ask myself how I can inject a "dynamic" dependency, the answer is almost automatically - "an abstract factory will do!"... which is probably right, it's just that I don't think it's right that pretty much everything is a "dynamic" dependency!

public class SomeViewFactory : IViewFactory
{
    public IView Create(IViewModel viewModel)
    {
        return new SomeWindow {DataContext = viewModel};
    }
}

public class SomeRepositoryFactory : DataRepositoryFactory<SomeEntity>
{
    public IRepository<SomeEntity> Create(DataContext context)
    {
        return new SomeRepository(context);
    }
}

I even have an abstract IFunctionalityFactory:

public interface IFunctionalityFactory
{
    IFunctionality Create();
}

public class SomeFunctionalityFactory : IFunctionalityFactory
{
    private readonly IFunctionalityAuth _auth;
    private readonly ISomeBusinessLogic _logic;

    public SomeFunctionalityFactory(IFunctionalityAuth auth, ISomeBusinessLogic logic)
    {
        _auth = auth;
        _logic = logic;
    }

    public IFunctionality Create()
    {
        var canExecute = auth.IsCurrentUserAuthorised(typeof(SomeFunctionality).FullName);
        return new SomeFunctionality(canExecute, _logic);
    }
}

Is it normal to have factories for almost everything - from repositories to services and models in the DAL, to Views and ViewModels in the PLL/View library? For now the project is rather small and I'm still the only programmer working on it; I'd like to have an architecture as SOLID and simple as possible when it comes to implementing new functionalities.

This is only my 2nd project implementing DI/IoC, needless to say I'm trying to run (and swim, and perhaps even fly) before I can walk...

Any tips or issues I'm not seeing? Am I abusing Abstract Factory? How else could I instantiate a View? How else could I have a data model that could connect to any of a given set of [almost] identical databases, at the user's discretion? I know I'm breaking MVVM, have I overlooked anything in the pattern that could simplify my existence?

14
  • 3
    Are you using any IOC container (Ninject, Castle Windsor, etc.)? A container that can resolve dependencies would remove the need to define so many abstract factories.
    – Despertar
    Jul 17, 2013 at 3:38
  • 2
    Because that woukd mean passing the kernel around and using it as a Service Locator; I want to avoid having a Ninject.dll dependency anywhere else than in the CompositionRoot assembly. Jul 17, 2013 at 4:27
  • 2
    I'm waiting for IAbstractFactoryBuilderStrategyMaker to be defined. (I don't know the technologies you're using well, but the pieces of code you've posted so far absolutely reek of pointless overengineering.) Jul 17, 2013 at 9:25
  • 2
    So u are using the ViewFactory inside ur ViewModel? If so, you are hiding dependencies which is a well know issue when starting to work with DI... It is dangerous when hiding dependencies, future developers will not be thankful... Jul 17, 2013 at 12:44
  • 2
    It seems you're happily ripping KISS and YAGNI to shreds here. My advice is don't come up with big design patterns up front, refactor to patterns. I.e., get a working implementation of a slice of functionality first, then consider using DPs if needed. Nothing good can come out of an architecture prematurely littered with complex (especially, creational) design patterns. Jul 18, 2013 at 14:50

1 Answer 1

4

It seems you're struggling trying to create objects that create objects... wouldn't you be better off just creating objects ?

Ninject allows you to do so in a very simple way :

// Ninject module
Bind<IRepository<SomeEntity>>().To<SomeEntityRepository>();

// injected class
public class SomeService
{
  public SomeService(IRepository<SomeEntity> repository)
  {
    // ...
  }
}

// get instance of injected class
var someService = kernel.Get<SomeService>();

I can't see the benefit of an abstract factory in addition to an IoC container when you're creating objects based on basic, compile-time-known constraints such as in your SomeViewFactory and SomeRepositoryFactory examples.

The dynamic nature of SomeFunctionalityFactory would be more debatable, but when you think of it, do you really need to instantiate an object with the built-in notion that it will never be able to be used by the current user ? Wouldn't it make more sense to have the object evaluate user authorization when the functionality is called ?

5
  • 1
    U're using it as a ServiceLocator. Which is an anti-pattern, since u still hide dependencies. Jul 18, 2013 at 14:36
  • Actually from the "answer" I supplied that's precisely what I ended up doing, only with convention-based bindings, so as new features (and repositories) are added I don't need to go back to the CompositionRoot assembly and manually configure bindings for the new repositories, as long as I follow the naming conventions. As for the "Functionality" auth, you're right, but there's more to it... this library needs to "fit in" with an existing VB6 code base that it's eventually going to be replacing (one bite at a time). The existing VB6 code must be able to know if it should show a button or not. Jul 18, 2013 at 14:37
  • @FrederikP. This was a simplified example. In reality you would only get top level objects through the container. There's still at least one place at the root of your system where you need to have a "hidden dependency" though, it can't be injection forever. Jul 18, 2013 at 15:02
  • Depends how you look at it, in MVC for example you can influence the DependencyResolver to resolve using ur ninject kernel. You then inmediatly start injecting your objects (from ninject) into the controllers. So there is no "hidden dependency" apart from registering your IOC Container to the framework (not 100% sure about wpf, but I assume it's possible aswell like in MVC) Jul 18, 2013 at 21:51
  • @guillaume31 I see what you mean with compile-time known constraints - I think somewhere along the line I mixed up dependencies with simple ViewModel properties. That's how I ended up with a MessageWindowViewModelFactory. Jul 19, 2013 at 0:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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