5

I'm organizing a solution and I need some tips on how to properly arrange the project's components.

Right now I have everything implemented on a single project, but I feel like it makes sense to isolate some of the components on their own projects. The main modules I have are categorzed by folders on the project, and are the Logic module, Database Access module and the Model module. It makes sense to me that these modules should be defined on their own project (maybe as a DLL).

Now, my question comes from the fact that during the application startup, the logic instantiates a configuration class which reads configurations from the app.config file and is known by these modules. Does it make sense to isolate the configuration into it's own project, to prevent the other modules from depending on the logic module? If so, should the configuration class implement from interfaces so that each module only has access to it's relevant configurations?

EZ PZ
  • 99
  • 9
  • To elaborate, It doesn't seem correct to have circular dependencies between the different projects. I'm set on separating the database and the model modules into different projects, with the logic module referencing both of them, and the database module referencing the model module. If I was to keep the configuration class in the logic module, it would mean that the database module would need a reference to it, creating a circular dependency. – EZ PZ Jul 28 '14 at 09:24
  • 1
    What is in the config that you're needing to reference? –  Jul 28 '14 at 09:41
  • In short, the config class reads the app.config file and sets up some properties that are used by the modules, so if I have to change or add something in the config file, the only change I have to do in the code is in that config class. – EZ PZ Jul 28 '14 at 09:51
  • I'm sorry you will either need to store your shared config in some class/structure and pass it to your modules... or manually parse your app.config file ... app.config or solutioname.exe.config as it will be called when compiled, is well for your .exe file. Another hairy option is to inject data into your shared application resources. But we are talking of nasty ways of handling code here :) – Stígandr Aug 03 '14 at 08:39
  • 1
    Related: http://stackoverflow.com/a/9503612/126014 You may also want to read up on Robert C. Martin's package principles (the ones that are *not* SOLID): http://c2.com/cgi/wiki?PrinciplesOfObjectOrientedDesign – Mark Seemann Aug 04 '14 at 15:42
  • Just a heads up, if you move the code that uses a configuration value from web.config from proj A to proj B, you still have to define the configuration value on proj A, even though it's only being used on code defined on proj B... – Andrei Dvoynos Aug 04 '14 at 15:43
  • @AndreiDvoynos Could you explain why is that needed? – EZ PZ Aug 05 '14 at 08:08
  • That's just how it works :) I guess it's because configuration values should be set by the project using the library, which does make sense when you think about it. I'm sure there are ways to work around this, but when using regular web.config app.settings values, this is the way it works... – Andrei Dvoynos Aug 05 '14 at 16:15

1 Answers1

3

"The main modules I have are categorzed by folders on the project, and are the Logic module, Database Access module and the Model module... the logic instantiates a configuration class which reads configurations from the app.config file and is known by these modules."

The picture this paints to me is that you've got a class or classes that either take the configuration class as a constructor parameter, or there's a global/ singleton instance of the configuration class that the other classes make use of.

But the configuration class can read configs, etc. Presumably, the other classes don't need something that can read configs. They just need some values* (that happen for now to be read from a config). Those other classes don't need to go out and ask anybody for those values**; they should just require those values as parameters in their constructors.

This way, those other classes do not need to have any knowledge of the configuration class. Someone just hands them the data that they need. But who?

The answer is the entry point(s)***. Each project in the solution that contains an entry point (console apps, web apps, and test projects) has the responsibility for interfacing with the environment; it knows the context that it wants the rest of the code to run in. So the entry points need to get configuration information by whatever means necessary (e.g. your configuration class or the autogenerated MyEntryPoint.Properties.Settings) and then supply that to the constructors of the other classes they need.

*If each class requires a great deal of configuration information (as your comment below implies), consider either breaking those classes up into something simpler (because needing a lot of configuration may point to an ill-defined responsibility) or grouping the necessary information into DTOs that represent coherent concepts. Those DTOs could then be placed in their own project that can be referenced by both consumers and producers of configuration information.

**This assumes that the values obtained from the configuration class are constant for the lifetime of the objects that would be constructed with them. If not, then instead of taking those values as constructor parameters, you should take an interface (or Func) that you can call for the info you need when you need it. Those interfaces could be defined in an otherwise-empty project which anybody can reference. This sounds like what you're getting at with

"should the configuration class implement from interfaces so that each module only has access to it's relevant configurations?"

When you say

"Does it make sense to isolate the configuration into it's own project, to prevent the other modules from depending on the logic module?"

the answer is yes and no. The Logic module does stuff; doing stuff implies a need for tests; tests want to configure whatever they are testing in whatever way suits the test. So Logic shouldn't be responsible for configuration; it should itself take in information from whoever does the configuration. Rather, configuration is the entry points' job.

***I'm using "entry point" a little loosely here. I'm not talking specifically about the .entrypoint IL directive, but just the first places in your code that can be given control by stuff outside of your control. This includes Main in C# console apps, HttpApplication.Application_Start in web apps, methods recognized as tests by the test runner of your choice, etc.

Jerry Federspiel
  • 1,504
  • 10
  • 14
  • I agree that I could just pass data into the different modules, but a "wrapping class" seems better. Point in case, I have a database module factory that creates a different database instance according to a "provider" property in my configurations, and each database implementation would read it's corresponding configurations. The configurations module evaluates whether some configuration values are present or not, according to that provider property. If I passed the raw values, I'd have a huge signature just for each and every configuration value, and that doesn't seem right. – EZ PZ Aug 04 '14 at 09:46
  • Edited my answer to include: in case of huge signatures, break things up or group parameters into DTOs. – Jerry Federspiel Aug 04 '14 at 15:39