Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does DI make sense in a desktop app?

I am about to create a Desktop App (with .NET windows forms)

Essentially, I want to create an n-tier app but I also want loose coupling between layers. However, I am not very sure if this is a good approach for windows forms

And now I just wonder if it would be really a wise choice to use any IoC (StructureMap, Ninject, Spring.Net), I have used them before for Asp.Net web applications but what makes me doubt now is the fact that working with windows forms my business entities will persist when I navigate through tabs and unlike than web forms or mvc apps where it would be necesary to inject my business entity for every new request that is performed, I mean this because of the Asp.Net page life cycle where is performed the initialization and controls instantiation.

This it is kind of a long-term development project, it combines maintenance tracking, inventory, work orders, and management reporting. I am currently working on a proposal for its architecture.

Maybe I am misunderstanding the point of using an IoC, so please tell me what do you think would be a better choice?

Any point of view would be appreciated.

like image 457
luis_laurent Avatar asked Nov 12 '13 18:11

luis_laurent


People also ask

Why do we need DI in enterprise application development?

Ideally, to make testing setup easier, we'd want to put only assignments into the constructor. However without DI, you'll have a lot of logic in constructors that deal with object lookup and construction. DI eliminates the unnecessary work in the constructor and makes code a lot easier to test.

What is the difference between a Windows app and a desktop app?

Microsoft Store Apps: Interface. The same app can vary quite a bit between versions. In general, desktop apps offer more features and navigation icons, while Store apps use larger, more spaced-out buttons. This makes Store apps more convenient for touchscreen use.

Why is dependency injection necessary?

The loosely coupled structure of code using dependency injection makes it easier to reuse business logic implementations in different locations around your codebase. Suppose you need to pass an external database connection access to a method to read data from your database.


1 Answers

Your question is odd. You're question implies that you would write your business layer for a Winforms app in a different way than you would in a web application, but if you apply layering correctly, the business layer should be completely independent of the used technology. So in that sense, if you would apply the Dependency Injection pattern in the business layer of a web app, you should also apply it in the business layer of a desktop application.

I'm currently working on a Winforms project myself and using the Dependency Injection pattern (and an IoC container) extensively. For me there's not a question whether DI should be used; it follows naturally from applying the SOLID principles. Whether or not you should use a IoC container however, is a totally different question, although for the types of application's I write and the type's of architectures I use, I can't imagine life without it.

Although desktop applications are very different in nature than web applications are, I use the same patterns on both types of applications. For instance, I use constructor injection in my Windows Forms classes and those forms mainly depend on a few generic interfaces, namely:

  • IRepository<TEntity> (the repository pattern) for loading an entities.
  • IQueryHandler<TQuery, TResult> for doing complex or custom queries of all sorts.
  • ICommandHandler<TCommand> for execution of use cases (processing user actions).

I use the same abstractions in the web applications I build.

Those interfaces helped me a few months back to change this desktop application from a 2-tier application (all business logic ran in the desktop application) to a 3-tier application (where all business logic is now moved to a WCF service). We were able to do this without having to change any code in the Forms.

In the 2-tier model we didn't inject ICommandHandler<TCommand> implementations directly, but injected a (singleton) proxy class that would create a new implementation each time it was called. For instance, when the form called the injected ICommandHandler<ProcessOrder>, the actual CommandHandlerProxy<ProcessOrder> would start a lifetime scope (a lifestyle that mimics the per-request lifestyle of a web application) and would create the real ProcessOrderCommandHandler class that would do the actual logic. By doing this we ensured that a single unit of work (Entity Framework's DbContext in our case) would be injected in all classes within this 'request'. All of course with dependency injection all the way down the call graph.

In the new 3-tier model, the forms are injected with an WcfProxyCommandHandler<TCommand> which will serialize the given command to JSON and send it over to the WCF service, which will pick it up, deserializes the command, creates the ProcessOrderCommandHandler and executes the command.

But bear in mind that this model is probably very different than what you're probably used to do. For instance:

  • The real entities are hidden behind the WCF service. The desktop application knows nothing about them.
  • Instead DTOs are returned from the WCF service when data is requested through the IQueryHandler<TQuery, TResult> abstraction.
  • We use Entity Framework 5 (POCO classes with T4 and designer).
  • Those DTOs are (mostly) used for reading; they're not sent back to the server for updating.
  • Any request for a change of state (the execution of a use case) is done by sending a command message to the server (through the ICommandHandler<TCommand> abstraction).
  • One single use case is encapsulated in single class that implements the ICommandHandler<TCommand> interface.

And as I said, it's Dependency Injection all the way down and this and the described design gives us much flexibility. For instance:

  • We find it very easy to add new functionality.
  • We find it very easy to add new cross-cutting concerns.
  • It lowers the mental barrier; it makes the application easier to maintain and much less likely to break in unexpected ways.

There is however one thing I found out in the process:

  • Binding in winforms is optimized to work with DataSets. If you try anything else (Poco's, Entity Framework entities, etc) you will get some very frustrating moments where you find out that the support for anything else but DataSets is minimal. It's clear that Microsoft hasn't invested in this area and won't investing in this area anymore. To workaround these limitations we wrote our own BindingList<T> implementation and found it very hard to create an implementation that works correctly with sorting and filtering (especially since our DTOs don't implement INotifyPropertyChanged). We also wrote our own infrastructure to add DataAnnotations validation support to Winforms.

If you want to read more about the designs I use, please read these articles:

  1. Meanwhile... on the command side of my architecture
  2. Meanwhile... on the query side of my architecture
  3. Writing Highly Maintainable WCF Services
like image 179
Steven Avatar answered Oct 21 '22 19:10

Steven