реализовать объекты функциональности базы данных в N-уровневой архитектуре? - PullRequest
5 голосов
/ 30 марта 2011

Я добавляю функциональность на наш сайт, который асинхронно выполняет длительные процессы с использованием MSMQ.Однако выполнение этой синхронизации означает, что мы должны уведомлять пользователей о завершении их запросов.Используя шаблон команды, я создал интерфейс * с именем INotify и скомпоновал его в классе сообщений, поэтому класс обработки сообщений может просто вызвать GiveNotice () для объекта INotify сообщения.Первая реализация, EmailNotify, оказалась более сложной, чем ожидалось, так как я с удивлением обнаружил, что MailMessage не сериализуем, но начал работать.

Сейчас я работаю над новым конкретным уведомителем, DBNotify, который будетвызвать своего рода SP и обновить статус в основной транзакционной базе данных.Я запутался в том, что хотел бы повторно использовать архитектуру DAL, которую мы уже создали, но INotify является участником проекта Model, который является более фундаментальным, чем DAL.

Наша иерархия выглядит следующим образом: Обычное> Модель> DAL> BAL

Подробнее об уровнях.Помните, что я унаследовал это от: Common отвечает за все «служебные» функции, которые используются во многих местах приложения, такие как доступ к настройкам конфигурации, анализ строк, функции, не связанные с бизнесом.

Модельбизнес-объекты, которые некоторые называют объектами передачи данных, коллекции геттеров и сеттеров.Я добавил несколько «умов» на этом уровне, но только бизнес-правила, внутренние для этого объекта, такие как «Имя элемента должно начинаться с буквенно-цифрового символа».

DAL - это уровень доступа к данным, теоретическивсе, что здесь происходит, это объекты модели перемещаются в базу данных и из нее.

BAL - бизнес-уровень;теоретически применяются бизнес-правила, регулирующие взаимодействие объектов (т. е. «форма должна содержать как минимум два элемента»).

Таким образом, интерфейс INotify определяется как абстракция, позволяющая изменять метод уведомления.независимо (например, электронная почта, TXT, Twitter и т. д.).Это фундаментально для системы, поэтому я создал ее на уровне модели, который не зависит от уровня DAL.Тем не менее, я создаю новую конкретную реализацию INotify, метод уведомления которого заключается в вызове SP в базе данных.

Кто-нибудь еще имел дело с бизнес-объектом, целью которого является взаимодействие с базой данных, и как выПоместите это в вашу N-уровневую архитектуру?

Прежде чем вы скажете мне использовать Linq to Sql, большое спасибо.Это не технический вопрос (как мне это сделать), это вопрос дизайна (как мне это сделать).

Я думаю, что есть сайт StackExchange, более сфокусированный на таких типах вопросов, не зависящих от языка.так что я собираюсь скопировать это туда.

Ответы [ 6 ]

3 голосов
/ 01 апреля 2011

Возможно, на самом деле это не ответ на ваш вопрос, но, тем не менее, есть над чем подумать.

Я не согласен с тем, куда вы помещаете доступ к данным в иерархию компонентов.Я бы не поставил его между двумя функциональными уровнями домена.Даже не «над» классами модели одного домена.Доступ к данным или их постоянство не имеют значения для любого класса домена.Это должно быть только то, что можно сделать с ними, а не то, что они делают.

Хотя я начал кодировать такие вещи, как TClient.Save и TClient.Load, я пришел к выводу, что это неКлиент, который решает, что он должен быть сохранен, но пользовательские взаимодействия, которые определяют, когда нужны данные экземпляра домена и, следовательно, должны быть загружены, и когда данные клиента должны быть сохранены, если вообще.Поэтому я теперь сторонник кодирования (в GUI, точнее, контроллера в GUI) таких вещей, как DataStore.Load(ClientInstance) и DataStore.Save(ClientInstance).Затем это зависит от уровня доступа к данным, чтобы выяснить, как это сделать.Он может использовать отражение в C # или новый RTTI в Delphi для итерации по всем свойствам клиента, чтобы он мог отправлять их в базу данных.

В то время как многоуровневое использование - это очень хорошая концепция, чтобы отделить проблемы и не дать вам поставитьПовсюду, просто придерживаясь «Вы можете звонить, но не звонить», это не так помогает при решении таких вопросов, как ведение журнала, обработка исключений, уведомления и все другие интересные сквозные проблемы, которые нужны каждому другому компоненту / слою.

Кроме того, общий слой, поскольку он является служебным слоем, должен быть действительно доступен для всех остальных слоев.

Чтобы поместить все это в изображение (где я сохранил различие между вамисделайте между простыми классами доменов, вашей моделью и межклассовыми бизнес-правилами, вашим BAL):

+---+   +-------------+
| C |<--| Data Access |<--------------------------+
| o |   +-------------+                           |
| m |         |                                   |
| m |         |                                   |
| o |         v                                   |
| n |   +-------------+   +----------------+   +-----+
|   |<--| Model       +<--| Cross class    |<--| GUI |
|   |   +-------------+   | business rules |   |     |
|   |                     |                |   |     |
|   |<--------------------|                |   |     |
|   |                     +----------------+   |     |
|   |                                          |     |
|   |<-----------------------------------------|     |
+---+                                          +-----+

Реализация INotify, которая обращается к базе данных, в настоящее время находится в модели, которая на рисунке выше не делаетвызов самого уровня доступа к данным, он только вызывается, или, вернее, intдопущено уровнем доступа к данным.

Вопрос на самом деле в том, должен ли INotify входить в «модель», часть уровня домена, или это должен быть общий интерфейс и должен быть отдельный ».Уровень уведомления / компонент, доступный из домена и графического интерфейса.Этот новый компонент может касаться не только уведомлений, но и многих других сквозных задач, например ведения журнала.У него есть доступ к общим (конечно) и компонентам доступа к данным, а также к графическому интерфейсу, по крайней мере, в некотором роде обратного вызова.

На рисунке ниже я попытался визуализировать это, но я неочень хорош в визуализации и всегда имеет проблемы с этими надоедливыми поперечными резцами.Вот почему нет стрелок вызова от уровня домена к сквозным задачам, хотя, конечно, уровень домена должен иметь доступ, например, к интерфейсу «Logger».Может быть, я стараюсь провести различие между общими и сквозными компонентами, и можно привести аргумент для их объединения и визуализации их в виде отдельных блоков в слое / компоненте «Утилиты».

        +--------------------------------------------+
  +-----| Cross cutting concerns                     |
  |     +--------------------------------------------+
  v           v^                                    ^
+---+   +-------------+                             |
| C |<--| Data Access |<--------------------------+ |
| o |   +-------------+                           | |
| m |         |                                   | |
| m |         |                                   | |
| o |         v                                   | v
| n |   +-------------+   +----------------+   +-----+
|   |<--| Model       +<--| Cross class    |<--| GUI |
|   |   +-------------+   | business rules |   |     |
|   |                     |                |   |     |
|   |<--------------------|                |   |     |
|   |                     +----------------+   |     |
|   |                                          |     |
|   |<-----------------------------------------|     |
+---+                                          +-----+
2 голосов
/ 01 апреля 2011

Тот факт, что ваш INotify интерфейс находится на уровне модели, не означает, что все конкретные реализации должны быть там.Это ДОЛЖЕН быть интерфейс - целью интерфейса является достижение абстракции, а не базовый класс, целью базового класса является достижение общей функциональности.Поэтому, если у вас есть свойство или параметр этого типа на любом из ваших слоев, оно должно быть объявлено как INotify.В вашем BAL (вы имели в виду BLL, уровень бизнес-логики?) Вы должны выбрать конкретный тип, который будет использоваться для этих экземпляров INotify.В зависимости от того, насколько сложны ваши уведомления, вы можете определить конкретную реализацию в вашем BLL и заставить ее использовать вспомогательный класс в DAL для фактического выполнения вызова вашего sproc, или вы можете определить его как класс непосредственно в вашем DAL, так как онвзаимодействует с базой данных;это действительно суждение, основанное на том, за что отвечает класс;в любом случае он должен быть доступен на верхнем уровне.

Кто-нибудь еще имел дело с бизнес-объектом, целью которого является взаимодействие с базой данных, и как вы расположите это в своей N-уровневой архитектуре?

Как структурированы ваши проекты, логические обязанности каждого слоя звучат так:

Общее: Методы общих утилит без каких-либо зависимостейостальное в проекте
Модель: Определяет структуру объектов в системе, также называемых DTO или объектами передачи данных, что означает, что они могут передаваться между слоями.Все, что они делают, это хранят ваши данные и выполняют базовую проверку.
DAL: Отвечает за создание экземпляров классов на уровне модели и настройку свойств на основе значений, хранящихся в репозитории, например в базе данных.Также отвечает за отслеживание изменений в сущностях модели и сохранение (сохранение) этих изменений обратно в хранилище.
BAL / BLL: Использует классы, определенные в других слоях, для достижения чего-то полезного, и проверяет, чтобизнес-требования соблюдаются.

Вы можете достичь этого с помощью различных технологий, и даже при использовании одной и той же технологии ваша точная реализация будет зависеть от вашей работы.Нечто подобное Linq2Sql, или Entity Framework из коробки, размыло бы границы между вашей моделью и вашим DAL;они хотят определить оба в одном проекте.Тем не менее, Entity Framework является более гибким, и с некоторой работой вы можете разделить определение модели объекта и «контекст» (чтобы использовать термин Entity Framework), который в конечном счете отвечает за компонент DAL, на отдельные проекты.Вы можете редактировать шаблоны T4 или находить в сети, которые будут генерировать определения классов сущностей и классов из модели сущностей для поддержки шаблонов проектирования «Репозиторий» и «Единица работы», где вы никогда не ссылаетесь непосредственно на контекст сущности, а вместо этого применяете интерфейс IRepository, что делает ваш код более тестируемым.Я никогда лично не работал с NHibernate, но я понимаю, что он способен делать то же самое (и, возможно, в настоящее время может делать лучшую работу).

1 голос
/ 01 апреля 2011

Вы можете использовать свои объекты данных в своем проекте, если они являются POCO.В противном случае я бы создал отдельные модели, как вы сделали.Но держите их в отдельной сборке (не в проекте DataAccess)

imho люди злоупотребляют слоями.Большинству приложений не нужно много слоев.У моего текущего клиента была архитектура, подобная вашей, для всех их приложений.Проблема заключалась в том, что только уровень доступа к данным и уровень представления содержали логику, а все остальные уровни просто брали данные с нижнего уровня, преобразовывали их и отправляли на уровень выше.

Первое, что яОн сказал им удалить все слои и вместо этого использовать что-то вроде этого (требуется контейнер IoC):

  • Core (Содержит бизнес-правила и доступ к данным через orm)
  • Спецификация (Шаблон отдельного интерфейса. Содержит сервисные интерфейсы и модели)
  • Пользовательский интерфейс (может быть веб-сервис, winforms, webapp)

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

Вы уже используете ORM и думали ли вы об использовании блока проверки (FluentValidation или DataAnnotations) для проверки?Упрощает проверку ваших моделей во всех слоях.

1 голос
/ 01 апреля 2011

Если ваши классы Model являются вашими DTO (то, что некоторые могут назвать структурами данных или типами данных), они должны (вероятно) лежать «между» вашими другими слоями и быть известными всем им.

На основечто вы говорите, у вас может быть класс MessageProcessing, который находится в BAL и получает сообщения от других частей BAL или DAL, а затем уведомляет любого, кто слушает (пользовательский интерфейс или другие заинтересованные члены BAL).

0 голосов
/ 01 апреля 2011

Спасибо всем за ваш вклад, здесь есть несколько идей относительно улучшений, которые я планирую реализовать, хотя ни одна из них не отвечает непосредственно на вопрос, который я задавал.

Я отправил это к программистам, где, как мне кажется, этот вопрос действительно может принадлежать, и получил несколько полезных идей. Если вам интересно, тема здесь: Тема для программистов по этому вопросу . По общему признанию, я добавил «подсказку» внедрения зависимости, основанную на моем собственном исследовании, когда я отправил туда, таким образом, проблема, возможно, была более ясной.

Это большое и полезное сообщество, в котором я так горжусь, что принимаю участие.

0 голосов
/ 01 апреля 2011

Классы, которые используются во многих слоях, вызывают у меня беспокойство.

Особенно, когда они также связаны с моделью данных / базой / слоем.

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

Тем не менее, поддержание кода трансформации (от слоя к слою) тоже не очень весело, но в целом это меньше работы.

Промежуточным решением может быть использование интерфейсов / ролей: определите для каждого уровня интерфейс / роль, который должен играть объект, и используйте этот интерфейс для передачи на уровень. (Общий) класс должен затем реализовать роль (или многие из них). Это обеспечит более слабосвязанную систему.

Я многое узнал из этой аккуратной лекции о DCI (Данные, Сотрудничество и Взаимодействия)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...