Мучает меня последнее время вопрос... прям спать не могу . Решил поделиться с общественностью — что б тоже сон потеряла.
Итак, что такое многоуровневая архитектура? В моём понимании это такое построение приложения, при котором оно состоит из нескольких слоёв, причём слои верхнего уровня используют слои нижнего, но не наоборот. Классический пример:
Presentation(UI)->Business Logic(BL)->Data Access Layer(DAL)->Storage(DB)
Постулат такой: любые вызовы из нижлежащего слоя к вышестоящему недопустимы. За редким исключением (например callback-и) которых следует таки избегать.
Почему? Достоинство я вижу на самом деле только одно: уменьшается связность. А следствий может быть очень много, как то упрощение юнит-тестирования, повышение потенциальной масштабируемости вширь, и т.п.
В описаном примере думаю очевидным будет тот факт, что вызовы к UI из BL и тем более из DAL — признок дурно пахнущей архитектуры.
(Конечно из всех правил есть исключения, но речь не о них.)
Собственно это была приcказка. Сама же сказка такая:
Реализуем BL на основе Domain Model. Т.е. бизнес логика приложения содержится в классах, которые являются отражением предметной области. Классы содержат данные и логику работы с ними (инкапсуляция блин, кудаж без неё!).
Реализуем DAL на основе Data Mapper. Собственно слой предоставляющий интерфейс для работы с хранилищем на основе обектно-ориентированой модели.
И вот что не даёт мне спать по ночам: DAL связан с BL, поскольку выполняет создание экземпляров классов из Domain Model и их инициализацию данными из DB. Налицо вопиющее нарушение "слойности" архитектуры. Анархия. Разруха. Полный капец.
Читал Фаулера. Этот "нехороший человек" обошёл проблемму стороной упомянув что "однозначно хорошего решения не существует".
Собственно какие способы вижу я:
1. Отделение BL от данных и выделение отдельной группы объектов-data-контейнеров. Где же тогда инкапсуляция? Правильно — в Ж.
2. Выделение интерфейсов для Data Mapper и их реализация в классах BL. Но здесь всё равно остаётся проблемма создания экземпляров. Можно разрулить каким-нибудь порождающим паттерном, фабрикой к примеру или даже заюзать IoC engine, reflection в конце концов. Но не все фреймворки реализующие Data Mapper такое позволяют.
Есть у уважаемого сообщества идеи ЕДИНСТВЕННО ПРАВИЛЬНОЙ БОЖЕСТВЕННОЙ АРХИТЕКТУРЫ?
PS Если заюзать на BL Transaction Script к примеру, проблемма исчезает сама собой. Но тот же Фаулер Transaction Script ругает...
PPS Всё вышесказанное есть моё глубокое ИМХО. Если вы считаете иначе — отлично, поделитесь мнением!
Повреждение мозга после ректальной биопсии — редкая штука (с) Хаус
Здравствуйте, server_mouse, Вы писали:
_>Доброго всем времени суток!
_>Мучает меня последнее время вопрос... прям спать не могу . Решил поделиться с общественностью — что б тоже сон потеряла.
_>Итак, что такое многоуровневая архитектура? В моём понимании это такое построение приложения, при котором оно состоит из нескольких слоёв, причём слои верхнего уровня используют слои нижнего, но не наоборот. Классический пример: _>Presentation(UI)->Business Logic(BL)->Data Access Layer(DAL)->Storage(DB)
На самом деле такая кратина неполная.
Есть еще Cross-Cunnting Concerns, а еще есть данные, которыми обмениваются слои, которые тоже ортогональны слоям.
_>Собственно это была приcказка. Сама же сказка такая: _>Реализуем BL на основе Domain Model. Т.е. бизнес логика приложения содержится в классах, которые являются отражением предметной области. Классы содержат данные и логику работы с ними (инкапсуляция блин, кудаж без неё!). _>Реализуем DAL на основе Data Mapper. Собственно слой предоставляющий интерфейс для работы с хранилищем на основе обектно-ориентированой модели.
_>И вот что не даёт мне спать по ночам: DAL связан с BL, поскольку выполняет создание экземпляров классов из Domain Model и их инициализацию данными из DB. Налицо вопиющее нарушение "слойности" архитектуры. Анархия. Разруха. Полный капец.
Именно так и получается. Потому что Domain Model прибивает BL к данным. Единственный способ убрать такую проблему в Domail Model — ввести DTO для обмена данными между слоями.
Короче костыли.
_>Читал Фаулера. Этот "нехороший человек" обошёл проблемму стороной упомянув что "однозначно хорошего решения не существует".
Этот нехороший человек много скользких момнетов обходит стороной. Не стоит на веру принимать все что они пишет.
_>Собственно какие способы вижу я: _>1. Отделение BL от данных и выделение отдельной группы объектов-data-контейнеров. Где же тогда инкапсуляция? Правильно — в Ж.
Это правильное решение. Инкапсуляция ради инкапсуляции не нужна. То что объекты данных занимаются только переносом данных — это правильно.
_>2. Выделение интерфейсов для Data Mapper и их реализация в классах BL. Но здесь всё равно остаётся проблемма создания экземпляров. Можно разрулить каким-нибудь порождающим паттерном, фабрикой к примеру или даже заюзать IoC engine, reflection в конце концов. Но не все фреймворки реализующие Data Mapper такое позволяют.
Вот поэтому domain model сосет.
_>Есть у уважаемого сообщества идеи ЕДИНСТВЕННО ПРАВИЛЬНОЙ БОЖЕСТВЕННОЙ АРХИТЕКТУРЫ?
Ага. Написал в блоге пост-введение к этому вопросу http://gandjustas.blogspot.com/2009/03/blog-post.html.
Как будет время сделаю что-то типа "набросков правильной архитектуры".
_>PS Если заюзать на BL Transaction Script к примеру, проблемма исчезает сама собой. Но тот же Фаулер Transaction Script ругает...
Фаулер нигде сам Transaction Script не ругает, ругает абсолютно идиотскую реализацию в виде паттерна Command, и говорит что это "не ООП".
Хотя он много на что так говорит.
Я имел в виду что исключить логику (действия) из классов предметной области. BLL будет выглядеть в виде сервисов, работающих над классами предметной области и организующих необходимую логику. и DAL и BLL и UI будут знать о классах предметной области, т.е. классы предметной области составляют отдельную группу, доступную на всех уровнях.
Здравствуйте, MozgC, Вы писали:
MC>Я имел в виду что исключить логику (действия) из классов предметной области. BLL будет выглядеть в виде сервисов, работающих над классами предметной области и организующих необходимую логику. и DAL и BLL и UI будут знать о классах предметной области, т.е. классы предметной области составляют отдельную группу, доступную на всех уровнях.
Собственно получается тот же самый Transaction Script просто в виде набора сервисов, верно? А классы предметной области — data-контейнеры?
По поводу видимости классов предметной области на UI — иногда не удобно. Зачастую UI-ю нужны комбинированые объекты или какае-то, обычно простая, логика преобразования. Типа такого:
class Person
{
private string firstName;
private string lastName;
public string FullName
{
get{ return firstName+" ,"+lastName;}
}
}
Пихать её в UI не удобно, получишь копи-паст. Оставлять в Domain (в виду того, что логики там уже нет) — не логично.
Логичнее было бы оставить такие Domain-объекты на уровне DAL, а на BLL сделать свои, удовлетворяющие потребностям UI (и бизнес-логику формирования FullName оставить таким образом бизнес-логике). Есть только одно но — как удобно это реализовать не знаю. Удобно, это когда программисту не нужно делать вручную врапперы над domain-объектами.
Повреждение мозга после ректальной биопсии — редкая штука (с) Хаус
Здравствуйте, server_mouse, Вы писали:
_>Здравствуйте, MozgC, Вы писали:
MC>>Я имел в виду что исключить логику (действия) из классов предметной области. BLL будет выглядеть в виде сервисов, работающих над классами предметной области и организующих необходимую логику. и DAL и BLL и UI будут знать о классах предметной области, т.е. классы предметной области составляют отдельную группу, доступную на всех уровнях.
_>Собственно получается тот же самый Transaction Script просто в виде набора сервисов, верно?
Да.
_>А классы предметной области — data-контейнеры?
Это уже как вам угодно. Некоторые наследуют бизнес-объекты от общего предка, которые реализует интерфейсы для транзакционного редактировния (undo), оповещения об изменениях, отслеживания статуса объекта (сохранен или нет) (в .NET это интерфейсы IEditableObject, INotifyPropertyChanged, IChangeTracking). Хорошо это или нет — отдельная тема для больших споров.
_>По поводу видимости классов предметной области на UI — иногда не удобно. Зачастую UI-ю нужны комбинированые объекты или какае-то, обычно простая, логика преобразования. Типа такого: _>
_>Пихать её в UI не удобно, получишь копи-паст. Оставлять в Domain (в виду того, что логики там уже нет) — не логично.
Лично я бы не заморачивался, и оставил FullName в классе Person.
_>Логичнее было бы оставить такие Domain-объекты на уровне DAL, а на BLL сделать свои, удовлетворяющие потребностям UI (и бизнес-логику формирования FullName оставить таким образом бизнес-логике). Есть только одно но — как удобно это реализовать не знаю. Удобно, это когда программисту не нужно делать вручную врапперы над domain-объектами.
Тут надо найти для себя грань между хорошим/правильным дизайном и слишком большими заморочками.
Здравствуйте, Gadsky, Вы писали:
IT>>Здесь это называется Business Entities. Используется всеми слоями приложения.
G>Надо бы пояснить. Либо статьей в RSDN (в идеале), либо ссылками.
Здравствуйте, server_mouse, Вы писали:
_>И вот что не даёт мне спать по ночам: DAL связан с BL, поскольку выполняет создание экземпляров классов из Domain Model и их инициализацию данными из DB. Налицо вопиющее нарушение "слойности" архитектуры. Анархия. Разруха. Полный капец.
Фишка вот в чем. Твои бизнес объекты включают в себя сразу 2 слоя. Это слой BL и слой который я называю контрактным. Это даже не слой, а формат обмена данными между слоями, на него неизбежно завязаны все или почти всем слои приложения. Если DAL использует только контрактную часть бизнес объектов (а ему по сути больше и не требуется) и не вызывает методов бизнес логики которые торчат из них же — проблемы нет: BL отдельно, DAL отдельно.
Смешение контракта и логики довольно серьезный недостаток жирной модели, но не настолько фатальный чтобы отказываться от нее только поэтому. Спи спокойно.
Здравствуйте, server_mouse, Вы писали:
_>Доброго всем времени суток!
_>И вот что не даёт мне спать по ночам: DAL связан с BL, поскольку выполняет создание экземпляров классов из Domain Model и их инициализацию данными из DB. Налицо вопиющее нарушение "слойности" архитектуры. Анархия. Разруха. Полный капец.
1) Сейчас все чаще уходят от традиционных слоев. Например посмотите The Onion Architecture, если коротко то DAL там не рассматриваеться как отдельный слой, он расматриваеться как инфраструктура, типа логирования или конфигурирования. Ну а если принять что это инраструктура, то все становиться на свои места, вас же не удевляет что CLR знает про ваши обьекты?
2) Репозитарии или ДАЛ это абстракции над базой данных, фактически вы их можно расматривать как саму базуданных(точнее как хранилище), соотвевенно логично что ДАЛ знает про сущьности, ведь именно сущности находяться в хранилище.
_>Читал Фаулера. Этот "нехороший человек" обошёл проблемму стороной упомянув что "однозначно хорошего решения не существует".
?? Ссылочку если можно. Обычно у Фаулера идет "однозначно хорошого решения не существует для всех ситуаций". Это правда, если был силвербулет все бы им пользовались.
_>Собственно какие способы вижу я: _>1. Отделение BL от данных и выделение отдельной группы объектов-data-контейнеров. Где же тогда инкапсуляция? Правильно — в Ж.
Есть принцип YAGNI, принципы ради принципов? Щас ОРМ тулзы сводят ДАЛ до декларации типа "хочу репозитарий для типа Юзер"...
_>2. Выделение интерфейсов для Data Mapper и их реализация в классах BL. Но здесь всё равно остаётся проблемма создания экземпляров. Можно разрулить каким-нибудь порождающим паттерном, фабрикой к примеру или даже заюзать IoC engine, reflection в конце концов. Но не все фреймворки реализующие Data Mapper такое позволяют.
Опять же принцип YAGNI, зачем? Вам будет проще от этого отказаться если вы попытаетесь сформулировать зачаем это вам надо. Если получиться попытаться сформулировать а надо ли вам оно щас...
_>Есть у уважаемого сообщества идеи ЕДИНСТВЕННО ПРАВИЛЬНОЙ БОЖЕСТВЕННОЙ АРХИТЕКТУРЫ?
Здравствуйте, server_mouse, Вы писали:
_>Т.е. бизнес логика приложения содержится в классах, которые являются отражением предметной области. Классы содержат данные и логику работы с ними (инкапсуляция блин, кудаж без неё!).
Это не инкапсуляция, а скорее наоборот.
_>1. Отделение BL от данных и выделение отдельной группы объектов-data-контейнеров. Где же тогда инкапсуляция? Правильно — в Ж.
А в данном случае, с инкапсуляцией все в порядке.
Здравствуйте, MozgC, Вы писали:
MC>Я имел в виду что исключить логику (действия) из классов предметной области. BLL будет выглядеть в виде сервисов, работающих над классами предметной области и организующих необходимую логику. и DAL и BLL и UI будут знать о классах предметной области, т.е. классы предметной области составляют отдельную группу, доступную на всех уровнях.
В чистом виде такой подход ИМХО несостоятелен. Существует масса мелких "алгоритмиков" (типа счётчика ссылок в A->setB(B)), которые ты либо пихаешь внутрь объектов модели, либо вынужден дублировать этот код во всех сервисах BLL.
Здравствуйте, IB, Вы писали:
IB>Здравствуйте, server_mouse, Вы писали:
_>>Т.е. бизнес логика приложения содержится в классах, которые являются отражением предметной области. Классы содержат данные и логику работы с ними (инкапсуляция блин, кудаж без неё!). IB>Это не инкапсуляция, а скорее наоборот.
По-моему, вы двое говорите о разной инкапсуляции.
Я повторю свой пример: есть метод A->setB(B) и поле B::countAs. Корректировка второго из первого — это ИМХО оправданная инкапсуляция логики (в данном случае тривиальной), поскольку в противном случае об этом счётчике придётся помнить всюду, где используется первый метод.
Скажем так, я склонен вгонять в entities ту логику, которая (при наличии желания) также естественно могла бы лечь в триггеры базы.
_>>1. Отделение BL от данных и выделение отдельной группы объектов-data-контейнеров. Где же тогда инкапсуляция? Правильно — в Ж. IB>А в данном случае, с инкапсуляцией все в порядке.
Бояре сумлеваются. Редуцировать entities до структур... я лучше уж DTO добавлю (в качестве базовых классов entities).
Здравствуйте, dimgel, Вы писали:
D>Я повторю свой пример: есть метод A->setB(B) и поле B::countAs. Корректировка второго из первого — это ИМХО оправданная инкапсуляция логики (в данном случае тривиальной), поскольку в противном случае об этом счётчике придётся помнить всюду, где используется первый метод.
Неправильные у вас пчелы. Вот этот B::countAs, как и упомянутые счетчики, никакого, я подозреваю, отношения к предметной логике не имеют, а являются неким инфраструктурным моментом, болтающимся под ногами и отвлекающим от основного занятия. Так что пример несколько некорректный.
D>Скажем так, я склонен вгонять в entities ту логику, которая (при наличии желания) также естественно могла бы лечь в триггеры базы.
Какие-то триггеры появились уже. С таким критерием, кстати, все еще хуже: эти самые entities начинают вмешиваться в дела, соверщенно к ним не относящиеся, как то сохранение в БД, ведение аудита (это я гадаю на кофейной гуще; триггеры же для этого собрались использовать?), обновление каких-то полей. Это, по-вашему, правильная "инкапсуляция"? По-моему, это повышение связности на ровном месте и усложнение себе жизни.
D>Бояре сумлеваются. Редуцировать entities до структур... я лучше уж DTO добавлю (в качестве базовых классов entities).
А альтернатива какая? Монстроподобные объекты с кучей логики, не умеющие разве что кофе варить? Тоже плохо. Объекты с неким вменяемым объемом логики? А где проходит эта граница "вменяемости" и какими критериями (по возможности наименее противоречивыми и субъективными) стоит руководствоваться для решение, куда какую логику помещать?
HgLab: Mercurial Server and Repository Management for Windows
Сдаётся мне, что меня также неправильно поняли.
D>>Я повторю свой пример: есть метод A->setB(B) и поле B::countAs.
Н>Неправильные у вас пчелы. Вот этот B::countAs, как и упомянутые счетчики, никакого, я подозреваю, отношения к предметной логике не имеют, а являются неким инфраструктурным моментом, болтающимся под ногами и отвлекающим от основного занятия. Так что пример несколько некорректный.
Тем не менее, это исполняемый код. Помню я давнишний спор IT с adontz-ом, в котором IT отстаивал entities как просто структуры без поведения, благодаря чему он получал возможность пользоваться этими entities на всех слоях, в т.ч. передавать их по сети в качестве DTO. (Одним из основных его аргументов было нежелание возиться с параллельной структурой DTO.) Adontz, напротив, отстаивал "умные объекты", из которых автоматически следовала необходимость DTO.
Так вот, инфраструктурный момент это или бизнес-логика, неважно. Это код. Наличие которого в entities означает автоматическую невозможность (или сильную усложнённость, как минимум) использования этих entities в качестве DTO. Если ты согласен с тем, что инкремент счётчика ссылок B::countAs можно размещать в A::setB(B), то считай, что ты уже в лагере adontz-а, сознательно или нет. Сам я в его лагере совершенно сознательно.
Я тут приведу дальнюю аналогию: мнение Стива Круга относительного максимального числа кликов (переходов по ссылкам) при поиске страницы на сайте. Он говорит, что неважно сколько кликов, до тех пор, пока каждый клик очевиден, т.е. очевидным образом приближает нас к цели. Аналогия тут с избыточным кодом DTO. Вот между (1) необходимостью создавать параллельную иерархию DTO и методы toDTO и fromDTO в каждом классе entity, (2) необходимости выносить "инфраструктурные моменты" в BLL, я выберу первое. Оно и удобнее, и гибче, хоть и объёмнее и тормознее. Пример насчёт гибкости: был у одного сайтика swing-клиент, из-под которого могли логиниться и админы, и обычные юзеры. Юзверям отдавались урезанные DTO, не содержащие чувствительной информации. Админам — полные. Вторые наследовались из первых; методы выглядели как toDTO(Class).
D>>Бояре сумлеваются. Редуцировать entities до структур... я лучше уж DTO добавлю (в качестве базовых классов entities).
Н>А альтернатива какая? Монстроподобные объекты с кучей логики, не умеющие разве что кофе варить? Тоже плохо.
Согласен, безусловно.
Н>Объекты с неким вменяемым объемом логики? А где проходит эта граница "вменяемости" и какими критериями (по возможности наименее противоречивыми и субъективными) стоит руководствоваться для решение, куда какую логику помещать?
В общем случае это вопрос открытый, но некий пример был, кажется, в "Архитектуре" Фаулера: отправка уведомлений на мыло при изменении базы выполнялся внешним слоем (Service Layer это был, кажется). Идея в том, что это внешняя вещь к логике взаимодействия объектов системы.
Сам я привык руководствоваться следующим правилом: если изменение состояния объекта базы (entity) ведёт к необходимости каскадных изменений состояний других объектов (как в примере со счётчиком ссылок), код, выполняющий эти изменения, я вгоняю внутрь entity. В результате entities у меня, так сказать, сами следят за логической целостностью базы. Любые другие эффекты, не относящиеся к модификации состояния базы, я выношу в контроллеры.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[5]: Кстати говоря, в lift (scala web framework)
В сабже вообще фокус с использованием entities в качестве DTO не прокатит. Я ещё не разобрался толком, как оно работает, но POJO там и не пахнет:
class ToDo extends LongKeyedMapper[ToDo] with IdPK {
def getSingleton = ToDo
object done extends MappedBoolean(this)
object owner extends MappedLongForeignKey(this, User)
object priority extends MappedInt(this) {
override def defaultValue = 5
// TODO Throws an exception on non-numeric value!!!
override def validations = validPriority _ :: super.validations
def validPriority(in: Int): List[FieldError] =
if (in > 0 && in <= 10) Nil
else List(FieldError(this, <b>Priority must be 1-10</b>))
override def _toForm =
Full(select(ToDo.priorityList, Full(is.toString), f => set(f.toInt)))
}
object desc extends MappedPoliteString(this, 128) {
override def validations =
valMinLen(3, "Description must be at least 3 characters") _ ::
super.validations
}
}
object ToDo extends ToDo with LongKeyedMetaMapper[ToDo] {
lazy val priorityList =
("AAA", "AAA") ::
(1 to 10).map(v => (v.toString, v.toString)).toList
}
Это пример из "Getting started with Lift" pdf. Как бы дико он не выглядел, эта фигня не привязана к существующим non-scala frameworks и позволяет на полную использовать возможности языка, в частности mixins (в примере это IdPK). Что меня безумно радует: у меня тут отдельные миксины для поведения TreeNode, Deletable (флаг deleted/scheduled for deletion, связанный со счётчиками ссылок на данный объект), Filtered (для отношений master/slave) и прочего; каждый mixin содержит объявления своих полей базы. И я их все могу впендюрить в один и тот же entity class.
И тут, как видите, изрядная прорва логики валидации и представления вшита в entity. Дико? Ну и идите в пень. Зато удобно.
Здравствуйте, dimgel, Вы писали:
D>Так вот, инфраструктурный момент это или бизнес-логика, неважно. Это код. Наличие которого в entities означает автоматическую невозможность (или сильную усложнённость, как минимум) использования этих entities в качестве DTO. Если ты согласен с тем, что инкремент счётчика ссылок B::countAs можно размещать в A::setB(B), то считай, что ты уже в лагере adontz-а, сознательно или нет. Сам я в его лагере совершенно сознательно.
Ты на С++ пишешь чтоли? Там уже давно shared_ptr для посчета ссылок придумалию
D>Я тут приведу дальнюю аналогию: мнение Стива Круга относительного максимального числа кликов (переходов по ссылкам) при поиске страницы на сайте. Он говорит, что неважно сколько кликов, до тех пор, пока каждый клик очевиден, т.е. очевидным образом приближает нас к цели. Аналогия тут с избыточным кодом DTO. Вот между (1) необходимостью создавать параллельную иерархию DTO и методы toDTO и fromDTO в каждом классе entity, (2) необходимости выносить "инфраструктурные моменты" в BLL, я выберу первое. Оно и удобнее, и гибче, хоть и объёмнее и тормознее.
Это полнейший бред.
Все что ты описал сильно снижает mantainability, один и теже изменения делать в разных местах одинаково хреново независимо от того очевидно это или нет.
Есдиственная случай когда дублирование оправдано — автогенерный код, и то не всегда.
Н>>Объекты с неким вменяемым объемом логики? А где проходит эта граница "вменяемости" и какими критериями (по возможности наименее противоречивыми и субъективными) стоит руководствоваться для решение, куда какую логику помещать? D>В общем случае это вопрос открытый, но некий пример был, кажется, в "Архитектуре" Фаулера: отправка уведомлений на мыло при изменении базы выполнялся внешним слоем (Service Layer это был, кажется). Идея в том, что это внешняя вещь к логике взаимодействия объектов системы.
В общем случае надо отделять всю логику от данных. Тогда будет меньше дублирования и больше повторного использования.
D>Сам я привык руководствоваться следующим правилом: если изменение состояния объекта базы (entity) ведёт к необходимости каскадных изменений состояний других объектов (как в примере со счётчиком ссылок), код, выполняющий эти изменения, я вгоняю внутрь entity. В результате entities у меня, так сказать, сами следят за логической целостностью базы. Любые другие эффекты, не относящиеся к модификации состояния базы, я выношу в контроллеры.
Это чисто инфраструктурная вещь, которая порождена отсуствием GC. В принципе есть shared_ptr для этих целей, но в данных циклические зависимости случаются часто.
Здравствуйте, gandjustas, Вы писали:
G>Ты на С++ пишешь чтоли? Там уже давно shared_ptr для посчета ссылок придумалию
Нет, не на C++. Но есть у меня сомнение, что C++ный shared_ptr можно прикрутить к ORM entities, чтобы он записывал обновлённое значение счётчика ссылок в базу.
G>Это полнейший бред.
Сам дурак.
G>Все что ты описал сильно снижает mantainability, один и теже изменения делать в разных местах одинаково хреново независимо от того очевидно это или нет.
Это очевидно. А теперь изволь изложить, как бы ты реализовал мой пример — когда админам и обычным юзерам (и те и другие коннектятся к сайту из swing-клиента) нужно отдавать одни и те же структуры данных, за исключением чувствительной информации, которую юзерам видеть не полагается.
G>В общем случае надо отделять всю логику от данных. Тогда будет меньше дублирования и больше повторного использования.
То есть, в каждом месте, где выполняется a->setB(b), не забывать добавлять b->countAs++? Да, действительно меньше дублирования. И очень повышает maintainability.
G>Это чисто инфраструктурная вещь, которая порождена отсуствием GC. В принципе есть shared_ptr для этих целей, но в данных циклические зависимости случаются часто.
Здравствуйте, dimgel, Вы писали:
D>Так вот, инфраструктурный момент это или бизнес-логика, неважно. Это код. Наличие которого в entities означает автоматическую невозможность (или сильную усложнённость, как минимум) использования этих entities в качестве DTO.
Вообще говоря, DTO ты упомянул совершенно вскользь, так что непонятно, зачем к этому аппеллировать -- разговор совершеннл не про них.
D>Вот между (1) необходимостью создавать параллельную иерархию DTO и методы toDTO и fromDTO в каждом классе entity,
Уже плохо. Сегодня -- toDTO/fromDTO, завтра -- toJSON/fromJSON, послезавтра -- бардак. Ясных критериев-то нет.
D>Сам я привык руководствоваться следующим правилом: если изменение состояния объекта базы (entity) ведёт к необходимости каскадных изменений состояний других объектов (как в примере со счётчиком ссылок),
Ты отвлекись от счетчиков и ссылок -- это тебе твой же инструмент сложности создает.
D>В результате entities у меня, так сказать, сами следят за логической целостностью базы.
То есть все твои сущности знают о том, как сохранять себя в БД?
HgLab: Mercurial Server and Repository Management for Windows