A few comments.
I would avoid building MyCompany.Common.dll . Usually they end up filling in all kinds of unrelated things, which then change, which often requires restoring all your assemblies.
I would name your assemblies with the name of the application, as well as the name of the company. MyCompany.MyApplication.Business.dll preferable to MyCompany.Business.dll . It is then easier to split applications into additional parts and reuse code from multiple applications.
It is better to have separate contract assemblies for each type of assembly assembly that you will have. In your case, I would suggest the following:
MyCompany.MyApplication.Windows-Contract.dll MyCompany.MyApplication.Windows.dll MyCompany.MyApplication.Web-Contract.dll MyCompany.MyApplication.Web.dll MyCompany.MyApplication.Business-Contract.dll MyCompany.MyApplication.Business.dll MyCompany.MyApplication.Data-Contract.dll MyCompany.MyApplication.Data.AccountingSys1.dll MyCompany.MyApplication.Data.AccountingSys2.dll
From your description, it turns out that the AccountingSys1 and AccountingSys2 assemblies have a common contract, therefore, only one contract assembly for two implementation assemblies.
Contract builds should represent your design, not your implementation, and only changes due to design changes. You should avoid having “significant” code (to avoid errors), and you should limit code to interfaces, enumerations, exceptions, attributes, event arguments, and structures — all without “significant” code.
When setting up assembly references, you must ensure that assemblies only ever reference assembly nodes, for example:
Data.AccountingSys1 Data-Contract Data.AccountingSys2 Data-Contract Business Business-Contract Data-Contract Windows Windows-Contract Business-Contract Data-Contract (maybe) Web Web-Contract Business-Contract Data-Contract (maybe)
As a result, implementation assemblies never depend on other implementation assemblies. When you change the implementation, you have only one assembly to restore.
An exception to this rule is the creation of an inheritance hierarchy. For example, you can create *.Data.AccountingSys.dll to determine the base classes for two specific accounting system assemblies.
If you can follow all the above, you will need to implement some kind of dependency recharging approach in order to be able to instantiate objects from interfaces in contract assemblies. You can use the existing DI infrastructure or create a third set of *-Factory.dll assemblies that contain your factory methods.
Another advantage of this structure is that unit testing is much simpler and can be based on contracts rather than implementation, helping you write clean, testable code.
This may seem like a lot of builds, but the benefits that you get from saving the code from creating unpleasant dependencies will greatly reduce the likelihood that your project will become too complex and will help the driver of good quality as you progress. A little pain will now eliminate so much pain later.