Стандартный подход, который я часто использую, состоит в том, чтобы иметь общую библиотеку объектных моделей, представляющих мой домен: Customer, Order, OrderLine и т. Д., Которые являются общими для всех слоев.
Более того, не делитесь типами между слоями, просто делитесь интерфейсами и имейте фабрику в наличии, когда требуются экземпляры.
После этого вы можете иметь подключаемый DAL, но ваш DAL по-прежнему должен соответствовать договору о возврате ICustomer.
Это не мешает ссылкам быть обязательными, необходима ссылка, по крайней мере, на интерфейсы - как прокомментировал кто-то другой, другие ссылки, для сильных типов или фабрик, могли бы тогда быть учтены - например, с IoC / DI frameworks.
На мой взгляд, модель commom - это сквозная проблема в дизайне вашего приложения, и ее не следует рассматривать как нарушение слоев.
Обновление: это очень расплывчатое объяснение решения, так что я стою на нем опираться.
Обновление 2: и теперь, конечно, чтобы ответить на ваш вопрос. Стандартный способ непосредственного удаления ссылки на DLL - это предоставление контракта через интерфейс, в вашем случае с помощью метода DAL GetCustomers
ваш уровень обслуживания связывается с интерфейсом, но запрашивает экземпляр DAL через внедрение / инверсию зависимости. Каркас управления или, проще говоря, заводской. Я обычно иду заводским путем для небольших приложений, он включает в себя другую DLL. Сервисный уровень будет ссылаться на интерфейсы и фабрику, DAL будет ссылаться на интерфейс, фабрика будет ссылаться на интерфейс и DAL.