In my current development situation, we have a lot of DLLs, executables, and static libraries. How do you decide what should go into a DLL? What should go into an executable? Why have separate functionality in different executable files? I'm hoping the answer will be concise, but this is a largely opinionated topic it would seem.How do you decide where functionality resides in a large-scale project (more than one executable file)? I'm expecting answers to range from, Good Design to Modularity to Whatever management puts in a requirements doc.
How do you decide where functionality should belong in a large-scale project?
design;architecture;libraries
Choosing between a library and an executable file is relatively simple: does it make sense to execute the code you want to put into an executable as a standalone program? If not, it should probably be a library. In general, I would favour a thin executable layer over as many libraries as needed, since that makes it easier to reuse those backend libraries later and they are not tied to a particular program.Far as deciding on how to split your code between libraries goes, you might find Uncle Bob Martin's article on granularity useful.In it he talks about OO structure and defines several principles that can help you package your code appropriately. These are also covered in more detail in his book, Agile Principles, Patterns, and Practices in C#.I will summarize the principles below:The Reuse/Release Equivalence Principle (REP)The granule of reuse is the granule of release. Only components that are released through a tracking system can be effectively reused. This granule is the package.Uncle Bob defines reuse as being able to statically or dynamically link the reused library into his program and never having to look at its source code. When a new version of the library is released, he can just integrate it into his system.Treating libraries this way drives only keeping related things together in the same package. Otherwise, the library's consumers might have to upgrade to a new version for no reason or lag a few versions behind.The Common Reuse Principle (CRP)The classes in a package are reused together. If you reuse one of the classes in a package, you reuse them all.This principle supports the one above. If you have classes in the same package that aren't related to one another, you may be forcing the users of your library to upgrade unnecessarily.The Common Closure Principle (CCP)The classes in a package should be closed against the same kinds of changes. A change that affects a package affects all the classes in that package.This principle talks about maintainability. The idea here is to group classes based on how they might need to change. That way your changes can be localized to one part of the application and not spread all over.The Acyclic Dependencies Principle (ACP)The dependency structure between packages must be a Directed Acyclic Graph (DAG). That is, there must be no cycles in the dependency structure.Disallowing cyclic dependencies allows each package to be developed independently and released to the rest of the company when new changes are ready. This way you don't end up with two teams deadlocked, waiting on each other to finish some work.