Библиотечный код полезно выносить в отдельный проект, для его повторного использования.

Чтоб не перегружать потребителя данного кода информацией о многочисленных классах, в качестве хорошей практики, принято использовать паттерн Фасад.

Предлагается некая вариация данного паттерна:

Пусть все классы в проекте будут internal , вся логика, которая должна быть доступна внешнему миру – объявляется в interface.

Но как внешнему коду достучаться до реальной реализации контрактов, если они видимы только в пределах своей сборки? Пусть некий главный фасадный класс с помощью IoC контейнера выполнит связывание интерфейсов с реализациями. И тогда внешний код, запрашивая интерфейс, получит реализацию.

Выглядит это следующим образом:

/* AS.Lib */
public interface IDoSomething {
     void DoSomething();
}

internal class DoSomething : IDoSomething {
  public void DoSomething() {
     this.DoSomethingElse();
     new СкрытыйОтВнешнегоМираВспомогательныйКласс().HelpMeDoSomething();
  }
}

/* Facade */
public class Facade {

   public void RegisterBindings(UnityContainer container){
     container.RegisterType(IDoSomething, DoSomething)
   }
}

/* External code */
void Main(){
  var uContainer = new UnityContainer()
  AS.Lib.Facade.RegisterBindings(uContainer);
  uContainer.Resolve(AS.Lib.IDoSomething).DoSomething();
}

Более того, связывание можно настраивать, если требуется альтернативная реализация (см. паттерн Стратегия), подобно тому как принято настраивать Js-модули.