Как стать автором
Обновить

Комментарии 27

НЛО прилетело и опубликовало эту надпись здесь
Спасибо. Если правильно понял ваш вопрос, то это уже отдается на откуп разработчику. Например в демо проекте в домене находится интерфейс репозитория, тогда как его реализация (где уже используется ORM) находится в инфраструктурном слое. Таким образом уровень домена об ORM ничего не знает (с небольшой оговоркой т.к. в домене, как ни крути, будет зависимость на коллекции).
НЛО прилетело и опубликовало эту надпись здесь

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

часто в последнее время наблюдаю картину: берут люди hibernate (сам пишу на java), поначалу всё красиво, а потом появляются сложные запросы, кастомные маппинги и прочее. в итоге переходят на что-то низкоуровневое. так ли нужен очередной orm?

О чем я и написал в статье, если знаете зачем вам orm — берите, в противном случае, как и с любым другим инструментом, будут проблемы.

Если в DataMapper ORM бояться кастомных маппингов, то особо и не нужна она значит.

Это интересный вопрос. В каких случаях нужны кастомные маппинги?

почти всегда, когда приложение чуть сложнее простого CRUD
Как правило, и для «простого CRUD» нужны кастомные маппинги.

Работал с приложениями сложнее простого CRUD, такой необходимости не возникало. Разве что пару раз была необходимость сделать поле в виде массива.

Когда доменная модель плоховато ложится на реляционную стандартными. А такое почти всегда :(

А можете пример привести? Может я как-то неправильно представляю "кастомный маппин". Если это типа 1 поле в коде отобразить на 2 в базе, то мне кажется, это просто неправильная архитектура. Иногда да, есть недостаток выразительности БД, но это довольно редкая ситуация, чаще всего связанная с массивами.

Именно одно поле в коде отобразить на два в базе. Ну вот, например, банальная денежная сумма: собственно сумма и валюта. Или id валюты в связанной таблице. Product.priceAmount и Product.priceCurrencyName в объектной модели так себе идея.


Датавремя и часовой пояс (не секрет, что в некоторых базах с часовыми поясами всё очень плохо).

А, ну составные value objects, тут согласен. Это примерно то же, что и хранить массив в поле. Хотя отдельные поля price и currency_id вполне нормальное решение, я бы и не подумал заморачиваться тут с кастомным маппингом.

А если у нас есть и price, и cost, причём могут быть в разных валютах, которые надо как-то друг с другом связывать. предавать в какие-то сервисы doSmth(price, price_currency_id, cost, cost_currency_id). Для меня нарушение инкапсуляции налицо :)


Или ещё пример. Было поле какое-то, стандартный маппинг. Всё хорошо, объекты соотвествуют бизнес-модели и хранятся в базе более-менее удачно. Но подтормаживать начало. Прибегает DBA и кричит "срочно выносите это поле в отдельную таблицу!". Менять объектную модель? Перестанет соответствовать бизнес-модели.

Да нет, почему, связь один-к-одному вполне может быть частью бизнес-модели. Может быть наоборот, изначально модель была слишком упрощена? Слишком много полей в одной таблице может говорить о том, что там смешано 2 сущности.

Считаем, что сущности выделены идеально :) Чисто техническая претензия DBA, типа в одном из полей тяжёлые блобы) И просит их вынести, чтоб не бэкапить в основном цикле

Согласен. Но в принципе я тут не вижу причин считать одну реализацию правильнее другой. Если это изображение или вектор координат, у нас все равно будет какой-то специальный value-тип для него, как составной для денег. С отдельной сущностью будет не value, а reference-тип, вот и вся разница.

Это технически разница не особая, но для того же DDD между Entity и ValueObject разница определяется предметной областью и контекстом, а не удобством для DBA ) Если, конечно, не инструмент для администрирования DB разрабатываем

ORM-ы такие ORM-ы


Много ненужных абстрактных названий Repository, Session, entity, которые программист вынужден учить, много "магии"


userRepo.Persists(ctx, user2)
session.Flush()

которую тоже нужно знать. Магия подозрительно простовая кстати, где же


if err := userRepo.Persists(ctx, user2); err != nil {}

в Go без этого никак
Отдельный язык


d3:"many_to_many:<target_entity:User,join_on:u1_id,reference_on:u2_id,join_table:lw_friend>"

который опять надо учить, и хорошо если в runtime он проверяется, а скорее всего нет. Это опять ведет к жестокой магии и многочасовым поискам ошибок


кодогенерация вместо рефлексии

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


Ну и в результате всех этих высоких абстракций все заканчивается переизобретением старого доброго SQL


userRepo.Select().AndWhere("id", "=", 1)

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


Короче вообще не очевидно что так лучше чем просто select-ы на старом добром SQL


rows, _ := db.Query(`SELECT * FROM users where id = 1`)
defer rows.Close()
if rows.Next() {
    user := User{}
    _ = rows.Scan(&user.Id, &user.email)
}
Много ненужных абстрактных названий Repository, Session, entity, которые программист вынужден учить, много «магии»

Никто никого учить не заставляет, но, если так уж сложилось, что вы знаете что это за названия (а знания эти далеко не тайные) и хотите их использовать в GO то эта статья может быть полезной.

Про переизобретение SQL и тд отвечать не буду, вы пытаетесь в очередной раз завести великую битву ORM vs NO ORM, мне это не интересно. Для меня оба подхода — инструмент, их можно изучить и пользоваться по месту.
Какая-то нечестная статья, по заголовку, ожидал примеры архитектурных или хотя бы тактических решений проблем, с которыми сталкиваются go-разработчики при попытках внедрить DDD, вместо этого реклама еще одной недоделанной ORM.
Чем вам GORM не подошел?
Какая-то нечестная статья, по заголовку, ожидал примеры архитектурных или хотя бы тактических решений проблем, с которыми сталкиваются go-разработчики при попытках внедрить DDD, вместо этого реклама еще одной недоделанной ORM.


За обман ожиданий конечно извиняюсь. Но давайте немного конкретики, я смотрю на написанное и вижу такую структуру:
0. Общее описание проблематики.
1. Небольшой референс к материалам на которые опираюсь.
2. Описание 3х DDD-шных паттернов с примерами, выделение четких проблем.
3. Описание решения проблем через собственную ORM (что логично ведь источником проблем в пункте 2 я выделил именно отстутвие подходящего инструментария).

Ну и вывод в конце, по вашему не так?
Чем вам GORM не подошел?

— нет логических транзакций
— не может сохранить граф объектов (аггрегат)
— нет lazy/eager loading
— встраивание gorm.Model
— и прочие вещи исходящие из того что gorm и прочие GO-orm не ставят перед собой цели работать с доменными сущностями а не просто со структурами
Ну вот например, в книге Фаулера «Шаблоны корпоративных приложений» описание механизмов работы с БД занимает 4 главы или 180 страниц, половина из них — жесткие листинги кода. А представляете обратную ситуацию: вы покупаете книгу, а там полстраницы описания общеизвестных фактов и в конце ссылка на гитхаб?
Из конкретики: объектная модель в go несколько отличается, от таковой в PHP, Java или C#, отсюда и особенный go-style, поэтому как раз самое интересное, это то как вы обходили эти различия: инъекции зависимостей, наследование, отсутствие generic + строгая типизация. Плюс go предполагает широкое использование параллельности, как в таких условиях работает ваш UoW и IM?

А как у вас реализованы транзакции в хранилищах, которые не поддерживают их нативно?
нет логических транзакций

Это предполагает инъекции разных репозиториев в UoF? Как?
не может сохранить граф объектов (аггрегат)
нет lazy/eager loading

А что вместо этого?
встраивание gorm.Model

То есть общий принцип:
1. Решил реализовать в go стандартный паттерн X
2. Столкнулся с такими-то проблемами
3. Решил их так-то

Тогда и полезность статьи сразу возрастает — кто-то увидит решение собственной похожей проблемы, и фидбек по правильности решений сразу получите.
Ну вот например, в книге Фаулера «Шаблоны корпоративных приложений» описание механизмов работы с БД занимает 4 главы или 180 страниц, половина из них — жесткие листинги кода. А представляете обратную ситуацию: вы покупаете книгу, а там полстраницы описания общеизвестных фактов и в конце ссылка на гитхаб?


Боюсь у меня и Фаулера разные задачи и ресурсы. По поводу общеизвестных фактов — очевидно, это не так, ознакомьтесь с материалами в дисклеймере.
Ссылка на гитхаб по той же причине — я не ставлю цель рассказать об ORM, я хочу показать проблему и показать как ее решить. Если кого то заинтересует — вот ссылка на гитхаб, можно прочитать подробнее.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Изменить настройки темы

Истории