0

I have a large-ish application which's purpose is to aid the user in designing and analysing multiple configurations of Battle Mechs in Mechwarrior: Online. The curious can find the source here.

Essentially the data model is a VFS of directories and files where each file is a "loadout", a loadout is a base Battle Mech "chassis" + all equipment that the user puts on it.

I'm building the application with JavaFX and their take on the MVC pattern.

The UI consists of a main window where the user can manage the VFS and view stats about equipment and available Battle Mechs. From the main window the user can open an existing loadout or create a new one. Each loadout opens in a separate window.

In addition to the loadouts, I have application wide settings and current open VFS, message busses and command stacks (I'm using Command Pattern to provide multilevel-undo/redo throughout the entire application) which are passed around alot. For example the command stack is shared within each window for window-local undo. Each window has a local message bus and there is an application wide message bus too. So most sub-panels needs to get their local command stack and message bus but also the global message bus. I can't really figure out how to get a DI framework to realise which model, command stack and message bus goes where.

Both the main window and loadout windows are quite complex. They display a the full loadout in all it's components, available equipment, real-time stats and graphs. To this end, each window is broken down into sub-panels with their own controller and view, each hooked up to the same model (loadout).

I do manual constructor injection most of my dependencies that make sense. Although it is working fine, I would like to replace a lot of the wiring code with a Dependency Injection framework. And I'm looking at Dagger 2 as it seems light weight and doesn't require me to pull in a bunch of other dependencies.

However I have a hard time seeing how to apply Dagger (or any DI framework) to a data heavy application like mine. I would like to inject a lot of the sub-panels for the windows that I build and their dependencies (which get passed through from the root window as it constructs itself), but the constructor also needs pieces of user data, like the loadout to display or other configured objects which are not services.

I found an article describing the problem I'm facing here To new or not to new. Basically my model (which is data driven and loaded from massive XML files) consists entirely of "newables" and is not injectable. But the application controllers would be, if it weren't for the loadout window needing a loadout which is a newable.

The article doesn't discuss any solution to the problem I'm experiencing but at least it highlights it.

This StackOverflow answer basically says to create factories. And I find this boiler plate of creating a factory for each sub-panel kind of off-putting as I thought a DI framework was supposed to reduce the amount of code I need. The other idea was to use more modules but that seems counter to the intention of modules to me, I shouldn't have to create a new module for each model datum. Or the last option to only DI what is known at compile time. But this doesn't help with using a framework.

I'm looking for any ideas on how I should solve my problem: I would like to reduce wiring code in presence of user data. Whether it be better frameworks, patterns or if the best solution is to not use a DI framework then a motivation for that.

  • Just to clarify: It sounds like you're seeking a way to use IoC containers with objects whose lifetime/creation/initial-state depends on user data and/or user actions/events (presumably objects which have a lot of stateful fields, data members, etc); Have I understood the problem correctly, or am I missing the point? – Ben Cottrell Apr 16 '17 at 11:14
  • @BenCottrell sounds about right – Emily L. Apr 16 '17 at 11:20
1

TL;DR - You are (perhaps without realising) stuck in the middle of a collision between two (almost-opposing) methodologies/ideologies about software design.

Related answer on SO: https://stackoverflow.com/a/4407400


(A bit of detail/explanation - skip to the bottom for the conclusion)

Domain Driven Design (DDD)

The 'model' for this application fits with the principles of Domain-Driven-Design (see also: Martin Fowler's website)

In many ways, DDD has become synonymous with many peoples' understanding of "Object Oriented Design". Most popular Java books, as well as many courses and degrees tend to follow the mantra that Domain Objects should encapsulate their own data and behaviour.

The hallmark of DDD classes are those which have their behaviour and data all in the same class, ideally with all data made private; avoiding "POJOs" (in Java) which are just simple data classes only containing getters/setters or public fields.

Service Oriented Architecture (SOA) and SOLID principles

Martin Fowler once described an "anti-pattern" which he called the Anemic Domain Model. It's the anti-pattern of DDD, not just because it promotes POJOs/POCOs or other classes which only contain public data and/or getters/setters, but because it also promotes separation between those classes which contain an application's behaviour, versus those which represent its data.

The Anemic Domain Model is a natural side-effect of Service Oriented Architecture and SOLID design principles. The term 'service' is often defined in fairly vague, wooly terms, but most of the time in reality a service is just a class which contains a bunch of methods/behaviour with no data. Whereas SOLID presents a series of guidelines which naturally leads you towards writing stateless decoupled classes, DTOs, and splitting code up into separate layers and modules.

Furthermore, all of the SOLID guidelines naturally lead you toward the Anemic Domain Model by promoting stateless decoupled classes, DTOs, and segregation of code into discrete "layers" (e.g. a Data Layer, Service Layer, Business Layer, etc.).

The IoC Pattern

At its core, IoC is about relinquishing control of the lifetime and creation of objects, taking those concerns away from the rest of the application so that your classes can focus on whatever real functional requirements they need to satisfy. This works best for objects whose lifetime matches the lifetime of the application, and which can be created when the application starts.

It becomes painfully obvious when working with the Inversion of Control (IoC) pattern when you have classes which violate SOLID principles - to put it bluntly IoC Containers don't play nicely with DDD classes which depend upon their own lifetime (or the lifetime of other related classes) for their behaviour.

When an object's creation depends upon some other external event during the normal operation of the system, you're implicitly tying some functional requirement of the application into its lifetime and creation, so the practical implications of IoC usually lead to the conclusion of "Don't bother!" because it's more fuss than its worth.

DDD vs SOA

DDD has advantages; you end up with classes whose identity is clear and relatable to the real world. DDD's strength is having code which is easier to relate directly back to your requirements. If you have an application which needs to simply satisfy a set of requirements, with no need for multiple integration points, no need for a web-based interface or multithreading, then DDD is just fine.

SOA also has many advantages - particularly web and microservice environments where you're not just working with a single system, and you have multiple integration points, perhaps a lot of multi-threaded behaviour too. SOLID also becomes necessary for methodologies such as TDD/BDD - all things which are increasingly popular now compared with 15 years ago. These days most applications aren't just a single executable, they hook into all kinds of 3rd party APIs, tools, websites, and other external systems, and they need to consider various aspects of distributed computing, scalability, etc. Microservices are preferred for those reasons.

Conclusion

Your choice boils down to two things

The easiest option is to stick with your current design and forget trying to tie everything up with IoC. use IoC where it makes sense but don't try to hammer a square peg into a round hole.

An alternative is to refactor your application with IoC in mind. This will be harder to do, and it requires a paradigm shift, but there are probably two key differences:

  1. Wherever you have objects whose lifetime is part of their behaviour, consider how you would move that behaviour into a single-instance 'service' class whose lifetime can persist throughout the application.
  2. Move your data into a 'repository' or other data store which supports a method of querying that data on-demand, such as when a user issues a request. You will end up with a lot of 'anemic' DTOs for your XML structure by doing this; you could consider some options for some in-memory data store for extra querying power

Lastly, you don't need to be idealistic - use IoC where it's practical and provides a benefit. DDD and SoA can "mix" to some degree, but there will always be trade-offs and places where it makes sense to follow one over the other.

  • Yes, you are correct that I kind of feel stuck between ideologies if you will. If you wouldn't mind, could I talk with you in a chat for a bit? – Emily L. Apr 16 '17 at 12:39

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.