Вопрос по архитектуре
От: Jeremy Россия  
Дата: 25.09.07 07:54
Оценка:
Есть такой простейший пример. Пусть предметная область состоит из двух сущностей — Организация и Департамент, связанных между собой отношением "один-ко-многим" (организация состоит из нескольких департаментов). Допустим нам надо реализовать сервисный метод, возвращающий количество департаментов для заданной организации.

Как это сделано сейчас:

Все обьекты предметной области описывается двумя классами (для примера возьмем Organization)

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

    class OrganizationData
    {
        public int Id;
        public int Name;
    }


2. Бизнес-обьект. Класс предметной области, описывающий структуру и поведение обьекта в доменной области. Этим классом можно оперировать только внутри сервисного контекста (секьюрити, коннект к БД, транзакции).


    class Organization
    {
        public Organization(int id)
        {
            this.id = id;
        }

        public Organization(OrganizationData data)
            : this(data.Id)
        {
            this.data = data;
        }

        public OrganizationData Data
        {
            get
            {
                if(data == null)
                {
                    // Load data from db here...
                    data = mapper.GetOrganization(id);
                }
                return data;
            }
        }

        public DepartmentCollection Departments
        {
            get
            {
                if(departments == null)
                {
                    // Load departments from db here
                    departments = mapper.GetDepartments(id);
                }
                return departments;
            }
        }

        public void Delete()
        {
            mapper.Delete(id);
        }

        public void Create()
        {
            mapper.Create(Data);
        }

        private int id;

        private OrganizationData data;

        private DepartmentCollection departments;

        private IDbMapper mapper;
    }


Обращаю внимание, что бизнес обьект общается с БД посредством отдельного интерфейса IDbMapper, реализация которого находится в слое доступа к данным. Также важно отметить, что в реализации применяется принцип LazyLoad, то есть например для того чтобы удалить Организацию нам не придется загружать данные о ней, достаточно только знать его идентификатор:


        new Organization(5).Delete();


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


    class DepartmentCollection : Collection<Department>{}


Таким образом, итоговый код получающий количестиво департаментов будет выглядеть примерно так:

    return new Organization(5).Departments.Count;


А теперь вопросы:

1. Критика?
2. Что смущает меня в этой архитектуре, так это наличие связи типа Organization -> IDbMapper, пусть и максимально ослабленной посредством интерфейса. В идеале хотелось бы чтобы обьект предметной области Organization не имел методов типа Delete и вообще не занимался (даже опосредованно) общением с БД. Один из вариантов (описанных у того же Фаулера например) это вынести все мапперы уровнем выше (в сервисный метод), то есть иметь нечто вроде:


    IDbMapper mapper = ...
    return mapper.GetDepartments(5).Count;


Но это, по-моему, еще хуже, поскольку стремительно приближает решение к структурному программированию и гораздо менее наглядно, особенно если логика сервисного метода потребует чего-то большего, нежели просто вычисления количества элементов в коллекции. Что делать?
Re: Вопрос по архитектуре
От: Sinclair Россия https://github.com/evilguest/
Дата: 25.09.07 10:31
Оценка:
Здравствуйте, Jeremy, Вы писали:

J>1. Критика?

Да.
J>2. Что смущает меня в этой архитектуре, так это наличие связи типа Organization -> IDbMapper, пусть и максимально ослабленной посредством интерфейса.
И правильно.

J>

J>    IDbMapper mapper = ...
J>    return mapper.GetDepartments(5).Count;
J>

В данном случае опасность в том, что в типичных сценариях все работает независимо. Т.е. маппер не имеет никакой возможности понять, что от департаментов нужно только count. В плохом случае такой код подымет на клиента(!) все десять тысяч детальных записей о департаментах, а потом посчитает.

Полухороший вариант:
int deptCount =IOrganizationManager.GetDepartmentCount(5);
В хорошем варианте IDbMapper.GetDepartments(int organizationID) возвращает IQueriable<Department>.
Поэтому когда ты пишешь
    IDbMapper mapper = ...
    return mapper.GetDepartments(5).Count;

на сервере выполняется таки
select count(ID) from department where organizationId=5


J>Но это, по-моему, еще хуже, поскольку стремительно приближает решение к структурному программированию и гораздо менее наглядно, особенно если логика сервисного метода потребует чего-то большего, нежели просто вычисления количества элементов в коллекции. Что делать?
Это как раз более наглядно: у тебя есть метод с нормальным именем и документацией, а не спагетти из кода, который делает что-то большое и сложное с объектами предметной области. Ну вот как K&R страшно гордились своим while(*p++ = *q++); , а Степанов понимал, что гораздо правильнее писать p = q. Это куда как нагляднее.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[2]: Вопрос по архитектуре
От: Jeremy Россия  
Дата: 25.09.07 13:43
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>В данном случае опасность в том, что в типичных сценариях все работает независимо. Т.е. маппер не имеет никакой возможности понять, что от департаментов нужно только count. В плохом случае такой код подымет на клиента(!) все десять тысяч детальных записей о департаментах, а потом посчитает.


Здесь я бы поспорил. Кмк, в данном случае, это все же оптимизация ради оптимизации и в ущерб читаемости и прозрачности кода. То есть создавать отдельный метод считающий только count (select count(*)) в дополнение к методу читающему всю коллекцию (select *) нужно только в случае если увидим в этом меcте серьезные проблемы с производительностью. Впрочем к сути рассматриваемого вопроса это напрямую не относится.

S>Это как раз более наглядно: у тебя есть метод с нормальным именем и документацией, а не спагетти из кода, который делает что-то большое и сложное с объектами предметной области.


Вот этот момент не понятен. Все-таки в доменной области хотелось бы работать именно с обьектами доменной области. Понятно, что свойство "количество департаментов" просто по логике принадлежит либо организации (Organization.DepartmentCount), либо коллекции департаментов (как в примере выше — Organization.Departments.Count). Все прочие варианты кмк усложняют восприятие. В том числе и

return mapper.GetDepartments(5).Count;

Здесь как раз и получается спагетти из обьектов предметной области и неких "искусственных" сущностей типа мапперов, которые инкапсулируют не логику работы бизнес-модели, а внутренние механизмы реализации вспомогательных операций типа считывания данных из БД.

В общем, пока для меня вопрос остается открытым. Но спасибо за ваше мнение.
Re: Вопрос по архитектуре
От: Кирилл Лебедев Россия http://askofen.blogspot.com/
Дата: 25.09.07 14:55
Оценка: 58 (2) +1
Здравствуйте, Jeremy, Вы писали:

J>1. Критика?

J>2. Что смущает меня в этой архитектуре, так это наличие связи типа Organization -> IDbMapper, пусть и максимально ослабленной посредством интерфейса.

На мой взгляд, причина Вашей задачи заключается в объектном подходе к проектированию. Не критикуя сам подход в общем случае, рассмотрим, к каким проблемам он приводит в контексте Вашей задачи. Вы выделяете две сущности: Организация и Департамент. И непременно хотите узнавать количество департаментов в организации таким образом:

int Count = Organization.Departaments.Count;


Т.е. заранее, не зная сути задачи (или, по крайней мере, не описывая ее), Вы предполагаете, что архитектура программы будет один в один соответствовать иерархической схеме организации: есть Организация, и она состоит из Департаментов. Иными словами, объектная модель Вашего приложения будет соответствовать объектной модели предметной области.

Между тем, это неправильно. В самом деле, для описания одной и той же организации могут использоваться совершенно разные модели. Структурная (объектная) модель организации описывает отношение включения. Пользуясь этой моделью, можно узнать, какие отделы содержит организация, и какие сотрудники относятся к тому или иному отделу. Функциональная модель организации описывает функции самой организации и распределение их по отделам. А внутри отделов — по сотрудникам. Кроме того, существует еще и масса других моделей: модель управления, модель финансовых потоков, модель для ведения бухгалтерии и т.д. Т.е. в зависимости от задач, которые надо решить, могут использоваться разные модели.

Поэтому начинать проектирование лучше не с построения объектной модели (т.к. это всего лишь одна модель из множества доступных), а с составления перечня функций высокого уровня, которые должна выполнять система. Посудите сами, ведь никто же не создает программы просто так (без цели). Программа должна что-то делать, выполнять какие-то обязанности. Какие? Из Вашего сообщения это неясно. Ясно только то, что программа должна возвращать количество департаментов в организации. Но для реализации этой функции совсем не нужна объектная модель. Можно обойтись простым запросом к базе данных.

Означает ли это, что вообще не нужно никакой объектной модели? Конечно, нет. Просто ее не нужно брать из "реального мира". Лучше все-таки сначала определить обязанности программы, а затем — под эти обязанности — разрабатывать объектную модель.
С уважением,
Кирилл Лебедев
Software Design blog — http://askofen.blogspot.ru/
Re[2]: Вопрос по архитектуре
От: Jeremy Россия  
Дата: 25.09.07 18:35
Оценка:
Здравствуйте, Кирилл Лебедев, Вы писали:

КЛ>...<skipped>...


Не буду с вами спорить, потому что я с вашими словами полностью согласен. Но вы говорите немного не о том, о чем мне хотелось бы услышать. Возможно, я не очень удачно сформулировал вопрос...

Пример с Организацией и Департаментами приведен чисто ради иллюстрации проблемы (хотя этот псевдокод и выдран с некоторыми сокращениями из реальной программы). Вопрос который я хотел задать — как лучше организовать взаимоотношение между обьектами предметной области и обьектами DAL (Data Access Layer)? В своем сообщении я писал чем меня не устраивает нынешний вариант. Однако альтернатива в виде выноса мапперов DAL на ту же ступень иерархии, где находятся сами бизнес-обьекты мне тоже не по душе (почему — писал выше в ветке). И что получается? То ли хороших вариантов просто нет, то ли я их не вижу, то ли я чего-то не понимаю
Re[3]: Вопрос по архитектуре
От: Sinclair Россия https://github.com/evilguest/
Дата: 26.09.07 02:50
Оценка:
Здравствуйте, Jeremy, Вы писали:

J>Здесь я бы поспорил. Кмк, в данном случае, это все же оптимизация ради оптимизации и в ущерб читаемости и прозрачности кода. То есть создавать отдельный метод считающий только count (select count(*)) в дополнение к методу читающему всю коллекцию (select *) нужно только в случае если увидим в этом меcте серьезные проблемы с производительностью. Впрочем к сути рассматриваемого вопроса это напрямую не относится.

К сожалению, если этого не делать, то в будущем никакой оптимизации не получится. В начале можно сделать этот метод тривиальным, т.е. просто вернуть GetDepartments.Count. А уж потом заменить реализацию на более оптимальную. Но если сразу не предусмотреть такой метод, то введение его в последующем ничем не поможет: все клиенты по прежнему будут сканировать весь список. Посмотри на реализацию SiteMapProvider в .Net.

J>Вот этот момент не понятен. Все-таки в доменной области хотелось бы работать именно с обьектами доменной области. Понятно, что свойство "количество департаментов" просто по логике принадлежит либо организации (Organization.DepartmentCount), либо коллекции департаментов (как в примере выше — Organization.Departments.Count). Все прочие варианты кмк усложняют восприятие.

Ну, это вообще говоря вопрос некоторого произвола — как гейзенберговское vs шредингеровское представления операторов физ.величин. Что первично — данные или сервисы?
J>Здесь как раз и получается спагетти из обьектов предметной области и неких "искусственных" сущностей типа мапперов, которые инкапсулируют не логику работы бизнес-модели, а внутренние механизмы реализации вспомогательных операций типа считывания данных из БД.
Гм. Здесь просто неудачно выбран термин "маппер". Речь не о маппере, а о некоем компоненте системы, который хранит данные об отделах и организациях и предоставляет эти данные другим компонентам. Это не искусственная сущность, а вполне объективная реальность, полученная путем анализа предметной области.
Кирилл Лебедев рядом это описал прямо-таки замечательным образом.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[3]: Вопрос по архитектуре
От: Кирилл Лебедев Россия http://askofen.blogspot.com/
Дата: 26.09.07 08:23
Оценка: 1 (1) +1
Здравствуйте, Jeremy, Вы писали:

J>Вопрос который я хотел задать — как лучше организовать взаимоотношение между обьектами предметной области и обьектами DAL (Data Access Layer)? В своем сообщении я писал чем меня не устраивает нынешний вариант. Однако альтернатива в виде выноса мапперов DAL на ту же ступень иерархии, где находятся сами бизнес-обьекты мне тоже не по душе (почему — писал выше в ветке). И что получается? То ли хороших вариантов просто нет, то ли я их не вижу, то ли я чего-то не понимаю


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

  1. Получить количество отделов для заданной организации.
  2. Получить реквизиты организации.
  3. Получить количество сотрудников в организации.
  4. Получить количество сотрудников в заданном отделе заданной организации.
  5. И т.д. Продолжите это список.

О том, как эти запросы будут реализованы, пока думать не надо. Составьте для начала список. Как только он будет готов, его можно будет использовать (практически один в один!) в виде интерфейса класса. Только после этого можно будет подумать и о реализации сущности. Каждый ее метод можно будет оформить в виде отдельного SQL-запроса. А можно будет сделать еще и кэш, и сохранять уже полученные данные. В последнем случае реализация функции сначала должна будет поискать "ответ" в кэше, а уж если ответ не найден, то посылать запрос к базе данных. В общем, вариантов реализации масса. Только сначала Вам все-таки нужно определиться с интерфейсом.
С уважением,
Кирилл Лебедев
Software Design blog — http://askofen.blogspot.ru/
Re[4]: Вопрос по архитектуре
От: Jeremy Россия  
Дата: 26.09.07 14:22
Оценка:
Здравствуйте, Кирилл Лебедев, Вы писали:

КЛ>Понимаете, описанная Вами проблема возникла только потому, что Вы решили завести класс Организация и класс Департамент.


Да не придирайтесь вы к классам Организации и Департамента — это всего лишь пример.

КЛ>Стоит Вам отказаться от такой модели, то и проблема исчезнет.


Пока я не вижу каким образом проблема исчезнет. Именно это я и пытаюсь выяснить.

КЛ>Попробуйте подойти к проектированию с другой точки зрения. Пусть у Вас в программе имеется некая сущность, которая уполномочена отвечать на запросы об организациях и департаментах. С какими запросами Вы будете к ней обращаться? ...<skipped>... О том, как эти запросы будут реализованы, пока думать не надо. Каждый ее метод можно будет оформить в виде отдельного SQL-запроса.


Это именно то, чего я хочу избежать. Хочется именно что отделить логику от SQL-запросов и т.п. То, что вы предлагаете — это просто вместо парадигмы "класс = сущность предметной области" использовать парадигму "класс = сервис предметной области". Как это поможет в деле отделения бизнес-логики от вспомогательных методов "считать/записать что-то в БД" (чего я хочу добиться) я не понимаю.
Re[2]: Вопрос по архитектуре
От: UrryMcA Россия http://www.UrryMcA.com
Дата: 26.09.07 14:34
Оценка:
Чего там насчет Деметры??
Re: Вопрос по архитектуре
От: Суслик Россия http://www.vkkb.ru
Дата: 26.09.07 21:41
Оценка:
Привет, Jeremy.

Мое мнение, что твой код ничем не хуже и не лучше других в этой области. Т.е. с точки зрения техники я тебе ничего не могу сказать. Скажу с общечеловеческой.

В свое время я безумно увлекался Фаулером и пр. ООП-мастерами (арх. корп. прил. Фаулера одна из немногих прочитанных мною книг от начала до конца). Знатоками ООП (или тем, кто себя причиляет к ним) озвучивается мнение, что можно классно спроектировать систему и пожинать с ее архитектуры плоды. Я был тоже уверен, что это так. Однако сейчас, будучи вед. разработчиком и автором архитектуры управленческой системы с элементами бух. учета под конкретного заказчика, могу сказать — что как ни напиши систему, а:
1) либо будешь постоянно дописывать ядро, в котором содержится та самая ООП-красотища с целью не потреять эту красотищу.
2) либо будешь выкручиваться без изменения ядра, перманентно уродуя идею. Впрочем ты можешь эту идею успешно уродовать на протяжении многих (может и десятков) лет и оставаться системой, которая предоставляет конечному пользователю требуемый ему функционал.

МОРАЛЬ. Я хочу сказать, что для того, чтобы научиться правильно выбирать ООП-решения не обязательно это делать правильно с точки зрения высшего разума. Нужно научиться решать с точки зрения ООП конкретные задачи. А от того, знает ли маппер о классе BL или наоборот — зависит намного меньше, чем от отсутствия критической функции, которую ты пропустил при анализе ТЗ и обработке требований.

PS Я, конечно, понимаю, что ты задаешь вопрос не с целью решить наилучшим образом конкретную задачу, а ты хочешь использовать топик в образовательном качестве. И я не буду ничего советовать тебе как поступать. Но могу сказать, что вот сейчас я хорошо понимаю, что методики разработки значат существенно больше архитектуры. И уж ООП точно не панацея. Идти нужно от задачи :)
Re[5]: Вопрос по архитектуре
От: Кирилл Лебедев Россия http://askofen.blogspot.com/
Дата: 27.09.07 05:19
Оценка:
Здравствуйте, Jeremy, Вы писали:

J>Это именно то, чего я хочу избежать. Хочется именно что отделить логику от SQL-запросов и т.п. То, что вы предлагаете — это просто вместо парадигмы "класс = сущность предметной области" использовать парадигму "класс = сервис предметной области".

Именно так.

J>Как это поможет в деле отделения бизнес-логики от вспомогательных методов "считать/записать что-то в БД" (чего я хочу добиться) я не понимаю.

Есть класс, который получает и сохраняет данные. Откуда и куда — совершенно не важно. А есть другой класс, который на основе полученных данных проводит какие-то вычисления. Вот и получается отделение бизнес-логики от считывания и записи.
С уважением,
Кирилл Лебедев
Software Design blog — http://askofen.blogspot.ru/
Re[2]: Вопрос по архитектуре
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 09.10.07 16:07
Оценка:
Здравствуйте, Суслик, Вы писали:

С>1) либо будешь постоянно дописывать ядро, в котором содержится та самая ООП-красотища с целью не потреять эту красотищу.

С>2) либо будешь выкручиваться без изменения ядра, перманентно уродуя идею. Впрочем ты можешь эту идею успешно уродовать на протяжении многих (может и десятков) лет и оставаться системой, которая предоставляет конечному пользователю требуемый ему функционал.
3) Не искать серебрянной пули и дописывать и ядро, если это даст больший эффект, и прикладной код, если это эффективнее.

С>МОРАЛЬ. Я хочу сказать, что для того, чтобы научиться правильно выбирать ООП-решения не обязательно это делать правильно с точки зрения высшего разума.


А Фаулер что то писал про высший разум?
... << RSDN@Home 1.2.0 alpha rev. 716>>
AVK Blog
Re[2]: Вопрос по архитектуре
От: Aviator  
Дата: 09.10.07 19:03
Оценка:
Здравствуйте, Суслик, Вы писали:


С>1) либо будешь постоянно дописывать ядро, в котором содержится та самая ООП-красотища с целью не потреять эту красотищу.

С>2) либо будешь выкручиваться без изменения ядра, перманентно уродуя идею. Впрочем ты можешь эту идею успешно уродовать на протяжении многих (может и десятков) лет и оставаться системой, которая предоставляет конечному пользователю требуемый ему функционал.

Знатоки ООП ваще то рекомендуют постоянно рефакторить ( = переделывать ) существующий код, который имеет тенденцию уродоваться при появлении новых требований. Так что невнимательно Вы видать читали мастеров ООП .
Re[3]: Вопрос по архитектуре
От: Суслик Россия http://www.vkkb.ru
Дата: 09.10.07 19:45
Оценка: +1
Здравствуйте, Aviator, Вы писали:

A>Знатоки ООП ваще то рекомендуют постоянно рефакторить ( = переделывать ) существующий код, который имеет тенденцию уродоваться при появлении новых требований. Так что невнимательно Вы видать читали мастеров ООП :shuffle:.


А не создается ли у Вас ощущение, что это все рекомендуется дабы оправдать непомерную сложность, которую привносит ООП и его не совсем разумное использование. Полагаю, что нет :)

Однако, можно жить и без ООП. Вообще. При этом делать потрясающие вещи, аналогов которых нет.

Я хотел бы добавить все же в защиту ООП. Конечно, я использую ООП направо и налево. Без него практичеки никуда, ибо библиотеки, используемые мною, написаны с использованием ООП. При этом я *лично* не считаю ООП чем-то таким, чего нужно избегать. Это хороший интсрумент.

Считаю же я, что ООП это *инструмент* и ничего больше, никакой религии в нем нет. Фаулер, ГоФ и пр. это лишь инструмент для лучшего понимания ООП. Я, конечно, и Фаулера прочел всего, ГоФ и другие родственные источники информации в сети (на том же фаулеровском сайте). Кроме классики (ГоФ и приложения) другие ООП подходы для меня создают (иногда!) впечатления упражнений для ума, но никак не решения, оптимальные по сочетанию цена/качество.

Поэтому отвечая на вопрос автора могу сказать — пофиг кто о ком знает, главное не забудь продумать то, без чего порядок следования уровней не будет иметь ровно никакого влияния на конечный результат, ибо проект будет мертв.

Главный принцип ООП, который я для себя вывел — выбери архитектуру, придумай ей идеологическое основание, запомни его и РЕШИ конкретную задачу. Если кто-то будет журить за архитектуру считай, что именно она принесла тебе успех в решении задачи.

ЗЫ Понимаю, что могу звучать парадоксально. К сожалению я не писатель, чтобы хорошо объяснить свою позицию. Однако она есть результат многолетних размышений: ООП переоценен в своей значимости!
Re[4]: Вопрос по архитектуре
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 10.10.07 14:22
Оценка:
Здравствуйте, Суслик, Вы писали:

С>Считаю же я, что ООП это *инструмент* и ничего больше, никакой религии в нем нет.


...

С>ЗЫ Понимаю, что могу звучать парадоксально. К сожалению я не писатель, чтобы хорошо объяснить свою позицию. Однако она есть результат многолетних размышений: ООП переоценен в своей значимости!


Тебе не кажется, что ты споришь сам с собой?
... << RSDN@Home 1.2.0 alpha rev. 716>>
AVK Blog
Re[5]: Вопрос по архитектуре
От: Суслик Россия http://www.vkkb.ru
Дата: 10.10.07 15:10
Оценка:
Здравствуйте, AndrewVK, Вы писали:

AVK>Тебе не кажется, что ты споришь сам с собой?


Да не спорю я ни с кем. Просто когда вижу, как люди пытаются решить не самые значительные вопросы по принципу "как более правильно", не могу не удержаться от того, чтобы не дать на это комментарий, возможно, излишне эмоциональный
Re[6]: Вопрос по архитектуре
От: Sinclair Россия https://github.com/evilguest/
Дата: 15.10.07 10:10
Оценка: :)
Здравствуйте, Суслик, Вы писали:
С>Да не спорю я ни с кем. Просто когда вижу, как люди пытаются решить не самые значительные вопросы по принципу "как более правильно", не могу не удержаться от того, чтобы не дать на это комментарий, возможно, излишне эмоциональный

- Батюшка, а вы когда спите, бороду под одеяло, али поверх кладете?
— Даже и не знаю...
...
— Отрок, замучил ты меня! Уснуть теперь не могу: не знаю, как бороду положить.

... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.