Обновить

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

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

Да, если только «заказчик» и «разработчик» не являются одним и тем же лицом.

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

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

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

Я попробовал, но получается плохо. Единственный ваш пассаж:

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

который можно взять на вооружение, сформулирован слишком общо. В том то и суть, как именно оформить используемые у меня классы C++ / WTL в виде относительно независимых модулей, со слабыми связями? Ибо, основной проблемой в реализации первой версии программы, стали слишком сильные связи между модулями (классами) приложения. Которые, как мне представляется, моделировали отношения «многие ко многим». А их, как мы знаем из теории баз данных, надо заменять отношениями «многие ко одному» и «один ко многим». Вот в этом направлении я сейчас и работаю. В идеале, собираюсь и статью опубликовать на эту тему с содержательными примерами (по принципу: «В статье только текст, изображения и диаграммы, а весь код должен быть в архивах, поскольку в статьях он воспринимается плохо»).

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

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

Сначала казалось, что архитектура - это просто аккуратная структура проекта: 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);
}

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

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

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

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

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

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

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации