6

I'm in a situation where part of my system has a dependency on another module in the same system, but the modules themselves need to remain independently deployable, where the parts they depend on would be filled in with another implementation.

A module in this instance is the concept of a bunch of components that all contribute to servicing a business function. The system is also built in C#.

So, where I depend on another module, the source module defines an interface describing the functions it needs the dependent party module to implement. The contract types (domain model objects) are also interfaces for the dependent module.

Here is where it gets a bit hazy to me. The dependency inversion principle doesn't sit well in my head at this point. Both of those "modules" are of the same importance as each other. Which should define interfaces and force the other to reference it? I'm suspecting a 3rd project sitting between the modules that handles setting up the dependencies (probably a DI container). Should the entities in the modules be interfaced? They are simply bags of get/set (no DDD here).

Can anyone offer any guidance here?

Thanks in advance.

Edit 1:

The project structure:

Module1.Interfaces
   IModuleOneServices
Module1.Models
   ModuleOneDataObject
Module1
   Module1 implements IModuleOneServices

Module2.Interfaces
   IModuleTwoServices

Module2.Models
   ModuleTwoDataObject - has property of type ModuleOneDataObject

Module2
   Module2 implements IModuleTwoServices
       depends on IModuleOneServices

Module2 needs to be deployable by itself, and remains compilable, and sometimes, run without a Module1 present at all.

Martin Blore
  • 4,645
  • 5
  • 29
  • 35
  • If you have a property of ModuleOneDataObject in Module2, I don't see how you're going to run without it being present. To eliminate that hard reference, you'll need a hard reference to a shared interface and then an IoC container (or some other late binding mechanism) to populate the property with the concrete type. – Telastyn Jul 31 '12 at 20:34
  • Right...and in that case, Module2 can define/carry that interface around, and Module1 implements that shared interface. Then the Module2 can be bootstrapped with a IoC layer over the top of it. Am I understanding you correctly? – Martin Blore Jul 31 '12 at 20:40

2 Answers2

2

Hmm, sounds like you need a glue module. How about an orchestrator which knows both the modules and can mediate between them? The modules themselves needn't know each other then.

Another option is to put the "shared" classes in a shared library that both the modules can depend on.

This is not exactly dependency inversion but why would you follow that approach dogmatically. I suspect that you are building an application with those two modules so a top down approach is fine here imho.

Falcon
  • 19,248
  • 4
  • 78
  • 93
  • I've been spinning on this. The "glue" module won't work when I want to deploy Module1 or Module2 independently. Hmm, beginning to kick myself how I've managed to my mind in to such a tangle about this. – Martin Blore Jul 31 '12 at 20:33
  • Good comment about the DI dogmatic approach. There are 3 of us working over this design and we don't take a step in a direction with out arguing it out pretty well. I thought perhaps I was missing a trick with the DI principle. – Martin Blore Jul 31 '12 at 20:36
  • 1
    @Martin Blore: Well, don't think about it too hard. The only way to achieve this are then shared deployment artifacts. Have you thought about DTOs that are generally available? – Falcon Jul 31 '12 at 21:24
  • Shared DTO's is another debate at the moment. We can have a single version of a specific domain object, one size fits all approach, and take on board that sometimes it's too fat for it's use case, meaning too much data load, but saves having 10 versions of it for each use case and the dev overhead. I guess the shared artifact design is the sweet spot. – Martin Blore Jul 31 '12 at 21:29
2

The dependency inversion principle would very nicely apply to what you are trying to do.

Let me exemplify. If A uses B, than A depends on B. To brake the dependency you introduce an interface. The interface must belong to package A and can be implement by B or C or any other module. The important part here is the belonging to A. If you do that, you can deploy A without deploying or recompiling B. A depends on an interface, which is the most abstract thing you can have in OO. Depending on abstract things is also the best you can do.

Now, this solved the problem where A uses B. If I understand you correctly, your B module also uses A. So you have a reverse dependency also. This cyclic dependency should be avoided whenever possible. However, if it happens, just create an interface belonging to B and implemented by A. Do the reverse of my previous paragraph.

This way you can deploy each of your modules independently. The only reason to deploy both is when you change the interface in such a way that it is not backward compatible any more.

Patkos Csaba
  • 2,054
  • 12
  • 16
  • And this interface contains domain object types, so those will also need to be interfaced, correct? Thanks for this, makes a lot of sense. – Martin Blore Jul 31 '12 at 20:43
  • I do not quite understand your question from the comment above, but I thing the answer is YES. Whatever is a dependency on the other object, you can reverse that by an interface. Just be careful, as with any principle, don't abuse it. Also, there is quite a possibility that one of you modules should be changed in such a way that the cyclic dependency is eliminated, nut just reversed with an interface. – Patkos Csaba Aug 01 '12 at 07:27