A simple way to approach an answer is to recognize that "DLL" stands for "dynamic-link library," and the key term is "library."
What do you want in a normal library? Good books that will be shared by many readers (users); useful items that gather dust until referenced by a reader (user) to answer a critical question at a perhaps desperate moment; and resources that link readers (users) to other resources. Code libraries are similar. We keep such things as frequently shared code, specialized or high-value reference modules, and architectural framework resources in them. Software libraries can be represented in several kinds of code artifacts, such as scripts, static libraries, dynamic libraries, components, and resource files.
Generally, I recommend making your executable modules act somewhat like scripts. They clearly outline and manage the main structure and flow of your system but call upon resources from your libraries to handle the nitty-gritty details. I find this approach better than muddying up high-level logic with confusing and overly specialized and technical low-level implementation concerns.
When considering how to allocate your code between executables and libraries, you need to consider both logical design and physical design.
In object-oriented systems, for example, it is important to logically organize your code to correctly assign responsibilities to the right methods in the right classes. This is part of logical solution design. Your logical design should be clear, clean, and lean and should be expressed in terminology that relates well to your users' domain.
When you plan to actually install your system on a user's site, you may be concerned to create a physical design that specifies how you will bundle your code into a set of easily deployable software resource objects (usually files) that can be readily mixed and matched to the needs of a particular target system. Determining which resources belong in which deployment package may generally involves some specific physical design considerations that have nothing directly to do with logical design. For example, you may want to swap out certain image file processing libraries depending on which customer will receive a given set of deployment objects.
In practice, you will find that good logical design usually leads to good physical design.
To summarize, the most important principle is intelligent packaging. By organizing your code materials into strongly related libraries, you end up with useful deployable code artifacts that you can easily re-use, move around, or give away.