Pull to refresh

Comments 27

А еще не хватает примера, как грузить контакт из базы :-)

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

Да, просто в этом случае в мэппере получается волосня, нет?

А в чём сложность?


Если доступ в БД организован хранимыми процедурами, то есть или DataReader, или DTO из которых напрямую можно вытащить данные, определить, какие данные контакта есть и создать экземпляр соответсвющего класса.


Если используется ORM, то всё уже зависит от конкретной реализации, но, насколько я знаю, Entity Framework и NHibernate поддерживают наследование.

Вы предлагаиете наследовать ваши модели от классов которые используют ORM?

Нет, я предлагаю использовать мои модели непосредственно в качестве классов, которые сохраняются в БД через ORM. Я имел ввиду, что ORM позволяют сохранять иерархию классов и восстанавливать нужного наследника из БД. Насколько сильно придётся подстраивать модели под возможности ORM, надо смотреть по конкретной ORM.

Ух, ну не надо так прибивать гвоздями друг другу доменную модель и модель хранения. Даже если есть ORM, то вполне вероятно, что структура доменного объекта будет не похожа на оптимальное представление в БД

А как вы предлагаете использовать ORM? Чисто для доступа к БД?


По моему опыту в этом случае лучше использовать хранимые процедуры, потому что всё равно получаются те же хранимые процедуры, только на C#. В результате ORM фактически работает с DTO, эти DTO всё равно надо как-то отображать в доменную модель, при этом язык для написания запросов ограничен, к оптимизации запросов прибавляется слой трансляции из C# в SQL, а схема БД жёстко связана с приложением (или приложениями).

Нет, только не хранимки. По моему мнению их использование усложняет поддержку — слой доступа к данным уезжает непонятно куда и выходит из под контроля.
Если говорить о настольных приложениях — то все просто — поднял доменную модель из базы и объекты долго живут и все хорошо.
Если говорить о веб приложениях, то объекты живут условно говоря только на время запроса. И тут приходит на помощь CQRS и начинают появляться плюсы orm. При выполнении команды — поднимаем нормальную модель из базы. При выполнении запроса работаем напрямую с orm, строя хитрые оптимальные запросы. Ведь, чтобы отобразить на странице список имён каких либо элементов, нам не нужно доставать из базы целые сущности — достаточно достать именно имена, да и сортировку можно сделать средствами базы.

слой доступа к данным уезжает непонятно куда и выходит из под контроля.

Мы используем SSDT, хранимки лежат в том же репозитории, что и остальной код.


поднял доменную модель из базы


поднимаем нормальную модель из базы


нам не нужно доставать из базы целые сущности

Видимо, мы не совсем поняли друг друга. В моём понимании иерархия Contact в этом примере — это и есть сущность. То есть это модель предметной области, а не модель представления для MVC. Должно быть, пример с отображением Contact сразу на интерфейс создал не совсем правильное впечатление.

Впринципе подход с SSDT имеет право на жизнь, однако если все запросы приходится писать на чистом SQL, то их потом и маппить вручную приходится на объекты? Не задалбывает? Кажется что это тупая и монотонная работа.


Нет, я понял о какой модели вы говорите. Я имел ввиду, что не всегда модель простая — в моей работе чаще всего это агрегат. При этом не всегда все части этого агрегата лежат в одной и той же базе. Иногда это может быть объектная БД с доступом через REST. Так вот для того, чтобы выполнять запросы к сервису "на запись", то есть во время которых выполняется какая-то бизнес-логика, мы собираем полный агрегат. Но это слишком дорого делать на каждый чих, например для запросов на чтение кусков этого агрегата. Тогда мы лезем напрямую в базу и не строим жирную модель.

Для маппинга у нас есть внутренняя наработка, генератор кода, который создаётся нужные DTO и обвязку для вызова хранимок. В проекте это выглядит ка T4-шаблон, в котором указывается имя хранимки, схема и прочие настройки. Наверное, что-то подобное можно сделать на импорте "функций" в Entity Framework, хотя с ним вылезают наружу ObjectParameter'ы.


А как вы обращаетесь к БД через REST? Есть поддержка со стороны ORM или какой-то специализированный слой доступа?

По факту у нас есть клиент в nuget пакете. А у него методы типа "дай значение по такому-то ключу из такой-то таблицы". Дальше в зависимости от задачи поверх этого наворачивается нужное.

Хранить ваши модели в БД так себе затея
1) При таком подходе сложнее поддердживать бд/ домейн логику, т.к. изминение в вашей модели затрагивают DAL и Buisness Logic.
2) Если БД уже есть вам нужно будет писать аналогичные модели, для того чтобы отобразить структуру БД
3) Изменение источника данных будет крайне сложным (например если источник данных нужно будет изменить на вызов сервиса, место БД)
  1. ORM, по-идее, и должен разделять модели бизнес-логики и их представление в БД. Например, EmailAddress не требуется хранить в отдельной таблице только потому, что в коде это отдельный класс. ORM как раз обещает persistence ignorance, а его несоблюдение называют анти-паттерном .
  2. Аналогично п. 1, придётся настраивать отображение структуры БД в модели.
  3. Если вместо БД надо будет вызывать сервис, то ORM всё равно придётся заменять на клиент для сервиса, разве нет?

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

Покажите как это красиво сделать, если не сложно.

Поковырявшись с EF6 и NHibernate вынужден признать, что ошибся.


Оба ORM поддерживают наследование, в типы добавляются закрытые (private) сеттеры и конструкторы по умолчанию, и после этого они сохраняются в БД и загружаются обратно. Однако на смене типа после UpdatePostalAddress возникают проблемы с сохранением нового объекта вместо старого. Судя по тикету, в Entity Framework Core замена объекта возможна, но я ещё не проверял. В любом случае, задача требует больше усилий и больше зависит от конкретной ORM, чем я думал.


Извините, если ввёл в заблуждение.

Как правило статьи, рассказывающие о проектировании типами, содержат примеры на функциональных языках
Что за «проектирование типами»? Первый раз слышу термин.
Оборачиваем примитивные типы в доменные
Что такое «доменные типы»? Типы, которые вы создаете, похожи на простые ООП обьекты.
Надеюсь, мне удалось показать, как средствами ООП ограничить корректные состояния бизнес-логики при помощи типов.
Хмм… вы показали какой-то код, но так как постановка задачи непонятно где, то сложно сказать справились вы или нет.

Придирки:

  • Имена приватных полей с подчеркиванием в конце?
  • ContactUI лучше чем ContactUi
Что за «проектирование типами»? Первый раз слышу термин.

Type Driven Development

Что за «проектирование типами»?

Как было отмечено выше, Type Driven Development. У Влащина есть серия на эту тему "Designing with types", из которой и были портированы примеры.


Что такое «доменные типы»?

Имеется ввиду, что это типы, выражающие специфику предметной области. То есть можно было бы везде использовать string для хранения адресов электронной почты, но не всякая строка является адресом электронной почты, и чтобы это выразить вводится "доменный" тип.


так как постановка задачи непонятно где

Во-первых, задача ставилась в статье, на которуюя я ссылаюсь в самом начале этой статьи и из которой были портированы примеры. Во-вторых, задча ещё раз указана начале раздела "Создаём контакт":


Итак, код должен выражать правило "Контакт должен содержать адрес электронной почты или почтовый адрес (или оба адреса)".


Имена приватных полей с подчеркиванием в конце?

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


ContactUI лучше чем ContactUi

Дело вкуса.

Про TDD, который test driven development, слышал, про ваш — нет, но по крайней мере «type driven development» гуглится, в отличие от «проектирование типами», спасибо. За доменные типы тоже спасибо, знал, просто забыл.

Насчет постановки задачи, ее не понять, если не быть в теме, потому и спросил. Но тема для меня лично не выглядит интересной, т.к. то что вы делали (из того что я понял) — весьма очевидное, я бы тоже сделал «как-то так», но т.к. в статье нет сравнения с «неправильным» кодом — поучиться чему-то увы не получилось.

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

Подчеркивания в конце имени поля — ни разу не встречал, не знаю о каком автодополнении вы говорите (в какой среде/редакторе), даже не задумывался об этом в VS. Довольно уверенно могу рекомендовать использовать подчеркивания в начале, т.к. сложилось впечатление, что вы используете правила из других языков.

По поводу именования, есть guidelines, это не обязательные, но все же рекомендации, ContactUI — правильнее в C#.

Конкретно эта статья и оригинал статьи на F# это типичный пример того как категорически нельзя писать бизнес логику. И то как, к большому сожалению, многие пишут бизнес логику.
Проблема этого кода в том что пока два типа Post, Email у тебя получается всего три варианта возможных значений: PostOnly, EmailOnly, PostAndEmail, и соответственно если бизнес попросит добавить третье значение то образуется "комбинаторный взрыв" Phone, Email, Post, PhoneAndEmail, PhoneAndPost, EmailAndPost, PhoneAndEmailAndPost. А в примере на C# на каждый из них надо модифицировать тонну C# код с наследованием, визиторами и т.д. и т.п. А если бизнес попросит 4-тый (рабочий адрес/телефон)? 5-тый?


Автор сам делает развитие этой мысли и решение в следующей статье: https://fsharpforfunandprofit.com/posts/designing-with-types-discovering-the-domain/


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

UFO just landed and posted this here

А "бросать" эксепшны в доменном слое – это норм?

Sign up to leave a comment.

Articles

Change theme settings