воскресенье, 3 марта 2013 г.

Зачем архитектура (или Мало кода == мало проблем)


Зачем архитектура

Мантры на тему "включите мозг лоси!!!"(с) 

Intro

Несколько лет назад я написал статью “Пособие для главного плотника” (http://habrahabr.ru/post/90880/) в которой я постарался изложить свое видение вопроса - что такое архитектура приложения, какие бывают хорошие и плохие практики ее построения. Судя по отзывам статья общественности понравилась. За время прошедшее с написание статьи фокус у меня сильно сместился в менеджмент и меня вместо вопроса “Как?” стал больше интересовать вопрос “Зачем?” а также вопросы “Сколько это будет стоить?” и “Что будет если это выкинуть нафиг из плана?”.

Legal Disclamer - статью будут читать коллеги так что детали в приводимых примерах несколько изменены дабы не вызывать нездоровой реакции окружающих.

Наверное многим менеджерам знакомо ощущение когда команда говорит нам вот тут надо backbone.js, а вон там мы все делаем через фабрику классов, а вот тут еще вот такая финтифлюшка и все это связывается при помощи Spring IoC, а в БД мы ходим через Hibernate. (небольшое отступление: всякие разные страшные слова типа Spring IoC я беру из миры Java ибо хорошо эту платформу знаю, но для любой другой платформы есть свой не менее объемный список “заклинаний”). Обычно проектного координатора который видит список из 10 архитектурных решений которые совершенно необходимо прикрутить к проекту перед тем как сделать About page возникает ряд вопросов а именно

  1. М-м-м окей, а вы уверены?
  2. Точно надо так много всего для нашего небольшого проекта?
  3. А что будет если выкинуть например библиотеку Х? Не будет работать библиотека Y - А давайте выкинем и Y?
На более поздних стадиях проекта эта же проблема звучит несколько иначе
- “У нас вся верстка сделана на замечательной библиотеке ABC и теперь мы не можем выполнить просьбу SEO консультанта ибо ABC так не умеет”.
Тут уже вопросы ПМ-а звучат так

  1. А что будет если выкинуть ABC? Переделывать 80% работы? ОК.
  2. А обойти как-то можно?
  3. А зачем мы вообще эту библиотеку взяли? Потому что модно?!!!
  4. Весело...

Постараюсь перефразировать. Зачастую с точки зрения менеджера, особенно не технического, “архитектура приложения” часто является неким монстром живущим внутри приложения, питающимся рабочим временем команды и с определенной регулярность доставляющим проблемы всем кто не успеет отпрыгнуть.

К счастью я совершенно точно знаю, что это не так. Архитектура, совершенно точно, бывает хорошая теплая и пушистая. Что впрочем не отрицает существование архитектуры плохой (холодной и не пушистой). Далее в статье я постараюсь показать откуда берется такая дисперсия свойств и что надо делать чтобы у Вас все было хорошо.

Отступление

23 года назад в 1990 году я учился на первом курсе Воронежского Государственного Университета, страна называлась Советский Союз и из компьютерной техники у меня в личном пользовании был только самосборный компьютер ZX Spectrum (http://ru.wikipedia.org/wiki/ZX_Spectrum) с внешней памятью на магнитофонных кассетах. Программы я писал на языке Assembler и вопросы выбора технологии, архитектуры и т.п. меня в принципе не волновали. Памяти было всего 48К и в них одновременно должны были помещаться компилятор, редактор, исходный код и сама программа. Выбор был не велик и развернуться было негде.

Где-то через год у меня появился IBM PC совместимый агрегат и на нем уже можно было делать существенно более сложные вещи. И я их делал, год за годом все сложнее и сложнее. Тут мы подходим непосредственно к ответу на вопрос вынесенный в заголовок статьи: “Зачем?” т.е. зачем я шаг за шагом шел по пути усложнения? И тут надо признать, что с момента появления в моем распоряжении достаточно мощного аппарата и далее примерно следующие 5 лет основным мотивом для придумывания и последующего применения разного рода архитектурных решений было банальное любопытство - “А что будет если сделать вот так?” или “О! Крутая, новая библиотека, давайте ее заиспользуем”.

Вообще говоря мотив “Круто-круто!!! Давайте использовать” обычно вполне хорош т.к. это драйв, а драйв это как минимум 50% успеха проекта. Дьявол тут кроется в деталях. Бывает так что драйв не столь уж и силен, а негативный эффект от использования может быть не самой подходящей технической платформы может быть очень и очень сильным. В общем все зависит от везения, насколько та библиотека которая сейчас Вам кажется самой лучше в мире действительно подходит для Вашего текущего проекта.

Возникает вопрос - что произошло через 5 лет? После очередного случая, когда сначала я половину проекта радостно внедрял новое архитектурное решение, а потом вторую половину боролся с side эффектами мне пришла в голову мысль - “А не ищу ли я приключений на свою собственную хм.. голову используя в проекте технологии которые по сути просто случайно показались мне интересными на момент старта проекта?”

Быстрый ответ

Сейчас мне кажется, что ответ “Чтобы было круто” на вопрос “Зачем придумывать архитектурные решения?” не самый правильный. Хорошо. Тогда какой правильный? Мне кажется что правильный ответ “Чтобы решать реальные проблемы возникающие в ходе работы над проектом”. Супер! Утверждение вида “Спасибо кэп!”. Но давай рассмотрим подробнее:

Подробный ответ

“Чтобы Решать проблемы”


Кажется, что использовать какой-то паттерн или технологию которая не решает проблемы никто, никогда не станет. Но увы в реальной жизни это не так. Очень часто проблемы “лечат“ не тем, чем нужно а тем, что умеют, что модно или просто по какой-то причине попалось под руку. Или просто отражает некое общее индустриальное заблуждение. Так например в средние века ожоги лечили обрабатывая пораженное место маслом, что на самом деле не только не лечило, но даже серьезно вредило пациенту. И это было устоявшейся индустриальной практикой. Приведу пример из мира IT. Cо слов одного из коллег:

База данных досталась проекту as is от предыдущей ре инкарнации и была она мягко говоря не оптимальная. Данных там было много гигабайтов, структура путанная и хуже всего все это на тот момент уже использовалось старой версией приложения, соответственно что-то поменять в базе было практически невозможно. В какой-то момент стало ясно что адская структура таблиц не позволяет ряду важных, нужных и что особенно обидно популярных у пользователя запросов работать достаточно эффективно.
Что предлагается в качестве решения? Предлагается использовать ORM framework.
Вообще говоря ORM framework-и очень и очень редко когда позволяют ускорить доступ к БД и даже если и позволяют, то только за счет кэширования которое можно сделать и другими способами. Как правило же, работа с БД при помощи ORM наоборот несколько замедляется. В данном случае так и случилось. Месяц судорожной работы по прикручиванию ORM и заметное глазу ухудшение производительности.
Почему вообще предложили такое решение - потому что это был 2006 год и все в то время были просто без ума от ORM. Т.е. ORM на тот момент в мире Явы был устоявшейся индустриальной практикой и его использовали в общем-то почти все кто знал что это такое.

“Решать реальные проблемы”

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

Какое-то время назад я выступал консультантом в проекте который представлял собой некую социальную сеть, где пользователи регистрировались и потом посредством своих действий начинали приносить доход владельцу приложения через просмотр рекламы.
Анализ посещаемости показал, что много пользователей заходят на главную страницу нажимают кнопку регистрация, но конца регистрационного визарда из трех экранов не доходят. Одной из причин была объявлена высокая нагрузка на процессор в процессе регистрации (что само по себе странно) и соответственно заказчиком было решено бороться с тайм аутами посредством асинхронной обработки регистрации новых пользователей.
В результате за 4 недели было перелопачено довольно большое кол-во кода, архитектура заметно усложнилась. Новые пользователи все равно валом не повалили. Причина была в другом
1. Сама бизнес идея была так себе и соответственно интерес пользователей к приложению был слабый и ради не понятно чего тратить время на 3-х страничный визард мало что хочет.
2. На втором шаге пользователя просили ввести номер кредитки, соответственно далеко не каждый готов на это пойти.
Ничего хорошего в результате не получилось. Пользователей больше не стало, архитектура усложнилась. Время (т.е. бюджет) был потрачен непонятно на что.

“Возникающие в ходе работы”

Иначе говоря где у нас в проекте планирование? В начале или размазано по всему проекту? Ответ зависит от того какой у нас проект Fixed Price т.е. проект в котором мы в начале знаем все требования (ну почти все) и за фиксированную плату беремся требования реализовать. Или T&M где заказчик нанимает команду для написания приложения и по ходу работы может менять требования в любую сторону.
В Fixed Price можно с самого начала все продумать, заранее исследовать и сделать прототипы. Там по идее вся-вся архитектура продумывается заранее. Для того чтобы ничего не забыть существуют специальные методики описанные в толстых книгах.
Сейчас на дворе эпоха Agile. Мир быстро меняется и мало кто может позволить себе зафиксировать требования и не менять их 6-12 месяцев.
Даже проекты которые юридически выглядят как Fixed Price с технической точки зрения делаются скорее в Agile стиле, т.е. проблемы в подавляющем большинстве выявляются по мере работы над проектом и решение для них ищется AdHoc. Это не очень здорово, но в динамичном мире приходится делать именно так. К какому перегибу это ведет?

Все прекрасно знают из книг, что перед началом проекта надо сделать архитектуру, но поскольку не все проблемы известны и да чего там говорить не все требования ясны, делают так. На старте в код закладывается некое решение “чтобы все было гибко и если вдруг надо поменять то менять было легко”. И собственно первые 8-40 часов работы над проектом все делают гибкую архитектуру на будущее. Потом время отведенное на это в плане проекта заканчивается и начинается работа. В ходе работы вылезают реальные проблемы, т.е. не те проблемы которые как казалось будут, а реальные о которых на момент придумывания мега гибкой архитектуры никто не знал. Времени на придумывание как эти проблемы решить наименее травматичным образом не остается и проблемы решаются как получится. Причем вот что удивительно я пока не видел ни одного случая когда мега гибкое универсальное решение задуманное в начале привело бы хоть к какой-то экономии времени впоследствии. Пример

Команда коллегиально решает сделать слоистую архитектуры в web MVC приложении. Контроллер вызывает слой бизнес логики, слой бизнес логики - слой сервисов, слой сервисов вызывает слой DAO, слой DAO вызвает слой DataRepository. Код изобилует методами вида

public class AccountService {
private AccountDAO accountDAO; //somehow injected :-)
public void transferMoney(Account from, Account to, long amount){
accountDAO.transferMoney(from,to,amout)
}
и ниже еще 20 похожих методов
}

Как легко заметить собственно методы класса никакой логики кроме делегирования вызова вниз слоеного пирога уровней архитектуры не несут.
Плюсы - у нас все гибко и логика добавляется по уровням каждый вид логики на своем уровне - бизнес в бизнесе, доступ к данным в DAO. И заодно очень хорошо писать юнит-тесты
Минусы - менее очевидны, но они есть и их дофига
1. Даже если приходит нужда добавить какую-то логику на уровне сервисов например - то человеку который не знает или не помнит к какому методу было что-то добавлено будет очень не тривиально догадаться что в одном из 100 безликих делегирующих методов уровня сервисов есть какая-то логика. Т.е. сопровождаемость кода существенно ухудшается
2. Кода реально много, зачастую в 4-5 раз больше чем если бы все писалось сразу в контроллере. Много кода == много работы. Причем работы зачастую тупой. Написал одну строчку SQL и 50 строк делегирования с уровня на уровень.
3. Если возникает новое требование - необходимость поддерживать мультивалютность т.е. везде вместе с amount, передавать currencyCode. То эту дополнительную переменную надо протащить по всем методам до самого низа. Т.е. большое кол-во уровне ни разу не помогает, а только увеличивается объем работы.
NB: я не против слоеной архитектуры, но это один из возможных архитектурных приемов и совершенно не факт что в каждом приложении большом и  маленьком надо использовать все приемы которые вы знаете, как не надо при приготовлении пищи кидать в кастрюлю все специи которые есть у вас под рукой. Может быть перец нужен, а без тмина можно обойтись или наоборот нужен именно тмин.

Несколько самых популярных вещей которые любят закладывать в архитектуру с самого начала “чтобы было”.
  1. С большим отрывом от остальных - слоеная архитектура из 3-4 слоев, в экстремальном случае я видел 5.  
  2. Универсальный слой работы с БД, не зависящий от типа БД. “Вот скажет заказчик давайте вместо MSSQL мы PostgeSQL или Oracle возьмем, а у нас все уже готово”
  3. Вынесение вообще всех числовых и строковых констант в конфигурационные файлы.
  4. Создание каких-то DAO и DTO со стандартными методам CRUD+find под все таблицы в БД до написания какой либо полезной логики.

Совет тут очень простой и вроде как логичный. Ищите решение тем проблемам которые существуют реально в текущем проекте, а не тем которые могу быть в теории или были в вашем прошлом проекте.
Да, конечно надо пытаться предвидеть, анализировать возможные риски и заранее готовить их решений. Но тут надо именно думать головой о текущем проекте. Увы обычно этого никто не любит. Разум ленив и старается подменить работу головой - опытом. Это хорошо, но беда в том, что опыт не всегда релевантен текущей ситуации и его надо тоже применять с головой. Приведу пример:

Один из тим-лидов проекте столкнулся с ситуаций когда заказчик примерно в середине проекта решил поменять БД с MySQL на Oracle. Проект в силу своей природы был достаточно базо-зависимым, т.е. обойтись ANSI SQL не получалось и переписывать пришлось много.
Случай вообще говоря не сильно распространенный. Но после этого следущие несколько лет этот тим лид во всех проектах добавлял уровень абстрации БД. базонезависимые фабрики SQL и т.п. вещи.
Надо говорить, о том, что смена БД больше ни в одном проекте не понадобилась?

Summary

Я наверное успел сказать достаточно слов про то как не надо делать, привел какие-то негативные примеры, напустил туману и в общем уже пора поведать ту страшную истину ради которой все писалось. Паттерны проектирования, разного рода архитектурные приемы, такие как слои, IoC, ORM и так далее это прежде всего инструменты. И сами по себе они не имеют вообще никакой ценности. Хуже того они стоят денег, т.е. в буквальном смысле слова внедрение каждого из инструментов сначала стоит каких-то усилий, ухудшает какое-то свойство конечного продукта (производительность, расход памяти, размер исполняемого файла, количество документации которое надо прочесть новому разработчику перед тем как влиться в проект, ...) и только потом приносит какую-то пользу.
Это конечно не значит что надо писать на ассемблере или чего уже там, на машинном коде. Но добавляя еще одну библиотеку в проект всегда надо держать эту бухгалтерию в голове.
Отсюда вытекает одно из основных требований к архитектору - широкий кругозор. Если вы предлагаете использовать ORM движок - скажем Hibernate, но при этом не знаете какие еще ORM движки бывают и какие есть способы работы без ORM, то дальше есть два варианта
  1. Проект не большой или не очень “плотный”, т.е. общем все не очень страшно. Тогда даже если предлагаемое решение не очень подходит вашему проекту - он все равно сойдется, пусть даже может быть ценой некоторого геморроя.
  2. Все страшно - проект на 20000 часов, плотные сроки и злой заказчик с паяльником. В таком случае идите нафиг со своими советами пока не сможете внятно рассказать какие есть альтернативы Hibernate, какие есть плюсы и минусы и что конкретно будет если вообще все делать без  ORM.
При этом заметьте (я знаю что предложение дальше большая часть читателей пропустит мимо мозга, но таки напишу). ORM сам по себе хорошая штука и Hibernate наверное лучший ORM движок из всех какие на текущий момент есть во вселенной. Но если вы архитектор, а не погулять вышли, для каждого предлагаемого решения Вы обязаны знать плюсы, минусы и альтернативы. Просто потому что это знание абсолютно необходимо для принятия решение.
Мы ведь именно принимаем решение, взвешено и сознательно по каждому компоненту нашей архитектуры, а не пихаем импульсивно в проект все что придет в голову? Правда?
Т.е. да, я понимаю есть драйв, есть опыт и при сочетании одного с другим можно что-то напихивать в проект на ходу без участия головного мозга вообще и оно будет работать.
Или можно сказать, “да знаю что либа новая, но давай ка я ее заиспользую в проекте, а если окажется что она кривая, то я буду работать по ночам 7 дней в неделю и вообще: сам виноват - сам исправлю.“
Это вполне нормальные случаи так надо делать, чтобы развиваться, сам работал в выходные исправляя последствия неудачных экспериментов, но тренироваться надо на чем-то безопасном, где возможный ущерб минимален. Условно говоря испытывайте ядерные бомбы на полигоне.

P.S.

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


P.P.S.
Вообще надо написать еще на темы


  1. Что не так с ORM?
  2. "Как конкретно планировать хорошую архитектуру" в стиле "Боевой устав младшего архитектора" т.е. книги в которой "каждая строчка написана кровью"(с)

P.P.P.S.

Вообще интересно насколько расползется статья опубликованная не на хабре, а черт знает где :-)