Обновить
1
Коновалов Павел@ljadrbln

Фулстек-разработчик, PHP+JS.

1
Подписчики
Отправить сообщение

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

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

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

Сначала казалось, что архитектура - это просто аккуратная структура проекта: controllers, services, repositories и т. п. Разложил код по слоям - и вроде бы архитектура уже есть.

Но со временем пришёл к мысли, что основная идея всё-таки про зависимости и независимость компонентов.

Например, довольно типичная ситуация:

function createUser(string $email): void
{
    $db = Database::instance();
    $mailer = Mailer::instance();

    $db->insertUser($email);
    $mailer->sendWelcomeEmail($email);
}

На первый взгляд всё аккуратно и удобно, но у функции появляются скрытые зависимости - база данных и почтовый сервис. Они не видны в сигнатуре, из-за чего становится сложнее тестировать код или менять реализацию.

Если сделать зависимости явными, картина меняется, и код становится гораздо прозрачнее:

function createUser(Database $db, Mailer $mailer, string $email): void
{
    $db->insertUser($email);
    $mailer->sendWelcomeEmail($email);
}

И здесь возникает второй важный вопрос: а как определять границы ответственности?

Я пришёл к тому, что хорошая граница - это ситуация, когда часть системы можно описать как отдельную задачу или роль. Например:

  • работа с базой данных;

  • отправка почты;

  • бизнес-логика создания пользователя.

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

Можно попробовать вместо Age использовать AgeBin - возраст в бинах.

В Titanic влияние возраста скорее пороговое (дети, взрослые, пожилые), а не линейное, плюс много пропусков. Бины часто дают более стабильный результат для RandomForest и меньше чувствительны к способу заполнения возраста.

У меня она выглядит вот так:

def add_age_bin(
    train: pd.DataFrame,
    test: pd.DataFrame,
) -> Tuple[pd.DataFrame, pd.DataFrame]:
    """
    Заполнить Age и добавить категориальный признак AgeBin.
    Бины: 0-12, 12-20, 20-40, 40-60, 60-100.
    """
    train = train.copy()
    test = test.copy()

    age_median = train["Age"].median()
    train["Age"] = train["Age"].fillna(age_median)
    test["Age"] = test["Age"].fillna(age_median)

    age_bins = [0, 12, 20, 40, 60, 100]
    age_labels = ["child", "teen", "adult", "mature", "senior"]

    train["AgeBin"] = pd.cut(
        train["Age"], bins=age_bins, labels=age_labels, include_lowest=True
    )
    test["AgeBin"] = pd.cut(
        test["Age"], bins=age_bins, labels=age_labels, include_lowest=True
    )

    return train, test


Хороший вводный материал, спасибо.
В статье вы рекомендовали использовать multi-stage build. Было бы интересно увидеть сравнение размеров образов до и после.

Спасибо, хороший пример с разными ролями и сценариями.

Тогда попробую зафиксировать границу, чтобы проверить, правильно ли я вас понял.

Я для себя обычно формулирую так:
Domain отвечает за то, что допустимо в предметной области (инварианты, политики, правила),
Application - за то, когда, кем и в каком порядке эти правила применяются.

Поэтому для меня тревожный сигнал, если Domain-сервис начинает:

  • знать о ролях пользователей,

  • различать use case'ы,

  • принимать решения о последовательности шагов.

На мой взгляд, в этот момент он уже превращается в 'скрытый use case', просто лежащий не в том слое.

Вопрос: есть ли у вас практические стоп-сигналы, по которым вы понимаете, что доменный сервис пора упрощать или дробить, чтобы он не начал оркестрировать сценарии?

Спасибо за статью. Вопрос из практики.

Часто сталкиваюсь с ситуацией, когда Domain начинает разрастаться, и граница между доменной логикой и прикладными сервисами становится размытой.

Например: есть доменная сущность Order и правило пересчёта итоговой суммы с учётом скидок, налогов и внешних ограничений (лимиты, акции).
Формально это бизнес-правило, но часть данных и решений приходит из Application / Infrastructure.

В таких случаях вы оставляете логику в Domain (через абстракции/политики) или предпочитаете выносить оркестрацию в Application?
Есть ли у вас практический критерий, по которому вы принимаете это решение?

А как изменилась ваша повседневная работа после появления Copilot с его рассуждениями? Легче ли стало находить узкие места, обучаться, отлаживать?

Да, вы правы.

Я думал об этом перед началом эксперимента.
Разработку с агентом решил отложить по двум причинам:

  1. Что он начнёт механически выполнять задачи цепочкой, и мелкие ошибки будут накапливаться.

  2. Что когда мне будут нужны уточнения по каким-то решениям, то агент не сможет их дать и подробно всё разжевать.

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

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

Для одноразовых задач примеров со StackOverflow было предостаточно.
Например, нужно было один раз вытащить из огромного лога все строки между двумя таймстемпами и быстро вывести их в читаемом виде. Гуглишь "grep between two dates", находишь на SO связку из awk и регулярки, копируешь, запускаешь и больше никогда про неё вспоминаешь.

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

Вы знаете, я тоже сначала пытался вести краткий список договорённостей и подгружать его перед каждой сессией, но за этим трудно следить. Однажды, решил попробовать загрузить весь проект целиком. Модель задумалась на 8 минут и выдала нужный результат. Ждать каждый раз столько времени я не готов :)

Спасибо! Очень приятно слышать такую оценку, особенно от коллеги по цеху.
Я стараюсь, чтобы задания были не просто красивыми, а действительно полезными детям.

Если когда-нибудь решите сделать свои развивающие игры - уверен, дочке понравится, а нам будет интересно посмотреть.

Не за что!

Рад, что вам понравилось, занимайтесь на здоровье!

Звучит интересно, спасибо.
Вы уже продумывали механику подбора заданий под уровень ребёнка или планируете поручить это ИИ, чтобы он подстраивал сложность сам?

Скрытых смыслов в названии нет - не переживайте.

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

Спасибо, занимайтесь с удовольствием! А про идею расскажете?

Информация

В рейтинге
Не участвует
Зарегистрирован
Активность

Специализация

Фулстек разработчик
Старший
JavaScript
PHP
Проектирование архитектуры приложений
Создание архитектуры проектов