• Что такое Доменный объект и Предметная область, простым языком?

    @deliro
    Предметная область — множество всех сущностей и их отношений в рамках контекста.

    Например:
    В рамках магазина это покупатели, продавцы, товары, цены, скидки, поставщики, процесс продажи, прихода на склад, возврата и т.п.
    В рамках дискорда это пользователи, серверы, комнаты, группы, права на доступ, процессы присоединения к серверу, входа в комнату, оплаты nitro, написания текстового сообщения и т.п.

    В целом, это всё, что понимает твой менеджер и ничего, что он не понимает. В предметную область не входит, например, то, где именно ты хранишь данные — в mysql, в файлах, получаешь и сохраняешь их по API или хранишь их в redis'е. Но входит абстрактная сущность "хранилище". Она же — интерфейс репозитория. Репозиторий — это паттерн, который скрывает реализацию конкретного хранилища и который оперирует объектами доменной модели — элементами предметной области, как будто они хранятся у тебя в оперативной памяти. Например, у репозитория могут быть методы
    users.getByID(1234), users.save(user), users.getByEmail("hello@world")
    и т.п. Реализация репозитория определяет, куда именно сохранится этот user или откуда он скачается. В одну таблицу или в несколько. Нормализованные ли будут данные лежать в РСУБД или денормализованные. Именно здесь можно реализовать запись в master, а чтение с реплик. Или энкодинг в msgpack и передачу его по API куда-то. Или по gRPC. Потому что предметная область не должна перегружаться деталями реализации хранилищ.

    Про доменную модель:
    Словарь (он же хэш, он же ассоциативный массив) — это не доменная модель (хотя в некоторых функциональных языках может быть). Объект ORM — это тоже не доменная модель, хотя много где пытается ей быть. Доменная модель не зависит от конкретных фреймворков, баз данных, оптимизаций этих баз и прочих навязанных технической составляющей сущностей. Чаще всего это обычный класс на языке, на котором пишется код (или struct в случае Golang).

    Недавний пример:
    У сущности есть теги по категориям:
    {"category1": ["foo", "bar"], "cat2": ["foo", "hello", "world"]}
    . Их так удобно представлять на уровне предметной области, об этой структуре я могу общаться с заказчиком. Но в монге они сохраняются в виде
    ['category1%%foo', 'category1%%bar', 'cat2%%foo', 'cat2%%hello', 'cat2world']
    , потому что так их легче индексировать и быстрее по ним искать. Но это скрыто в реализации репозитория, доменная область про это ничего не знает. Это даёт сразу много преимуществ:
    1. Это очень просто тестировать, без всяких моков. Вместо репозитория с монгой я делаю репозиторий, который хранит сущности в оперативе. Всё, тесты бизнес-логики не зависят от инфраструктуры. А репозиторий монги я тестирую отдельно, мокая запросы в саму монгу или даже не мокая
    2. Это облегчает понимание всего продукта. Мозг не пытается составить полную картину, когда запросы в БД перемешаны с логикой, тут же сплиты-джойны строк, какие-то ещё низкоуровневые действия. Мозгу легко ориентироваться в пределах уровня. На уровне репозитория я думаю о том, как сущности хранить и доставать из хранилища, на уровне служб я думаю о том, как сущности взаимодействую и какие их них нужно извлечь/сохранить, но не как
    3. Рефакторинг очень простой. Даже теоретическая смена БД. Завтра с монги нужно будет перенести одну сущность на постгрес — без проблем. Я напишу один новый репозиторий, не затронув ни строчки кода за его пределами, кроме места, где он создаётся. При этом, остальные сущности могут лежать всё также в монге

    Доменные модели бывают богатыми и анемичными (но не бескровными). Оба подхода применяются и, имхо, не является антипаттерном ни один из них. Лично я использую анемичные модели, а всю бизнес-логику храню в службах.
    Ответ написан
    2 комментария
  • Django DRF. Из-за чего возникает ошибка при использовании метода POST и PATCH?

    @deliro
    Это разные content types. django.contrib.contenttypes — это метаинформация для внутренностей джанги.

    Content-Type, который тебе нужен — это HTTP header. POST запрос с формой требует один из трёх Content-Type'ов: application/json, multipart/form-data или application/x-www-form-urlencoded в зависимости оттого, что ты отправляешь.
    Ответ написан
    Комментировать
  • Как контролировать сколько было создано экземпляров класса?

    @deliro
    class X:
        _created = 0
    
        def __init__(self):
            X._created += 1
    
    for _ in range(10):
        X()
    
    print(X._created)
    Ответ написан
    Комментировать
  • Фильтрация в списке словарей по динамическим критериям?

    @deliro
    Создать новый список и добавить туда элементы, которые подходят под предикат. Типичный map/filter/reduce

    def filter_by(self, data: list, param: dict) -> list:
        result = []
        for el in data:
            matches = True
            for name, val in param.items():
                if el[name] != val:
                    matches = False
                    break
            if matches:
                result.append(el)
        return result


    Или однострочником

    def filter_by(self, data: list, param: dict) -> list:
        return [el for el in data if all(el[name] == val for name, val in param.items())]


    rule of dumb: если метод/функция меняет свой аргумент, она не должна его возвращать (обычно это значит, что она вообще ничего не должна возвращать), имя метода/функции при этом должно явно говорить, что аргумент меняется (populate, remove_smth, drop_smth). Если метод/функция возвращает что-то, она не должна менять аргументы.

    Но нужно понимать, что сложность создания нового списка — O(n) [при условии, что param достаточно мал и не растёт с увеличением data], а сложность выкидывания элементов по индексу — O(n**2) [потому что сложность операции .pop(idx) — O(n), в цикле по data — ещё O(n), итого O(n**2)]
    Ответ написан
    Комментировать
  • Как сделать это быстрее?

    @deliro
    Не перемешивай массив, а бери из него элементы по индексам, которые у тебя есть.
    Ответ написан
    Комментировать
  • Redis есть ли зависимость производительности кеша от его объема?

    @deliro
    https://redis.io/commands/GET O(1) — константное время, не зависит от размера базы
    Ответ написан
    Комментировать
  • Как объединить два списка (с определенной последовательностью чисел), сохранив этупоследовательность, в Python?

    @deliro
    Есть.

    1. Сложить два списка, результат отсортировать. Сложность O(n*log n)
    2. «Слияние отсортированных списков». Алгоритм нагуглить, написать самому. Сложность O(n+m)

    Но если списки не размером по несколько миллионов, проще и быстрее первый вариант
    Ответ написан
    Комментировать
  • Стоит ли использовать Django при разработке относительно небольшого сайта?

    @deliro
    Всегда удивляли и удивляют доводы против джанги на тему "ну это же комбайн". Да, комбайн, и чё? Что значит "не слишком ли массивен"? Не вместится на впску? Да вроде вместится, не переживай. По этой логике, массивен тут скорее питон, потому что интерпретатор с зависимостями весит 100+мб. Или логика "массивен = лагает"? А у твоего лендинга будет нагрузка в две тысячи запросов в секунду и больше, чтобы переживать?

    Функциональность фреймворка должна быть надмножеством требуемой тебе функциональности. И чем больше функциональности сверх твоих требований, тем больше места для манёвра ты себе оставляешь. Остальные доводы неуместны.
    Ответ написан
    Комментировать
  • Как запустить одновременно две бесконечные функции?

    @deliro
    target=lambda: main()
    target=lambda: executor.start_polling(dp)
    Ответ написан
    Комментировать
  • Что это за синтаксис?

    Комментировать
  • Как данную строку преобразовать в русский язык?

    @deliro
    import urllib.parse
    s = '%D0%B7%D0%B0%D0%B3%D0%B0%D0%B4%D0%BA%D0%B8'
    urllib.parse.unquote(s)
    Ответ написан
  • Node js в серверном исполнении?

    @deliro
    Реально сделать на чём угодно. Но нода не очень-то любит "вычисления", если они CPU. Особенно неудобно она масштабируется на ядра процессора. Если у тебя 95% — IO задачи, то нода вполне ок. Но с её деплоем на клиенты тоже будут проблемы (т.к. нужна нода, sic!). Самым простым решением видится golang, т.к. он отлично скейлится на ядра, быстрее ноды и поставляется единым бинарником без зависимостей, изучается за неделю, если переходить с других языков.
    Ответ написан
  • Как перенести таблицу пользователей с сохранением возможности авторизации?

    @deliro
    Да. Для этого в каждом password hash есть информация об алгоритмах, соли, количестве итераций

    Например: sha1$15000$mysalt$<результат хэша>, bcrypt$3$...

    В коде появляется список хэшеров, хэш сплитится по символу (например $) и по алгоритму подбирается хэшер. Он занимается дальнейшей обработкой. Плюс есть дефолтный хэшер, который используется для всех новых паролей.

    Если у тебя сейчас все хэши в одном алгоритме, то задача простая — добавить префикс из того самого sha1$15000$mysalt$

    Причём, можно автоматом мигрировать базу пользователей на новые хэши. Для этого после логина нужно иметь информацию о хэше и если он не является актуальным, то менять хэш в базе. На этой стадии твой бэк будет знать пароль пользователя.
    Ответ написан
    2 комментария
  • Как распарсить json файл?

    @deliro
    1. 20мб — это вообще не бигдата. Бигдата — это 20тб
    2. В JSONе в основном массиве 3к элементов — это копейки. json.load у меня отработал за доли секунды
    3. Подозреваю, что основная проблема в том, что условие if ret< 54.532 and ret >56.5343: никогда не выполнится. Потому что не существует числа, которое будет меньше 54 и больше 56 одновременно
    Ответ написан
    2 комментария
  • Можно ли стать android-разработчиком без технического образования? Какие разделы высшей математики понадобятся в работе?

    @deliro
    Разделы высшей математики мало каким бэкендерам нужны, не говоря о мобильных разработчиках. В ВУЗе преподают мало из того, что требуется в реальной работе, поэтому его наличие/отсутствие редко на что-то влияет непосредственно. А если вдруг что-то понадобилось, это легко найти и изучить. Даже высшую математику.

    Так что нет никаких преград, чтобы стать мобильным разработчиком, кроме собственного желания и лени.
    Ответ написан
    3 комментария
  • Как лучше деплоить PHP в minikube/kubernetes?

    @deliro
    Твой образ с приложением должен быть самодостаточным (self-contained) и лишь должен получать извне конфиги для запуска — секреты к БД и всё такое (12factor). Маунтить папки — это для разработки, но не для прода.
    Ответ написан