tag:blogger.com,1999:blog-5422036137568788209.post3124589205973473089..comments2023-02-28T12:04:19.260+03:00Comments on handcode: Domain-Driven Design: создание доменаИлья Дубаденкоhttp://www.blogger.com/profile/03756815679135463187noreply@blogger.comBlogger44125tag:blogger.com,1999:blog-5422036137568788209.post-37829097757477226512010-05-17T23:37:17.388+04:002010-05-17T23:37:17.388+04:00Занимательная дискуссия получилась..Занимательная дискуссия получилась..Романhttps://www.blogger.com/profile/04917528930881078300noreply@blogger.comtag:blogger.com,1999:blog-5422036137568788209.post-8632404574942406602010-05-07T18:33:25.648+04:002010-05-07T18:33:25.648+04:00@ankstoo
к вам притензий нет и я поддерживаю точку...@ankstoo<br />к вам притензий нет и я поддерживаю точку зрения, что если ОРМ не предъявляет к сохраняемым объектам специфических требований, то от дополнительного слоя можно и нужно отказаться.<br /><br />Мне не очень нравятся интерфейсы в качестве доменных объектов, т.к. при смене ОРМ будут проблемы сдублированием кода и его поддержкой.hazzikhttps://www.blogger.com/profile/02278053558276208312noreply@blogger.comtag:blogger.com,1999:blog-5422036137568788209.post-64158174912213654172010-05-07T15:35:39.866+04:002010-05-07T15:35:39.866+04:00@hazzik
>> И вы все сами поймете.
я понимаю...@hazzik <br />>> И вы все сами поймете.<br />я понимаю :) Если домен наследуется от EntityObject или чего-то подобного - это плохо, т.к. он зависит от persistence. Намного лучше, чтобы домен был POCO.<br />Но в случае, например, NHibernate, вводить дополнительный слой ORM-объектов бессмысленно. NH отлично работает с POCO доменом. Об этом я и писал.<br /><br />>> PS:... Но с этим подходом когда-нибудь также возникнут трудности. <br />Тут трудности очевидны. Мы не можем использовать доменный объект без persistence (Например в unit-тестах). Или надо писать MockAccountRow.ankstoohttps://www.blogger.com/profile/10690746799538408916noreply@blogger.comtag:blogger.com,1999:blog-5422036137568788209.post-83565595929625179132010-05-07T14:25:12.679+04:002010-05-07T14:25:12.679+04:00@ankstoo
Так.
Просто попытайтесь выделить ответст...@ankstoo<br />Так.<br /><br />Просто попытайтесь выделить ответственности такой на половину сгенерированной entity. И вы все сами поймете. Она несет в себе лишнюю ответсвенность - хранит информацию о том как она сохраняется в базе. Таким образом, видно что большинство ORM, требующие наследоваться от своего базового класса - нарушают SRP.<br /><br />И еще раз повторю, что все что здесь мной сказано относиться к моему примеру с щедрым новым заказчиком и требованием сохранить старую ОРМ для старых заказчиков.<br /><br />PS: Если использовать интерфейсы для того, что нагенерировала нам ОРМ, то можно возложить обязанность преобразования на сам доменный объект.<br /><br />пример:<br /><br />public interface IAccountRow { // наш интерфейс, чтоб отвязаться от ORM<br />int Id {get;set;}<br />string Name {get;set;}<br />}<br /><br />public class AccountRow : EntityObject, IAccountRow { // то что нам нагенерировала EF<br />public int Id {get;set;}<br />public string Name {get;set;}<br />}<br /><br />public class Account : IEntity { // доменный объект<br />private IAccountRow row;<br />public int Id {get{ return row.Id;} set{ row.Id = value;}}<br />public string Name {get { return row.Name;}set{ row.Name = value;}}<br />}<br /><br />Но с этим подходом когда-нибудь также возникнут трудности. <br /><br />PS: следуйте SOLID и у вас все получиться. <br />PSS: помните, что SOLID это только принципы, а не законы и правила. Они требуют подхода к делу с головой.hazzikhttps://www.blogger.com/profile/02278053558276208312noreply@blogger.comtag:blogger.com,1999:blog-5422036137568788209.post-70432171753561893622010-05-07T13:10:41.574+04:002010-05-07T13:10:41.574+04:00@hazzik
>> Доменная сущность и объект отраже...@hazzik<br />>> Доменная сущность и объект отраженный из базы это совершенно разные вещи. <br /><br />Т.е. сначала мапим базу на объекты данных. Потом мапим объекты данных на доменную модель? Так?ankstoohttps://www.blogger.com/profile/10690746799538408916noreply@blogger.comtag:blogger.com,1999:blog-5422036137568788209.post-22877014361787760602010-05-07T11:02:03.199+04:002010-05-07T11:02:03.199+04:00Если например код из CalculateOrdersSum будет повт...Если например код из CalculateOrdersSum будет повторяться в других сущностях, то его проще вынести в helper.<br />В моих проектах всегда есть helper-расширения для IQueryble.<br />Например:<br />return account.Orders.Enabled().Where(order => order.IsComplete == false);<br /><br />Где под Enabled() позволяет избежать дублирование Where(entity => entity.Enabled);Илья Дубаденкоhttps://www.blogger.com/profile/03756815679135463187noreply@blogger.comtag:blogger.com,1999:blog-5422036137568788209.post-88992147022117726042010-05-06T21:43:31.637+04:002010-05-06T21:43:31.637+04:00> Я не силен в NH, не могу сказать, может там е...> Я не силен в NH, не могу сказать, может там есть нечто специфическое. <br />Под NH имеловсь ввиду сферический ORM в ваккууме, будь то L2S, NH, LLBLGenPro.<br /><br />Чем тогда ваше решение с хелперами будет лучше чем пример 1 из сататьи Александра, от которого он так старательно избавляется?hazzikhttps://www.blogger.com/profile/02278053558276208312noreply@blogger.comtag:blogger.com,1999:blog-5422036137568788209.post-9689097080229536612010-05-06T19:19:33.739+04:002010-05-06T19:19:33.739+04:00Это вопрос из серии, "Как сделать поддержку н...Это вопрос из серии, "Как сделать поддержку нескольких систем логирования или нескольких IoC контейнеров". Данная проблема решается в лоб, с помощью введения своих внутренних интерфейсов ILog, IDependencyResolver.Илья Дубаденкоhttps://www.blogger.com/profile/03756815679135463187noreply@blogger.comtag:blogger.com,1999:blog-5422036137568788209.post-89540549938114550002010-05-06T19:09:37.055+04:002010-05-06T19:09:37.055+04:001. В helper-ы можно вынести повторный код.
partial...1. В helper-ы можно вынести повторный код.<br />partial class Account : IAccount {<br />public void Some() {<br />// вызвать helper, как вариант<br />}<br />}<br />Те же самые kigg, например в классе Tag, свойство Stories юзает EntityHelper.<br /><br />2. Я не силен в NH, не могу сказать, может там есть нечто специфическое. Но подобное реализовано в kigg, где организовано переключение между LinkToSql и EF. Я особо не копался в исходниках, поэтому детально не расскажу. Например, можно подсмотреть реализацию свойства int StoryCount, в классе Tag.<br /><br />PS: Да, щедрость заказчика определяет многое :)Илья Дубаденкоhttps://www.blogger.com/profile/03756815679135463187noreply@blogger.comtag:blogger.com,1999:blog-5422036137568788209.post-4224209840981325392010-05-06T18:49:54.082+04:002010-05-06T18:49:54.082+04:00>если только не захотите вынести ее в helper-ы
...>если только не захотите вынести ее в helper-ы<br />Чем это будет отличаться от примера 1, из статьи Александра Бындю?<br /><br /><br />Ну смотрите, псевдо-реальный пример:<br /><br />У меня есть IAccount:<br /><br />public interface IAccount { <br />bool Activated {get;}<br />void Activate(); /*other methods*/ <br />}<br /><br />И есть конкретная реализация под EF:<br /><br />public class Account : EntityObject { <br />public bool Activated {get;set;} // ef realization<br />} <br /><br />partial class Account : IAccount {<br />public void Activate() {<br />this.Activated = true;<br />// other activation logic<br />}<br />}<br /><br />И теперь мне нужно *ДОБАВИТЬ* поддержку NH, как того требует мой щедрый новый заказчик. Добавить таким образом, чтоб существующие пользователи моего продукта не испытывали никаких неудобств - их полностью устраивает EF и они не собираются его менять.<br /><br />Мои действия?hazzikhttps://www.blogger.com/profile/02278053558276208312noreply@blogger.comtag:blogger.com,1999:blog-5422036137568788209.post-62155021107056907412010-05-06T18:43:09.180+04:002010-05-06T18:43:09.180+04:00@hazzik
В идеале :)
Логика никуда не должна деват...@hazzik<br /><br />В идеале :)<br />Логика никуда не должна деваться (если только не захотите вынести ее в helper-ы)<br /><br />Логика никак не должна меняться.Илья Дубаденкоhttps://www.blogger.com/profile/03756815679135463187noreply@blogger.comtag:blogger.com,1999:blog-5422036137568788209.post-87560877453192030552010-05-06T18:37:12.862+04:002010-05-06T18:37:12.862+04:00@Илья
Я хочу получить конкретный ответ:)@Илья<br />Я хочу получить конкретный ответ:)hazzikhttps://www.blogger.com/profile/02278053558276208312noreply@blogger.comtag:blogger.com,1999:blog-5422036137568788209.post-64757318644931721672010-05-06T18:20:40.199+04:002010-05-06T18:20:40.199+04:00@hazzik
Смотря как организована эта логика. Форма...@hazzik<br /><br />Смотря как организована эта логика. Формально смена ORM никак не должна на нее влиять, и это правильно. <br />Но зачастую сталкиваешься с тем, что домен (отражающий реальную предметную область) есть просто реляционное отражение базы (так как нам удобно это хранить), плюс поведение (логика).Илья Дубаденкоhttps://www.blogger.com/profile/03756815679135463187noreply@blogger.comtag:blogger.com,1999:blog-5422036137568788209.post-45515830162792832742010-05-06T17:50:04.809+04:002010-05-06T17:50:04.809+04:00@ankstoo
Доменная сущность и объект отраженный из...@ankstoo <br />Доменная сущность и объект отраженный из базы это совершенно разные вещи. И отражение называется Object Relation Mapping. А не Domain Entity-Relation Mappinghazzikhttps://www.blogger.com/profile/02278053558276208312noreply@blogger.comtag:blogger.com,1999:blog-5422036137568788209.post-4587209128907418982010-05-06T17:47:19.256+04:002010-05-06T17:47:19.256+04:00Что будет с инвариантной логикой, которая находить...Что будет с инвариантной логикой, которая находиться в сущностях? Куда она денется при смене ОРМ?hazzikhttps://www.blogger.com/profile/02278053558276208312noreply@blogger.comtag:blogger.com,1999:blog-5422036137568788209.post-52345634495350577012010-05-05T17:46:04.463+04:002010-05-05T17:46:04.463+04:00Да, спасибо, абсолютно точно подмечено. В случае с...Да, спасибо, абсолютно точно подмечено. В случае с EF нам "мешает" базовый тип System.Data.Objects.DataClasses.EntityObject.<br />Кстати, глянул в NHibernate, там такой проблемы нет. Надо будет глянуть применение T4 в EF4.Илья Дубаденкоhttps://www.blogger.com/profile/03756815679135463187noreply@blogger.comtag:blogger.com,1999:blog-5422036137568788209.post-47861273554936906722010-05-05T17:02:31.961+04:002010-05-05T17:02:31.961+04:00>> Пересмотрите топики, я это сказал еще в с...>> Пересмотрите топики, я это сказал еще в самом начале. Собственно здесь мы и пытаемся разорвать эту связь.<br /><br />Если ORM может работать с доменом как с POCO, т.е. не требовать от домена наследоваться от спец класса, реализовывать спец интерфейсы или иметь спец атрибуты, то такая проблема даже не появляется.<br /><br />NHibernate умее, EF в 4 версии вроде тоже.ankstoohttps://www.blogger.com/profile/10690746799538408916noreply@blogger.comtag:blogger.com,1999:blog-5422036137568788209.post-9078139102643117932010-05-05T16:47:07.600+04:002010-05-05T16:47:07.600+04:00@hazzik
Здесь Вы ошибаетесь, что такое домен и ка...@hazzik<br /><br />Здесь Вы ошибаетесь, что такое домен и как с ним работать я знаю.<br /><br />>>Если ваш домен зависит от ORM - все ваше приложение зависит от ORM.<br />Пересмотрите топики, я это сказал еще в самом начале. Собственно здесь мы и пытаемся разорвать эту связь.<br /><br />С NHibernate не работал, но представляю, что он работает как стандартный ORM.Илья Дубаденкоhttps://www.blogger.com/profile/03756815679135463187noreply@blogger.comtag:blogger.com,1999:blog-5422036137568788209.post-62230941938712364382010-05-05T13:10:20.305+04:002010-05-05T13:10:20.305+04:00На каждый экземпляр сервиса создаем новый репозито...На каждый экземпляр сервиса создаем новый репозиторий, который в себе создает UnitOfWork, либо инжектирует его:<br />IRepository r = new Repository(/*инжектирует*/)<br /><br />В сервис метод инжектируем созданный репозиторий, который потом используем в сервис методе.<br />void MakeBigWork()<br />{<br />// используем наши репозиторий..<br />r.Save(); //<- Save сохранит все измеения в БД<br />}<br /><br />Можно использовать TransactionScope, все зависит от конкретной задачи<br /><br />Это если по-простому. <br />Либо, можно посмотреть исходники Kigg.codeplex.com, там подобное реализовано очень хорошо, через абстрактный интерфейс IDataBase.Илья Дубаденкоhttps://www.blogger.com/profile/03756815679135463187noreply@blogger.comtag:blogger.com,1999:blog-5422036137568788209.post-28754965037297372032010-05-05T12:57:17.122+04:002010-05-05T12:57:17.122+04:00небольшой пример, если можнонебольшой пример, если можноАртем Мурадовhttps://www.blogger.com/profile/03514899372537911566noreply@blogger.comtag:blogger.com,1999:blog-5422036137568788209.post-41983077993161877462010-05-05T12:47:36.868+04:002010-05-05T12:47:36.868+04:00@Артем
Почему? Достаточно приемлемо. Сервис откры...@Артем<br /><br />Почему? Достаточно приемлемо. Сервис открывает транцакцию, точнее UnitOfWork, а репозитории работают в текущем UnitOfWork, в конце делаем Commit для UnitOfWork.Илья Дубаденкоhttps://www.blogger.com/profile/03756815679135463187noreply@blogger.comtag:blogger.com,1999:blog-5422036137568788209.post-46402813043421326372010-05-05T12:42:55.384+04:002010-05-05T12:42:55.384+04:00@Илья
то есть у тебя сервис-класс управляет транз...@Илья <br />то есть у тебя сервис-класс управляет транзакциями? Это не гудАртем Мурадовhttps://www.blogger.com/profile/03514899372537911566noreply@blogger.comtag:blogger.com,1999:blog-5422036137568788209.post-43196837349463004302010-05-05T12:29:14.045+04:002010-05-05T12:29:14.045+04:00@Артём
Конечно, все в сервис-классе(Service-layer...@Артём<br /><br />Конечно, все в сервис-классе(Service-layer), который инжектирует в себя репозитарий(Repository).Илья Дубаденкоhttps://www.blogger.com/profile/03756815679135463187noreply@blogger.comtag:blogger.com,1999:blog-5422036137568788209.post-67009422355133302322010-05-05T12:29:14.046+04:002010-05-05T12:29:14.046+04:00@Илья, вы не понимаете сути домена. Домен это ядро...@Илья, вы не понимаете сути домена. Домен это ядро приложения. Если ваш домен зависит от ORM - все ваше приложение зависит от ORM.<br /><br />Решение следующее: Repository оперирует именно доменными объектами, и ВНУТРИ себя ЧЕРЕЗ мэпер делает преобразование DataRow <-> Entity.<br /><br />NHibernate в общем случае позволяет избавиться от мапинга, потому что ОН уже берет на себя это преобразование. <br /><br />Если вы читали PoEAA, то вы не могли не заметить, что почти все патерны из нее реализованы внутри NH, но естественно называются по-другому.hazzikhttps://www.blogger.com/profile/02278053558276208312noreply@blogger.comtag:blogger.com,1999:blog-5422036137568788209.post-66905664914741646942010-05-05T12:26:50.873+04:002010-05-05T12:26:50.873+04:00@hazzik понял тебя
@Илья - так в том то и дело, гд...@hazzik понял тебя<br />@Илья - так в том то и дело, где ты будешь вызывать начало/конец транзакции? не в коде страницы жеж :)<br />@ankstoo конечно можно, мало того - иногда так и делаю, только вот проблема - экземпляр доменного объекта почему то создает репозиторий - это вообще никак не его обязанность :)Артем Мурадовhttps://www.blogger.com/profile/03514899372537911566noreply@blogger.com