Структура бизнес-уровня, как вы строите свой?
Я большой поклонник NTiers для моего выбора развития, конечно, это не соответствует каждому сценарию.
В настоящее время я работаю над новым проектом, и я пытаюсь играть с тем, как я обычно работаю, и пытаюсь увидеть, смогу ли я очистить его. Поскольку я был очень плохим мальчиком и слишком много кода вкладывал в презентационный слой.Моя обычная структура бизнес-слоя такова (базовый вид it):
- бизнес
- услуги
- Фоокомпонент
- FooHelpers
- FooWorkflows
- Бахкомпонент
- BahHelpers
- BahWorkflows
- Фоокомпонент
- утилиты
- общие
- ExceptionHandlers
- импортеры
- и т. д...
- услуги
Теперь с вышеизложенным у меня есть большой доступ к прямому сохранению объекта Foo и Bah объект, через своих соответствующих помощников.
В XXXHelpers дать мне доступ, чтобы сохранить, отредактировать и загрузить соответствующие объекты, но куда мне деть логику, чтобы сохранить эти объекты с объектами ребенка.
Например:
У нас есть следующие объекты (не очень хорошие объекты, которые я знаю)
- сотрудник
- EmployeeDetails
- EmployeeMembership
- EmployeeProfile
В настоящее время я бы построил их все в слое презентации, а затем передал бы их для их помощников, я считаю, что это неправильно, я думаю, что данные должны быть переданы в одну точку выше представления в бизнес-уровне какое-то место и отсортированы там.
Но я немного теряюсь в том, куда бы я поместил эту логику и как назвать сектор, пойдет ли он под Utilities как EmployeeManager или что-то вроде этого?
Что бы вы сделали? и я знаю, что это все предпочтения.
Более детальная компоновка
Рабочие процессы содержат все вызовы непосредственно к DataRepository, например:
public ObjectNameGetById(Guid id)
{
return DataRepository.ObjectNameProvider.GetById(id);
}
А затем помощники предоставляют доступ к рабочим процессам:
public ObjectName GetById(Guid id)
{
return loadWorkflow.GetById(id);
}
Это делается для сокращения количества дубликатов кода, так как вы можете сделать один вызов в рабочем процессе для getBySomeProperty а затем несколько вызовов в помощнике, который может выполнять другие операции и возвращать данные по-разному, плохим примером будет public GetByIdAsc и GetByIdDesc
Разделяя вызовы модели данных с помощью DataRepository, это означает, что она будет можно поменять модель на другой экземпляр (это было мышление), но ProviderHelper не был сломан, поэтому он не взаимозаменяем, так как это трудно сделать, к сожалению. Я не собираюсь менять технологию доступа, но в будущем может быть что-то лучше или просто что-то, что все крутые ребята сейчас используют, что я, возможно, захочу реализовать вместо этого.
Имя проекта.Ядро
projectName.Business
- Interfaces
- IDeleteWorkflows.cs
- ILoadWorkflows.cs
- ISaveWorkflows.cs
- IServiceHelper.cs
- IServiceViewHelper.cs
- Services
- ObjectNameComponent
- Helpers
- ObjectNameHelper.cs
- Workflows
- DeleteObjectNameWorkflow.cs
- LoadObjectNameWorkflow.cs
- SaveObjectNameWorkflow.cs
- Utilities
- Common
- SettingsManager.cs
- JavascriptManager.cs
- XmlHelper.cs
- others...
- ExceptionHandlers
- ExceptionManager.cs
- ExceptionManagerFactory.cs
- ExceptionNotifier.cs
projectName.Data
- Bases
- ObjectNameProviderBase.cs
- Helpers
- ProviderHelper.cs
- Interfaces
- IProviderBase.cs
- DataRepository.cs
projectName.Data.Model
- Database.edmx
projectName.Entities (Entities that represent the DB tables are created by EF in .Data.Model, this is for others that I may need that are not related to the database)
- Helpers
- EnumHelper.cs
Имя проекта.Presenation
(зависит от того, как называется приложение)
projectName.web
projectName.mvc
projectName.admin
Тестовые проекты
projectName.Business.Tests
projectName.Data.Test
2 ответа:
+1 за интересный вопрос.
Итак, проблема, которую вы описываете, довольно распространена - я бы выбрал другой подход - во-первых, с логическими уровнями, а во-вторых, с служебными и вспомогательными пространствами имен, которые я бы попытался полностью исключить-я расскажу вам, почему через секунду.
Но во-первых, мой предпочтительный подход здесь-довольно распространенная архитектура предприятия, которую я постараюсь кратко осветить, но там гораздо больше глубины. Это действительно требует некоторых радикальных изменений в мышление-использование NHibernate или Entity framework, чтобы позволить вам напрямую запрашивать вашу объектную модель и позволить ORM заниматься такими вещами, как отображение в базу данных и из нее, ленивая загрузка отношений и т. д. Это позволит вам реализовать всю вашу бизнес-логику в рамках модели домена.Сначала уровни (или проекты в вашем решении);
Ваше обращение.Домен
Модель предметной области-объекты, представляющие ваше проблемное пространство. Это обычная среда CLR объекты со всей вашей ключевой бизнес-логикой. Именно здесь будут жить объекты вашего примера, а их отношения будут представлены в виде коллекций. В этом слое нет ничего, что имеет дело с постоянством и т. д., Это просто объекты.
Ваше обращение.Данные
Классы репозитория-это классы, которые имеют дело с получением агрегированного корня(корней) вашей модели домена.
Например, маловероятно, что в ваших примерах классов вы захотите посмотреть на EmployeeDetails, не глядя на сотрудника (предположение, которое я знаю, но вы получаете gist - строки счета-фактуры-лучший пример, вы обычно получаете строки счета-фактуры через счет, а не загружаете их самостоятельно). Таким образом, классы репозитория, из которых у вас есть один класс на aggregate root, будут отвечать за получение исходных сущностей из базы данных с помощью рассматриваемого ORM, реализацию любых стратегий запросов (таких как подкачка или сортировка) и возврат aggregate root в потребитель. Репозиторий будет использовать текущий активный контекст данных (ISession in NHibernate) - способ создания этого сеанса зависит от типа создаваемого приложения.
Ваше обращение.Рабочий процесс
- также можно назвать YourApplication.Сервисы, но это можно спутать с веб-сервисами
- этот уровень полностью посвящен взаимосвязанным, сложным атомарным операциям-вместо того, чтобы иметь кучу вещей, которые должны быть вызваны в вашем уровне представления, и поэтому увеличьте сцепление, вы можете обернуть такие операции в рабочие процессы или службы.
- возможно, вы могли бы обойтись без этого во многих приложениях.
Другие уровни зависят от архитектуры и реализуемого приложения.
Ваше обращение.YourChosenPresentationTier
Если вы используете веб-службы для распределения уровней, то вы создадите контракты DTO, представляющие только данные, которые вы предоставляете между доменом и системой. потребитель. Вы бы определили ассемблеры, которые знали бы, как перемещать данные в эти контракты и из них из домена (вы никогда не отправляли бы доменные объекты по проводу!)
В этой ситуации, а также при создании клиента, вы будете использовать контракты операций и данных, определенные выше на уровне представления, вероятно, привязанные непосредственно к DTO, поскольку каждый DTO должен быть специфичным для просмотра.
Если у вас нет необходимости распределять уровни, помните первое правило распределенные архитектуры не распространяются, тогда вы будете использовать рабочий процесс/службы и репозитории непосредственно изнутри asp.net, mvc, wpf, winforms и др.
Это просто оставляет место, где устанавливаются контексты данных. В веб-приложении каждый запрос обычно довольно самодостаточен, поэтому лучше всего использовать контекст с областью действия запроса. Это означает, что контекст и соединение устанавливаются в начале запроса и удаляются в конце. Это тривиально, чтобы получить выбранный вами МОК / зависимость платформа впрыска для настройки компонентов по запросу для вас.
В настольном приложении, WPF или winforms, у вас будет контекст для каждой формы. Это гарантирует, что изменения в доменных сущностях в диалоге редактирования, которые обновляют модель, но не попадают в базу данных (например, был выбран параметр отмена), не будут мешать другим контекстам или, что еще хуже, случайно сохранятся.
Инъекция зависимостей
Все вышеперечисленное было бы определено как интерфейсы в первую очередь, с конкретными реализации, реализованные через IoC и dependency injection framework (мое предпочтение-castle windsor). Это позволяет изолировать, макетировать и модульно тестировать отдельные уровни независимо друг от друга, и в большом приложении инъекция зависимостей является спасением жизни!
Эти пространства имен
Наконец, причина, по которой я бы потерял пространство имен помощников, заключается в том, что в модели выше они вам не нужны, но также, как и пространства имен утилит, они дают ленивым разработчикам повод не думать о том, где кусок кода логически сидит. Мой друг.Прислуга.* и MyApp.Полезность.* просто означает, что если у меня есть какой-то код, скажем обработчик исключений, который, возможно, логически принадлежит MyApp.Данные.Хранилища.Клиенты (возможно, это клиент ref не является уникальным исключением), ленивый разработчик может просто разместить его в MyApp.Полезность.CustomerRefNotUniqueException без необходимости действительно думать.
Если у вас есть общий код типа фреймворка, который вам нужно завернуть, добавьте MyApp.Рамочный проект и соответствующие пространства имен. Если вы добавляете новую модель подшивки, поместите ее в MyApp.Рамки.Mvc, если это обычная функция ведения журнала, поместите ее в MyApp.Рамки.Ведение журнала и так далее. В большинстве случаев нет необходимости вводить пространство имен утилит или помощников.
Завернуть
Так что царапает поверхность-надеюсь, это поможет. Вот как я разрабатываю программное обеспечение сегодня, и я намеренно старался быть кратким - если я могу подробно рассказать о каких-либо деталях, дайте мне знать. Последнее, что нужно сказать дальше эта самоуверенная часть выше для разумно крупномасштабной разработки - если вы пишете Блокнот версии 2 или корпоративную телефонную книгу, выше, вероятно, полный перебор!!!
Ура! Тони
На этой странице есть хорошая схема и описание макета приложения, хотя он выглядит ниже в статье приложение не разбивается на физические слои (отдельный проект) - Entity Framework Poco Repository