Комментарии 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);
}
И здесь возникает второй важный вопрос: а как определять границы ответственности?
Я пришёл к тому, что хорошая граница - это ситуация, когда часть системы можно описать как отдельную задачу или роль. Например:
работа с базой данных;
отправка почты;
бизнес-логика создания пользователя.
Если модуль можно заменить другой реализацией, не переписывая остальную систему, то, скорее всего, граница выбрана удачно. Слои и структура каталогов - уже следствие этих решений.

База по архитектуре приложений для начинающих разработчиков ПО