Представление данных в PL в свете Rich, Anemic и пр.
От: stomsky Россия  
Дата: 19.01.11 18:32
Оценка:
Привет всем!
Вопрос у меня прежде всего к gandjustas, IB и пр. сторонникам анемичной модели с навигационным доступом (под этими терминами понимаю это
Автор: IB
Дата: 29.05.09
). Хотя, буду рад услышать доводы всех, кто захочет ответить.
Когда речь идет о стыке бизнес логики (BLL) с хранилищем данных (включая все промежуточные слои типа Репозитория, DAL и т.п.), то тут я скорее на стороне упомянутых выше участников форума.
Но когда речь идет о стыке представления (PL) и бизнес логики (BLL), то тут меня охватывают сомнения: модель работы с данными более естественна для GUI и при этом совместима с Transaction Script?
Как правильнее организовать представление составных сущностей на клиенте, сервере и передачу их с BLL на PL?

Уточню реализацию: клиент — WinForms в перспективой перехода на WPF, сервер — WinService на C#, транспорт — WCF.

Поясню на модельном примере.
Допустим есть сущности "Накладная" и "Поставщик". Накладная имеет ссылку на Поставщика.
-- работаю с MS SQL, поэтому скрипт смахивает на Transact SQL
CREATE TABLE [Поставщик]
(
  [ПоставщикID]   INT                NOT NULL,
  [Наименование]  VARCHAR(100)       DEFAULT '',
  [ИНН]           VARCHAR(15)        DEFAULT '',
  ...
  CONSTRAINT     [PK_ПоставщикID]   PRAIMARY KEY ([ПоставщикID]),
)
CREATE TABLE [Накладная]
(
  [НакладнаяID]  INT                NOT NULL,
  [Номер]        VARCHAR(10)        DEFAULT '',
  [Дата]         DATETIME           NOT NULL,
  [ПоставщикID]  INT                NOT NULL,
   ...
  CONSTRAINT     [PK_НакладнаяID]   PRAIMARY KEY ([НакладнаяID])
  CONSTRAINT     [FK_Накладная_Поставщик] FOREIGN KEY ([ПоставщикID])
                 REFERENCES [Поставщик] ([ПоставщикID])
)


На клиенте (WinForms с его "коробочным" DataBinding'ом) для редактирования сущности "Накладная" нужно иметь возможность не только отредактировать номер, дату и т.п. "скалярные" поля накладной, но и выбрать в отдельном диалоге поставщика. При этом, допустим, на форме о поставщике мы должны видеть его ИНН и наименование.
Для естественной работы в WinForm'овским DataBinding'ом накладную надо представить как один класс со свойствами, отображающими все характеристики как самой накладной, так поставщика, на которого она ссылается.
По-моему, что класс накладной, экземпляр которого будет привязана к контролам формы, должна являться оберткой для класса WCF-ного DataContract'а, и поддерживать нижеприведенный интерфейс:
interface IНакладная_Editable : INotifyPropertyChanged, IEditableObject
{
  // скалярные свойства накладной
  int        НакладнаяID { get; }
  string     Номер { get; set; }
  DateTime   Дата { get; set; }
  ...
  // свойства поставщика
  IПоставщик Поставщик { get; set; }
  int        ПоставщикID { get; }
  string     ИНН_Поставщика { get; }
  string     Наименование_Поставщика { get; }
  // всевозможные INotifyPropertyChanged, IEditableObject
  ....
}

И тут вопрос: как реализовать класс самого DataContract'а (тот самый, который будет завернут в класс с интерфейсом IНакладная_Editable)?
Так:
class Накладная
{
  public int       НакладнаяID { get; private set; }
  public string    Номер { get; set; }
  public DateTime  Дата { get; set; }
  public int       ПоставщикID { get; private set; }
  public string    INN { get; set; }
  public string    Name { get; set; }
}

Или так:
class Поставщик
{
  public int     ПоставщикID { get; private set; }
  public string  INN { get; set; }
  public string  Name { get; set; }
}
class Накладная
{
  public int       НакладнаяID { get; private set; }
  public string    Номер { get; set; }
  public DateTime  Дата { get; set; }
  Поставщик        Поставщик { get; set; }
}

В первом случае не надо никаких преобразований структуры данных при передаче из BLL на PL и обратно (считаем, что на BLL реализован Transaction Script), но как тогда быть при смене поставщика в форме? Надо ведь сменить значения во всех свойствах, относящихся к поставщику...
Во втором случае смена поставщика пользователем в PL реализуется легко и красиво, но нужен некий Mapper между представлением данных на сервере приложения и на клиенте. Т.е. преобразователь [Transaction Script] в [Anemic Domain Model] и обратно. Что, по-моему, тоже извращение какое-то...
Красота — наивысшая степень целесообразности. (c) И. Ефремов
Re: Представление данных в PL в свете Rich, Anemic и пр.
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 19.01.11 20:05
Оценка:
Здравствуйте, stomsky, Вы писали:

S>Привет всем!

S>Вопрос у меня прежде всего к gandjustas, IB и пр. сторонникам анемичной модели с навигационным доступом (под этими терминами понимаю это
Автор: IB
Дата: 29.05.09
). Хотя, буду рад услышать доводы всех, кто захочет ответить.

S>Когда речь идет о стыке бизнес логики (BLL) с хранилищем данных (включая все промежуточные слои типа Репозитория, DAL и т.п.), то тут я скорее на стороне упомянутых выше участников форума.
S>Но когда речь идет о стыке представления (PL) и бизнес логики (BLL), то тут меня охватывают сомнения: модель работы с данными более естественна для GUI и при этом совместима с Transaction Script?
S>Как правильнее организовать представление составных сущностей на клиенте, сервере и передачу их с BLL на PL?

S>Уточню реализацию: клиент — WinForms в перспективой перехода на WPF, сервер — WinService на C#, транспорт — WCF.

Совершенно не понял причем тут anemic\rich\ещечтото.

Когда работаешь с данными то нужны 4 операции: чтение, добавление, изменение, удаление. Для выполнения этих операций используются запросы. Не важно будет у тебя SQL Server или WCF-сервис.
Но, для целей UI удобен databinding, поэтому нужно данные как-то материализовывать в коллекции, которые потом будут байдиться. Кроме того нужен change-tracking, чтобы потом изменения в данных можно было превратить в запросы на сервер. Также в сложном UI может понадобится иметь не список объектов, а некоторую более сложную коллекцию, содержащую объекты разных типов.

То есть для UI c databnding тебе нужны: данные + change-tracking + как-то хранить множество объектов разных типов.

Из того что есть
1)Self tracking entities для EF — данные передаются графом связных объектов, нельзя просто предать несколько графов нельзя, changeset получить сложно
2)DataSet — строкотипизировано, неудобно
3)WCF Data Services — почти то что нужно, немного геморройно с навигационными свойствами работать
4)RIA Services — не сильно разбирался как оно в коде работает

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

Далее binding:
1)Для WinForms надо использовать что-то вроде

BindingSource bs1 = new BindingSource(Persons, null);
BindingSource bs2 = new BindingSource(bs1, "Parents");

// bind to grid
dataGridView1.DataSource = bs1;
dataGridView2.DataSource = bs2;


IEditableObject лучше не юзать ибо надо отслеживать изменения нескольких объектов.

2)В WPF все проще — в байндингах поддерживается навигация, INotifyPropertyChanged не сильно нужен, потому что объекты будут правиться в интерфейсе.

3)В вебе не будет байндинга. В вебе проще преобразовывать входящий запрос в запрос к хранилищу.
Re[2]: Представление данных в PL в свете Rich, Anemic и пр.
От: stomsky Россия  
Дата: 20.01.11 07:37
Оценка:
Здравствуйте, gandjustas, Вы писали:
G>Совершенно не понял причем тут anemic\rich\ещечтото.
Похоже я слишком широко задал тему вопроса.
Конечно Anemic/Rich (в смысле где хранить логику работы с данными) тут ни при чем. Вернее этот вопрос я для себя уже решил.

G>Из того что есть

G>1)Self tracking entities для EF — данные передаются графом связных объектов, нельзя просто предать несколько графов нельзя, changeset получить сложно
G>2)DataSet — строкотипизировано, неудобно
G>3)WCF Data Services — почти то что нужно, немного геморройно с навигационными свойствами работать
G>4)RIA Services — не сильно разбирался как оно в коде работает
G>Ну или можно самому написать, хотя я бы не рекомендовал.
Честно говоря, я склоняюсь именно к последнему. Использовать EF не хочется. Мне BLToolkit вообще понравился
Насчет DataSet'ов я их до сих пор использую. Знаю, что они "нехорошие", но пока потребности 2-хзвенок с десятком формочек они покрывают с лихвой. Я в основном сопровождением чужого занимаюсь. Свое писать нечасто приходится.

G>IEditableObject лучше не юзать ибо надо отслеживать изменения нескольких объектов.

Не очень понял о чем речь. IEditableObject обеспечивает возможность отмены несохраненных в объекте изменений. Ведет он себя, конечно, странновато в приложении к WinForm'ам, но при чем тут изменение нескольких объектов?

G>2) INotifyPropertyChanged не сильно нужен, потому что объекты будут правиться в интерфейсе.

Ну это как сказать... Я же не зря в своем примере про Поставщика упомянул.
Предполагается, что в диалоге редактирования Накладной будет среди прочего "кнопочка с тремя точками", по нажатию на которую будет открываться диалог выбора поставщиков. Из диалога выбора будем получать ID-шник выбранного поставщика и уже свойству
class Накладная
{
 ...
  public Поставщик Поставщик { get; set; }
 ...
}

будет присваиваться объект класса Поставщик, вытянутый из БД по этому ID-шнику (с теми свойствами).
(Ну естественно, в данном контексте "class Поставщик" это отдельный класс только с теми свойствами, которые нужны в диалоге редактирования Накладной, а не полное отображение таблицы "Поставщики" из БД).
Так вот, после присвоения свойству "Поставщик" другого объекта класса "Поставщик" придется изменить значения свойств
class Накладная
{
 ...
  public string ИНН_Поставщика { get; }
  public string Наименование_Поставщика { get; }
 ...
}

Чтобы эти изменения автоматически отразилось в диалоге редактирования.
Самый прямой способ это сделать, по-моему, — реализовать INotifyPropertyChanged и в сеттере свойства "Поставщик" иницировать события PropertyChanged для соответствующих свойств.
Или я что-то не так понимаю?
Красота — наивысшая степень целесообразности. (c) И. Ефремов
Re[3]: Представление данных в PL в свете Rich, Anemic и пр.
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 20.01.11 08:25
Оценка:
Здравствуйте, stomsky, Вы писали:

S>Здравствуйте, gandjustas, Вы писали:

G>>Совершенно не понял причем тут anemic\rich\ещечтото.
S>Похоже я слишком широко задал тему вопроса.
S>Конечно Anemic/Rich (в смысле где хранить логику работы с данными) тут ни при чем. Вернее этот вопрос я для себя уже решил.

G>>Из того что есть

G>>1)Self tracking entities для EF — данные передаются графом связных объектов, нельзя просто предать несколько графов нельзя, changeset получить сложно
G>>2)DataSet — строкотипизировано, неудобно
G>>3)WCF Data Services — почти то что нужно, немного геморройно с навигационными свойствами работать
G>>4)RIA Services — не сильно разбирался как оно в коде работает
G>>Ну или можно самому написать, хотя я бы не рекомендовал.
S>Честно говоря, я склоняюсь именно к последнему. Использовать EF не хочется. Мне BLToolkit вообще понравился
S>Насчет DataSet'ов я их до сих пор использую. Знаю, что они "нехорошие", но пока потребности 2-хзвенок с десятком формочек они покрывают с лихвой. Я в основном сопровождением чужого занимаюсь. Свое писать нечасто приходится.
Имхо лучше использовать Data Services.

G>>IEditableObject лучше не юзать ибо надо отслеживать изменения нескольких объектов.

S>Не очень понял о чем речь. IEditableObject обеспечивает возможность отмены несохраненных в объекте изменений. Ведет он себя, конечно, странновато в приложении к WinForm'ам, но при чем тут изменение нескольких объектов?
1)Одна отправка данных на сервер должна соотвествовать одной системной транзакции.
2)По EndEdit должно выполняться сохранение данных, то есть отправка данных на сервер.
В случае согласованного изменения нескольких объектов 1) и 2) начинают противоречить друг другу.

G>>2) INotifyPropertyChanged не сильно нужен, потому что объекты будут правиться в интерфейсе.

S>Ну это как сказать... Я же не зря в своем примере про Поставщика упомянул.
S>Предполагается, что в диалоге редактирования Накладной будет среди прочего "кнопочка с тремя точками", по нажатию на которую будет открываться диалог выбора поставщиков. Из диалога выбора будем получать ID-шник выбранного поставщика и уже свойству
S>
S>class Накладная
S>{
S> ...
S>  public Поставщик Поставщик { get; set; }
S> ...
S>}
S>

S>будет присваиваться объект класса Поставщик, вытянутый из БД по этому ID-шнику (с теми свойствами).
S>(Ну естественно, в данном контексте "class Поставщик" это отдельный класс только с теми свойствами, которые нужны в диалоге редактирования Накладной, а не полное отображение таблицы "Поставщики" из БД).
S>Так вот, после присвоения свойству "Поставщик" другого объекта класса "Поставщик" придется изменить значения свойств
S>
S>class Накладная
S>{
S> ...
S>  public string ИНН_Поставщика { get; }
S>  public string Наименование_Поставщика { get; }
S> ...
S>}
S>

S>Чтобы эти изменения автоматически отразилось в диалоге редактирования.

S>Самый прямой способ это сделать, по-моему, — реализовать INotifyPropertyChanged и в сеттере свойства "Поставщик" иницировать события PropertyChanged для соответствующих свойств.

S>Или я что-то не так понимаю?

С чего ты взял что "придется изменить значения свойств"? Можно просто байндинг еще раз запустить, чтобы он перечитал значения из объектов.
Но это уже на твое усмотрение.

ЗЫ. Если ты делаешь WS-* вебсервисы, которые имеют CRUD интерфейс для набора сущностей, то ты что-то делаешь что-то не так. В таком случае Data Services гораздо выгоднее.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.