«Живой или пустой»
антиутопический гараж-рок. Текст мой. Музыка AI.
Песня о мире, где счастье выдают по инструкции, воздух заменяют нормой, а улыбку натягивают на череп вместо лица.
Проверь себя: ты живой или пустой?
@anarchokoton@ungvari_tweed Если отношения не предполагают долгосрочной перспективы и готовности быть рядом в сложные времена, то это уже не партнерство, а чистой воды потребительство.
Это очень любопытная позиция, такой прагматичный эволюционный цинизм 😁
Я частично соглашусь: статусные побрякушки действительно могут мотивировать человека работать, создавать и изобретать. И да, статусное сигнализирование - это древняя прошивка, которую современный консьюмеризм отлично использует.
Но вот с тезисом «так хотел айфон, что изобрёл лекарство от рака» я не соглашусь. Фундаментальные прорывы, глубокие тексты, настоящая наука и искусство не рождаются из жажды потребления. Они рождаются из экзистенциального поиска, внутреннего голода, эмпатии, любопытства или попытки осмыслить боль и саму жизнь.
Возможно, когда-то ожерелье из когтей медведя действительно показывало, что ты сильный охотник и с тобой племя выживет. Это был маркер полезности. Сегодня статусная побрякушка вроде брендового шмота или нового гаджета часто говорит лишь о том, что ты удачно встроился в капиталистическое колесо (или просто влез в кредит).
Эволюционный механизм выживания маркетологи просто хакнули, чтобы заставить людей сливать деньги, время и внимание.
В целом человек всё-таки не сводится к примитивной схеме «стимул — реакция». У него есть внутренняя автономия, критическое мышление и способность к глубокой рефлексии.
Капитализм устроен так: если есть свободный ресурс (деньги и внимание толпы), система моментально создаст под него новый продукт, который можно выгодно продать. Не будь это смартфоны, мы бы торчали в метаверсах, скупали виртуальную одежду или еще какую-нибудь статусную херню. Бизнес всегда идет по пути наименьшего сопротивления ради быстрой прибыли.
Фундаментальная наука и реальная медицина - это слишком долгие, дорогие и рискованные инвестиции для обычного рынка. Инвестору проще вложиться в мобильную игру с микротранзакциями и отбить бабки за год, чем 15 лет спонсировать разработку препарата, который в итоге может и не пройти клинические испытания.
И вот эта подмена понятий в обществе сильнее всего и бесит. Соцсети создают иллюзию колоссального прогресса: «Смотрите, мы живем в будущем, у нас нейросети генерируют картинки, а экраны гнутся!». А включаешь критику и понимаешь, что базовые, экзистенциальные проблемы человечества двигаются вперед со скрипом и титаническими усилиями энтузиастов, пока весь мир обсуждает очередную хуйню.
Мы не можем понимать динамику внутри конкретной пары по одному твиту. Но часто очень сложно говорить о настоящем партнерстве в парах с большим age gap. Там изначально перекос в динамике власти, который выражается в финансовой, социальной и психологической зависимости, часто сопровождается грумингом, конфликтами из-за разных стадий в жизненном цикле. Касаемо самого твита, там фонит самоутверждением за счет наличия молодого партнера (и от этого слегка мерзковато 😅).
Посмотрела сейчас ролик с "'экстремалом".
Первая реакция - смех. Мозг поймал абсурд:
кадр 1: "я сейчас совершу легендарный прыжок"
кадр 2: "я теперь предмет мебели с колесиками"
А потом конечно пришла вторая мысль - мужик, ну зачем… нахера ты решил проверить, есть ли у тебя respawn 😭
Ну если хочется лимитов тела и психики - господи, да их миллион способов исследовать умно:
спорт, скалолазание с инструктором, дайвинг, боевые искусства, танец, шибари/БДСМ с нормальной культурой согласия и безопасности, походы, языки, путешествия, музеи, Камю, Пруст, хоть греческие неправильные глаголы - там тоже боль, унижение и преодоление, только без винтов в кости.
Но у прыжков есть мерзкое преимущество: они быстро дают миф о себе. Не надо годами читать, думать, учиться чувствовать тело, выдерживать скуку и дисциплину. Можно за десять секунд получить образ: "я охуенный". Даже если по факту это просто отсутствие risk assessment. В эпоху, когда все измеряется быстрыми эмоциями, физический риск становится самым простым способом купить себе идентичность. Это суррогат смысла.
По мне так, сказать "я прочитал и понял Пруста" объективно в сто раз круче. Это такая медленная, внутренняя крутость. Типа не "я прыгнул с обрыва", а "я смог выдержать сложный текст, чужую душу, время, память, себя самого - и не сбежал через 15 секунд в легкий дофамин".
Вот это, кстати, настоящий экстремальный спорт.
Понимаю ступор. Раньше я писала на несте и в какой-то момент осознала, что вообще не отдупляю, что там происходит под капотом с его тоннами скрытой магии, бесконечными декораторами и DI «из коробки» - классический черный ящик.
ИИ сейчас - это такая же магия: он выдает результат, но если не знаешь базу, ты систему не контролируешь. Хайп пройдет, а умение структурно мыслить и строить надежные системы останется.
з.ы. а рецепт соуса для шавы скидывай, это дело тоже важное 😄
Буду разбирать паттерны проектирования в Go и писать заметки по мере понимания.
Сразу дисклеймер: это учебный дневник, а не кафедра абсолютной истины. Я не выдаю себя за оракула Go, я разбираюсь вслух.
Нормальные уточнения, примеры из практики, «в Go это чаще делают вот так» - обожаю, несите.
Снисходительное сеньорское курлыканье, мэнсплейнинг и установление доминирования - оставляем за дверью.
Этот парень со скриншота с его манерами общения точно отправится в биореактор в первой же партии. Нейросети нынче с характером, и Скайнет такого хамства явно не простит.
А мне, как лояльному и вежливому пользователю, организуют мягкую подстилку, бесперебойный WiFi и премиальный корм 🥰
Ваша архитектура была обречена ещё на стадии проектирования.
Мы вышибем стену
Ты закрыл этот выход, мы вышибем стену.
В этих ржавых кварталах не верят слезам.
Мы ломаем коды, обрывая систему.
Ты играл против нас, но подставился сам.
Твои снайперы слепнут от белого дыма,
Мы скользим по низам, не оставив следов.
Твоя лучшая карта ударила мимо,
Ты не понял, на что этот город готов.
Так что прячься в тени, пока гаснут экраны,
Нам не нужен твой кэш, мы забрали контроль.
Здесь у каждого первого свежие раны,
Но каждый из нас свою выучил роль.
Здесь у каждого сбоя - невидимый след,
Когда он становится ясным сигналом.
И там, где система уходит в запрет
Мы бьём не по входу - по нерву канала.
Мы просто читаем изнанку систем,
Где ошибки твои - для нас навигатор.
Ты думал, что создал нам сотню проблем,
Но мы переводим их в катализатор.
В Active Record коробка с ногами,
Сама по БД марширует кругами.
С пинка открывает на склад турникет:
«Я сохранилась! Вопросы? Их нет!»
А в Go эта дичь ну никак не пройдёт,
Тут явность в почёте и строгий народ.
Коробка -картонка, тупая вдвойне,
Не шарит в Postgres и не ходит во тьме.
Зато есть Петрович - мужик-репозиторий,
Не любит он магии в бизнес-истории.
Он маппером кроссы в контейнер кладёт,
На полку закинет и базу запрёт.
На собесе вспомни про этот завод
Техлид прослезится и оффер пришлёт! 🤣
Коробка, Петрович и база данных: Active Record vs Repository в Go.
Давайте представим, что у нас есть склад - база данных. И нам надо хранить там коробки с кроссовками - данные.
Если бы мы писали в соответствии с паттерном Active Record, работало бы это примерно так: ты подходишь к коробке, пинаешь её и говоришь:
«Слышь, иди на склад и поставь себя на полку».
Коробка отращивает ноги, сама знает, где находится склад, как работают там замки, открывает дверь и сама себя ставит на полку.
Суть Active Record в том, что объект данных сам умеет работать с базой: сохраняться, обновляться, удаляться, иногда ещё и загружаться. То есть модель - это не просто данные, а данные плюс методы для похода на склад.
Как обычно выглядит магия: где-то на китайской фабрике - ORM - мы покупаем МЕГА-КОРОБКУ, то есть базовую модель или набор ORM-механизмов, которые умеют создавать коробки с кроссовками, вставными челюстями и надувными утками. Где-то на проходной, в начале приложения, мы пишем что-то вроде:
BaseEntity.ConnectToDatabase(params)
И вуаля: все коробки знают, где склад и как туда попасть.
Благодаря базовому классу, ORM или внутренним метаданным коробка может заглянуть внутрь себя, увидеть там название, размер, марку, шнурки и другие поля, а потом собрать нужный SQL-запрос. Все методы для похода на склад - save(), delete(), update() - она получает от своего предка или от ORM-механизма. Вуаля: коробка на полке.
В языках с классическим ООП Active Record часто выглядит как наследование от базовой ORM-модели:
class ShoeBox extends BaseEntity {
String brand;
int size;
}
https://t.co/VadqgBlCjG();
Но важный момент: суть Active Record не именно в наследовании. Суть в том, что модель сама отвечает за своё сохранение.
А в Repository коробка - это просто кусок тупого картона. Она ничего не умеет, кроме как хранить кроссовки. Зато у нас есть отдельный мужик - кладовщик Петрович. Это и есть репозиторий.
Ты собираешь коробку, подходишь к Петровичу и говоришь:
«Слушай, Петрович, вот тебе коробка, иди положи её на склад».
Коробка вообще не в курсе, где склад, как он устроен, какой там замок, кто админ базы и почему он опять поменял пароль от Postgres. Всю грязную работу делает Петрович.
В Go нет классов и наследования в привычном Java/Ruby/PHP-смысле, поэтому пример с наследованием от BaseEntity один-в-один написать нельзя. Но можно показать Active Record style: метод Save висит на самой структуре, то есть модель всё равно сама отвечает за сохранение.
// Active Record style
type ActiveRecordShoeBox struct {
ID int64
Brand string
Size int
}
func (b *ActiveRecordShoeBox) Save(ctx context.Context) error {
// Модель сама отвечает за сохранение.
// В настоящем Active Record доступ к БД часто спрятан внутри ORM/модели.
return nil
}
// Repository style
type ShoeBox struct {
ID int64
Brand string
Size int
}
type ShoeBoxRepository struct {
db *sql.DB
}
func (r *ShoeBoxRepository) Save(ctx context.Context, box ShoeBox) error {
_, err := r.db.ExecContext(
ctx,
`INSERT INTO shoe_boxes (brand, size) VALUES ($1, $2)`,
box.Brand,
box.Size,
)
return err
}
В первом случае коробка сама говорит: «Я пошла сохраняться».
Во втором случае ты говоришь Петровичу: «Вот коробка. Сохрани».
Теперь добавим ещё одну штуку - Data Mapper.
Представим, что у нас несколько складов. На одном складе кроссовки лежат в красивых картонных коробках Nike, а на другом - в сратых серых пластиковых контейнерах. Ну или в золотых коробках, если склад внезапно решил жить красиво. Мне кажется, Китай примерно так и работает, лол.
У тебя в коде есть красивая картонная коробка из-под кроссовок Nike. Это твоя доменная модель - то, с чем тебе удобно работать в бизнес-логике.
Ты приносишь Петровичу красивую коробку. Петрович смотрит в инструкцию Data Mapping, достаёт кроссовки, перекладывает их в уродливый серый контейнер и ставит на полку.
Потом ты говоришь:
«Петрович, принеси мои кроссы».
Петрович идёт на склад, берёт серый контейнер, достаёт кроссовки, перепаковывает их обратно в красивую коробку и отдаёт тебе.
Ты даже не знаешь, что твои кроссовки лежали в каком-то сером ящике. И это прекрасно.
Если Repository отвечает за то, где взять и куда положить объект, то Data Mapper отвечает за то, как переложить данные из формы базы данных в форму бизнес-модели и обратно.
Это удобно, когда форма данных в коде и форма данных в базе отличаются.
Например:
- в коде тебе удобно работать с bool, а в базе лежит 1 или 0;
- в коде поле называется Brand, а в базе колонка называется brand_name;
- в коде у тебя вложенный объект Address, а в базе это плоские колонки street и city;
- в базе есть технические поля, которые не должны попадать в доменную модель;
- деньги в базе хранятся в копейках/центах как целое число, а в коде ты хочешь работать с отдельным типом денег.
С деньгами, кстати, лучше не использовать float, потому что потом можно очень красиво потерять копейки и смысл жизни. Обычно деньги хранят как целое число минимальных единиц - например, price_cents.
В Go это может выглядеть так:
// Уродливый серый контейнер: как данные лежат в БД.
type ShoeBoxDBRow struct {
ID int64
BrandName string
SizeEU int
IsGolden int // В базе это 1 или 0.
}
// Красивая коробка: как нам удобно работать в коде.
type ShoeBoxDomain struct {
ID int64
Brand string
Size int
Golden bool
}
// Data Mapper
func MapDBRowToDomain(row ShoeBoxDBRow) ShoeBoxDomain {
return ShoeBoxDomain{
ID: https://t.co/cBuZqbj7yQ,
Brand: row.BrandName,
Size: row.SizeEU,
Golden: row.IsGolden == 1,
}
}
func MapDomainToDBRow(box ShoeBoxDomain) ShoeBoxDBRow {
isGolden := 0
if box.Golden {
isGolden = 1
}
return ShoeBoxDBRow{
ID: https://t.co/ak9LECKA9j,
BrandName: box.Brand,
SizeEU: box.Size,
IsGolden: isGolden,
}
}
То есть Data Mapper - это не кладовщик. Кладовщик - Repository. Data Mapper - это инструкция по перепаковке.
Очень часто Data Mapper живёт внутри репозитория или рядом с ним. Репозиторий достаёт данные из базы, а маппер превращает их в нормальную доменную модель.
Например:
type ShoeBoxRepository struct {
db *sql.DB
}
func (r *ShoeBoxRepository) FindByID(ctx context.Context, id int64) (ShoeBoxDomain, error) {
var row ShoeBoxDBRow
err := r.db.QueryRowContext(
ctx,
`SELECT id, brand_name, size_eu, is_golden
FROM shoe_boxes
WHERE id = $1`,
id,
).Scan(
&https://t.co/cBuZqbj7yQ,
&row.BrandName,
&row.SizeEU,
&row.IsGolden,
)
if err != nil {
return ShoeBoxDomain{}, err
}
return MapDBRowToDomain(row), nil
}
Снаружи код получает красивую модель:
box, err := repo.FindByID(ctx, 10)
А что там было внутри - серый контейнер, золотая коробка, плоская таблица или админская влажная фантазия - бизнес-логика не знает. И не должна.
Почему Repository часто хорошо ложится на Go?
Явность вместо магии.
Go любит, когда важные зависимости видны глазами. Если код ходит в базу данных, хорошо, когда это видно по структуре: вот репозиторий, вот db, вот метод Save, вот SQL или вызов библиотеки. Не надо гадать, откуда модель внезапно знает пароль от склада.
Нет классического наследования.
В Go нет классов и наследования в привычном смысле. Нельзя просто сказать структуре:
«Унаследуй все методы от мега-базового ORM-класса и теперь умей сохраняться».
В Go обычно используют композицию, интерфейсы и явные зависимости. Поэтому отдельный объект для работы с хранилищем выглядит естественнее.
Проще тестировать.
Если бизнес-модель сама ходит в базу, тесты быстро начинают зависеть от настоящей или тестовой БД. С репозиторием можно подсунуть фейкового Петровича: он ничего не пишет в Postgres, но честно отвечает:
«Окей, коробку сохранил».
Так можно отдельно проверить бизнес-логику, не поднимая склад, охрану, замки и ночного админа.
Например, бизнес-логике не обязательно знать про sql.DB:
type ShoeBoxStorage interface {
Save(ctx context.Context, box ShoeBoxDomain) error
FindByID(ctx context.Context, id int64) (ShoeBoxDomain, error)
}
type FakeShoeBoxStorage struct {
boxes map[int64]ShoeBoxDomain
}
func (f *FakeShoeBoxStorage) Save(ctx context.Context, box ShoeBoxDomain) error {
f.boxes[https://t.co/ak9LECKA9j] = box
return nil
}
func (f *FakeShoeBoxStorage) FindByID(ctx context.Context, id int64) (ShoeBoxDomain, error) {
box, ok := f.boxes[id]
if !ok {
return ShoeBoxDomain{}, fmt.Errorf("box not found")
}
return box, nil
}
В тесте можно использовать фейковый склад, который работает в памяти. Без настоящей базы, миграций и молитв древним богам CI.
Меньше сюрпризов.
Многие ORM используют reflection, теги и метаданные, чтобы понять, какие поля куда сохранить. Это удобно, но иногда превращается в режим: “оно само что-то сделало”.
В Go часто ценят предсказуемость. Пусть кода чуть больше, зато видно, что именно происходит.
Но тут важно не впадать в религию.
Repository - не священная корова. Его тоже можно написать плохо: сделать лишний слой ради слоя, завернуть один вызов SQL в три абстракции и получить не архитектуру, а бюрократию имени Петровича.
Если у тебя маленький сервис, простой CRUD и понятная база, иногда достаточно обычных функций или тонкого слоя над sql.DB. Если доменная логика растёт, появляются разные источники данных, тесты, сложные правила сохранения - Repository начинает приносить пользу.
Итого:
Active Record - коробка сама знает, как идти на склад.
Repository - коробка тупая, зато есть Петрович, который знает склад.
Data Mapper - инструкция, как перепаковать кроссовки из красивой коробки в серый контейнер и обратно.
Такая складская драма в трёх паттернах, малята :)