Современные системы являются распределенными и слабо связанными (loosely coupled). Построение слабо связанных систем создает немало хлопот, так почему же мы идем на это? Потому что хотим, чтобы наши системы были гибкими и не разваливались при малейших изменениях. Это критическое свойство в современных средах, где мы контролируем лишь небольшую часть своего приложения, а все остальное существует в виде распределенных служб или пакетов, находящихся под контролем других отделов или внешних производителей.
Итак, стремление создать систему гибкую и способную развиваться со временем — дело хорошее. Но это означает также, что наша система будет постепенно изменяться. Другими словами, «сегодня система уже не та, какой была вчера». К сожалению, это заметно усложняет документирование системы. Все знают, что документация устаревает в момент печати, но в постоянно изменяющейся системе дела могут обстоять еще хуже. Более того, построение гибкой системы обычно усложняет архитектуру, и получить пресловутую «общую картину» становится еще труднее. Например, если все компоненты системы обмениваются информацией по логическим, настраиваемым каналам, то, чтобы получить представление о происходящем, необходимо взглянуть на конфигурацию каналов. Отправка сообщений в логическое пойди-туда-не-знаю-куда вряд ли приведет к ошибке компиляции, но наверняка огорчит пользователя, действие которого было запаковано в это сообщение.
Архитектор, помешанный на тотальном контроле, — фигура из прошлого; решения, которые он создает, являются сильно связанными и хрупкими. С другой стороны, полная и неограниченная свобода приложения — верный путь к хаосу. Ослабление контроля необходимо дополнить другими механизмами, чтобы «полет по приборам» не происходил без самих приборов. Но какие приборы есть в нашем распоряжении? Вообще-то их более чем достаточно. Современные языки программирования поддерживают рефлексию (reflection), и почти все платформы предоставляют динамические метрики времени выполнения. По мере того как система становится более настраиваемой, ее текущая конфигурация сама становится отличным источником информации. Так как разобраться в слишком большом объеме низкоуровневых данных довольно трудно, создайте на их основе модель. Например, когда станет ясно, какие компоненты отправляют сообщения по тем или иным логическим каналам и какие компоненты ожидают поступления сообщений по этим каналам, вы сможете построить граф передачи данных между компонентами. Эту процедуру можно производить каждые несколько минут или часов, создавая точную и оперативную картину системы в ходе ее развития. Считайте это своего рода «MDA наоборот»:[31] вместо модели, управляющей архитектурой, вы строите гибкую архитектуру и формируете модель на основании текущего состояния системы.
Во многих случаях модель можно легко визуализировать, построив действительно «общую картину». Однако боритесь с соблазном заполнить квадратиками и линиями полотно 3x5 метров в стремлении изобразить все классы вашей системы. Такая картина сойдет за произведение современного искусства, но ее не назовешь полезной программной моделью. Вместо этого лучше использовать «вид с высоты 300 метров», как рекомендует Эрик Дорненбург, — тот уровень абстракции, который содержит действительно полезную информацию. Вдобавок вы сможете проследить за тем, чтобы в модели не было нарушений простейших правил корректности — циклических ссылок в графе зависимостей или отправки сообщений по непрослушиваемым логическим каналам.
Ослабление контроля пугает, особенно когда речь идет об архитектуре системы. Но в сочетании с тщательным наблюдением, построением моделей и проверкой корректности оно, вероятно, является единственным разумным подходом к построению архитектуры программного обеспечения в XXI веке.
Биография автора приведена ранее.