Please see my comment to the question.
Even though I still think my comment is correct, I can help you with just one thing which would not require writing a whole book. I can help you to dismiss your illusion which you seem to have about "DLLs". Even though it would be quite helpful to break the project into smaller projects, you certainly take it too serious.
Here is why: the .NET units of modularity are not "DLLs". They are
assemblies, and it makes big difference:
Assemblies in the Common Language Runtime[
^],
Understanding and Using Assemblies and Namespaces in .NET[
^],
Assembly Class (System.Reflection)[
^].
What you call "DLL" is nothing but an executable module of an assembly; usually, there is only one per assembly, but it's possible to few executable files (meaning: PE files) per assembly. "EXE" is the one with the entry point ("
Main
").
"DLLs" have not special meaning in .NET. There differences between DLL, EXE (and a lot more) are not really fundamental. To tell you just one thing which is not very well-known: a "real" EXE file (meaning, with application manifest and entry point) can also be referenced by another assembly exactly as a DLL. There are even the cases when it makes certain practical sense.
How it translates into development technology? Very simply: the boundaries between assemblies are so subtle that you don't really need to think that the decision of what goes in what assembly should be taken extremely responsively. There is nothing like that. Just the opposite: it's too easy to rethink distribution of code between assemblies as you develop. It won't be the bad practice to prototype some part of code using one single assembly, and later, as this code matures, re-distribute its parts into separate assemblies. Moreover, it may make your development process much smoother, because it can be hard to predict what part of code is going to grow too big. I'm telling you: the ability to postpone decision is a great thing. And the decision about distributing code between assembly is not the most important.
There are much more important architectural issues. You probably have too narrow view on the
separation of concerns. Those more important issues include: how workload is distributed between threads and how threads are synchronized (just one principle: best synchronization is no synchronization), how your architecture reflect the dependencies between programming entities, and one "little" problem which is the matter of concerns of nearly all
architectural patterns: how UI is isolated from other aspects of your application. In more heavy cases, you may need
application domain. And also, applications can be distributed. Anyway, the present paragraph is the only one which can be evolved into a whole book; for the rest of the issues, one answer would be enough.
Just one practical advice: put all projects in one solution, and reference between assemblies between projects, but DLLs. This way, you will avoid a lot of troubles.
There is only one thing which needs to be different when you put things apart in different assemblies: some
internal
access modifiers need to become
public
. Simple, isn't it?
The main idea of this answer is: this answer (and not other
Quick answer) cannot be used as a comprehensive advice for "
programming in the large".
—SA