+ 1 за интересный вопрос.
Итак, проблема, которую вы описываете, довольно распространена - я бы выбрал другой подход - во-первых, с логическими уровнями, а во-вторых, с пространствами имен утилит и помощников, которые я постараюсь полностью выделить - я вам скажу почему в секунду.
Но сначала, мой предпочтительный подход здесь - это довольно распространенная корпоративная архитектура, которую я постараюсь кратко осветить, но там гораздо больше глубины. Это требует некоторых радикальных изменений в мышлении - использование NHibernate или Entity Framework, чтобы позволить вам напрямую запрашивать объектную модель и позволять ORM решать такие вещи, как сопоставление с базой данных и из нее, ленивые отношения загрузки и т. Д. Это позволит вам реализовать вся ваша бизнес-логика в доменной модели.
Первые уровни (или проекты в вашем решении);
YourApplication.Domain
Модель предметной области - объекты, представляющие ваше проблемное пространство. Это простые старые объекты CLR со всей вашей ключевой бизнес-логикой. Здесь будут жить ваши примеры объектов, и их отношения будут представлены в виде коллекций. В этом слое нет ничего, что связано с постоянством и т. Д., Это просто объекты.
YourApplication.Data
Репозитарий классов - это классы, которые имеют дело с получением совокупного корня (ов) вашей доменной модели.
Например, в ваших примерах классов маловероятно, что вы захотите взглянуть на EmployeeDetails, не обращая внимания и на Employee (я знаю, что вы знаете, суть строки счета-фактуры - лучший пример, вы обычно получаете счет-фактуру) линии через счет, а не загружая их самостоятельно). Таким образом, классы репозитория, из которых у вас есть один класс на корень агрегата, будут отвечать за извлечение исходных сущностей из базы данных с помощью рассматриваемого ORM, реализацию любых стратегий запросов (таких как разбиение на страницы или сортировку) и возврат корня агрегата в потребитель. Хранилище будет использовать текущий активный контекст данных (ISession в NHibernate) - способ создания этого сеанса зависит от типа приложения, которое вы создаете.
YourApplication.Workflow
- Может также называться YourApplication.Services, но это может быть перепутано с веб-службами
- Этот уровень полностью связан с взаимосвязанными, сложными атомарными операциями - вместо того, чтобы иметь кучу вещей, вызываемых на вашем уровне презентации, и, следовательно, для повышения связности, вы можете объединить такие операции в рабочие процессы или службы.
- Возможно, вы могли бы обойтись без этого во многих приложениях.
Другие уровни зависят от вашей архитектуры и приложения, которое вы реализуете.
YourApplication.YourChosenPresentationTier
Если вы используете веб-сервисы для распространения своих уровней, то вы должны создать DTO-контракты, которые представляют собой только те данные, которые вы предоставляете между доменом и потребителями. Вы бы определили ассемблеры, которые будут знать, как перемещать данные в эти контракты и из этих контрактов из домена (вы никогда не отправите объекты домена по проводам!)
В этой ситуации, когда вы также создаете клиента, вы будете использовать контракты на операции и данные, определенные выше, на уровне представления, возможно, привязку к DTO напрямую, поскольку каждый DTO должен быть специфичным для просмотра.
Если вам не нужно распространять свои уровни, помните, что первое правило распределенных архитектур - не распространять, тогда вы будете использовать рабочий процесс / службы и репозитории непосредственно из asp.net, mvc, wpf, winforms и т. Д.
Это просто оставляет там, где установлены контексты данных. В веб-приложении каждый запрос, как правило, довольно самодостаточен, поэтому контекст области запроса лучше всего подходит. Это означает, что контекст и соединение устанавливаются в начале запроса и удаляются в конце. Тривиально заставить выбранную IoC / инфраструктуру внедрения зависимостей настроить компоненты для каждого запроса.
В настольном приложении, WPF или winforms у вас будет контекст для каждой формы. Это гарантирует, что изменения в сущностях домена в диалоговом окне редактирования, которые обновляют модель, но не вносят ее в базу данных (например, было выбрано «Отмена»), не мешают другим контекстам или, что еще хуже, случайно сохраняются.
Зависимость инъекций
Все вышеперечисленное было бы сначала определено как интерфейсы с конкретными реализациями, реализованными через IoC и инфраструктуру внедрения зависимостей (я предпочитаю замок Виндзор). Это позволяет вам изолировать, моделировать и тестировать отдельные уровни независимо друг от друга, а в больших приложениях внедрение зависимостей спасает жизнь!
Те пространства имен
Наконец, причина, по которой я потеряю пространство имен помощников, заключается в том, что в приведенной выше модели они вам не нужны, но также, как и пространства имен утилит, они дают ленивым разработчикам повод не думать о том, где фрагмент кода логически сидит. MyApp.Helpers. * И MyApp.Utility. * Просто означает, что если у меня есть какой-то код, скажем, обработчик исключений, который может быть логически принадлежит MyApp.Data.Repositories.Customers (может быть, ссылка клиента не является уникальным исключением), ленивый Разработчик может просто поместить его в MyApp.Utility.CustomerRefNotUniqueException, не задумываясь.
Если у вас есть общий код типа платформы, который нужно обернуть, добавьте проект MyApp.Framework и соответствующие пространства имен. Если вы добавляете новое связующее звено модели, поместите его в MyApp.Framework.Mvc, если это обычная функция ведения журналов, поместите его в MyApp.Framework.Logging и так далее. В большинстве случаев не нужно вводить пространство имен служебных программ или помощников.
Завершение
Так что это царапает поверхность - надеюсь, это поможет. Вот как я сегодня занимаюсь разработкой программного обеспечения, и я намеренно пытался быть кратким - если я могу уточнить какие-либо особенности, дайте мне знать. Последнее, что нужно сказать об этом мнении, это выше, для достаточно крупномасштабной разработки - если вы пишете блокнот версии 2 или корпоративную телефонную книгу, то, вероятно, это полный перебор !!!
Приветствие
Tony