- BrainTools - https://www.braintools.ru -
Современная UI-разработка живет быстрыми темпами. Мы постоянно создаем и меняем код, чтобы соответствовать новым трендам, но часто это делается «на коленке» и без долгосрочного видения. В итоге уже через пару месяцев все, что казалось суперсовременным, превращается в легаси из-за смены моды фреймворков и других деталей реализации.
В статье расскажу, почему традиционное долгосрочное планирование уже не работает, как можно отделить основную логику [1] от конкретных технологий и какие инструменты помогут быть более гибкими в будущем.
Сегодня большинство UI-разработчиков завязаны на конкретных фреймворках вроде React, Angular или Vue. Они помогают быстро запустить проект, но привносят свои особенности:
Они требуют обновлений — что сегодня в тренде, завтра уже уже устарело.
Их абстракции протекают — логика часто знает о специфических особенностях фреймворка.
Они приводят к высокой связанности — логика привязывается к редаксу или к хукам React.
Они ограничивают мышление [2] — мы начинаем путать архитектуру приложения с особенностями выбранного инструмента.
Предугадать, какие изменения претерпят фреймворки и браузеры в будущем, хотя и возможно теоретически, практически нецелесообразно. Мы оказываемся в роли догоняющих, а не опережающих, и чем больше проект, тем сложнее догнать текущие тенденции.
В таких условиях выбрать инструменты с расчётом на годы вперёд практически невозможно. Несмотря на то, что TypeScript значительно ускоряет адаптацию к изменениям и делает рефакторинг более доступным, этого недостаточно. Необходимо внедрение архитектурной изоляции, чтобы логика приложения оставалась независимой от мелких деталей реализации.
Есть теория, которая гласит, что технические решения, в том числе и UI-приложения, подчиняются определенным закономерностям. Один из главных законов — стремление к повышению степени идеальности. То есть, части системы перестают быть привязанными к конкретике и могут быть реализованы вне жесткой связки с ней.
Эта теория — ТРИЗ (Теория решения изобретательских задач). В ее рамках идеальным решением считается такое, при котором часть системы выполняет свою функцию и сама как бы исчезает.
Рассмотрим пример. Взять стеклоочистители в автомобилях. Раньше водитель сам протирал лобовое стекло, потом появились механические дворники с ручным управлением, а затем — автоматические, которые работают по таймеру.
Но настоящий идеал – когда стекло само остаётся чистым без движущихся элементов. Функция есть, а механизма нет. Гидрофобное покрытие, которое заставляет воду скатываться, является отличным примером: механизм исчезает, а функция выполняется даже лучше.
Этот же принцип можно перенести в UI-приложения, где мы повышаем степень идеальности:
Заменить сложные зависимости абстракциями, которые позволят менять реализацию, без переделывания всего кода.
Применить декларативный подход: описать, чего мы хотим добиться, а система пусть сама разбирается с деталями.
Чем меньше конкретных деталей и привязок внутри системы, тем более она гибкая, лёгкая и способная адаптироваться к изменениям.
Концепции этой статьи будут рассмотрены на примере реализации игры в крестики-нолики из документации React, но измененной нами [3].
Обобщение и абстракция — это то, чем занимается архитектура. Она позволяет сделать части независимыми, легкотестируемыми и отложить принятие решений о деталях как можно дальше в будущее.
Истинная архитектура приложения — это про четкое разделение ответственности, в противовес жесткой привязки к определенному фреймворку.
Ключевые принципы:
Компонент как самостоятельная единица
Каждый UI-компонент (например, Game, Board, Square) должен иметь ясные задачи и быть частью общего корпоративного языка. Названия компонентов задаются на этапе дизайна (ниже объясню почему). Они не придумываются разработчиком.
## Пример онтологии: сущности находятся в отношении "is-a"
## То есть предок (parent) может заменить потомка (child) без потери корректности
Entity
├── Game
├── Square
├── Mark
├── Player
└── Board
GameSubject
├── Game
├── SquareSubject
│ ├── Mark
│ └── Move
├── PlayerSubject
│ └── Player
└── BoardSubject
└── Board
ViewModel через пропсы
Если передавать все данные через пропсы, чтобы компоненты «не знали» о внутренней логике, то такой превращает пропсы в своего рода ViewModel, который позволяет независимо менять представление и бизнес-логику.
// Компонент App: получает состояние и передаёт его в Game через пропсы
export default function App() {
const [state, setState] = useState<TState>(...);
return <Game boardProps={mapStateToBoardProps(() => state, setState)} />;
}
// Компонент Board: отображает ряды квадратов, не вникая в детали управления состоянием
export function Board({ squareProps, status }: TBoardProps) {
return (
<>
<div className="status">{status}</div>
{squareProps.map((row, index) => (
<div key={index} className="board-row">...</div>
))}
</>
);
}
Инъекция зависимостей
Позволяет выносить операции ввода-вывода (например, запросы к серверу) в отдельный слой, чтобы упростить тестирование и заменить реализацию. А самое главное, позволяет избежать принятие решений о том, как именно мы будем получать данные на ранней стадии разработки.
// Преобразуем состояние игры в пропсы для квадратов, сгруппировав их по рядам
export const getMapStateToSquareProps =
({ someAsyncDependency }: { someAsyncDependency: () => Promise<void> }) =>
(
getState: () => TState,
setState: (state: TState) => void
): TSquareProps[][] => {
// Создаем массив рядов для квадратов
const rows: TSquareProps[][] = [];
// Для каждого квадрата создаем пропсы
setRow((i) => {
return {
value: state.squares[i],
onSquareClick: async () => {
try {
await someAsyncDependency(); // вызываем асинхронную функцию
makeMove(i, state, setState); // выполняем основной ход
} catch (error) {
console.error(error);
}
},
index: i,
};
});
return rows;
};
const mapStateToSquareProps = getMapStateToSquareProps({
someAsyncDependency: async () => {
await new Promise((resolve) => setTimeout(resolve, 500)); // имитируем задержку
alert("Async dependency has been resolved");
},
});
Тренд к повышению идеальности проявляется в усилении декларативного подхода. Главное — четко сформулировать, чего мы хотим достичь, а не зацикливаться на механизмах реализации, что позволяет отложить принятие решений до более поздних этапов.
Таким образом, предпочтительна стратегия реализации сверху вниз: мы постепенно выявляем сущности в процессе создания макетов и проведения экспериментов. Нет смысла продумывать все детали заранее: нужные элементы естественно сформируются во время итеративного тестирования и доработки.
В этом процессе и вырабатывается онтология сущностей, или универсальный язык, который позволяет нам общаться и согласовывать свои мысли. Это и есть декларативное описание того, что мы хотим достичь.
Онтологии
От идеи к сущности. Начиная с макетов и моков, которые описывают взаимодействие пользователя с приложением, можно постепенно вырабатывать сущности (например, Square, Move, Player) не плодя их заранее.
Автоматизация изменений. Использование инструментов вроде языка онтологий OWL, чтобы формализовать концепции и отношения между ними, может позволить AI автоматически рефакторить и упрощать структуру этой же онтологии, когда изменятся бизнес-требования.
Такой подход поможет избежать создания лишних сущностей и держать систему простой и гибкой. Это позволит изменить детали реализации, не переделывая всю архитектуру.
Дизайн
Дизайн тоже следует декларативному подходу. Макет в Figma описывает, как должна выглядеть страница, где разместить кнопки и другие элементы. Можно добавить описания: какую функцию выполняет кнопка, какие данные нужны или даже пример кода. Если попросить AI сгенерировать код, он сможет это сделать, опираясь на такое описание и данные внутри макета.
Как уже было сказано, технические решения, в том числе и UI-приложения, подчиняются определенным закономерностям, и один из главных их законов — это стремление к повышению степени идеальности. То есть, детали уходят на второй план, а обобщения на первый.
Хочу предположить и пофантазировать, что в будущем роль UI-разработчика будет не только «писать код», а скорее «строить систему идей», где:
Инструменты будут на базе онтологий. Код будет генерироваться автоматически из декларативных описаний, а онтология послужит единым источником правды.
AI в роли ассистента. Искусственный интеллект [4] поможет не только в рефакторинге, но и в реорганизации и упрощении архитектуры.
Независимость от фреймворков. UI-код будет опираться на структуру дизайна, а дизайн — на онтологии. Это создаст гибкий конвейер, где изменения легко распространяются по всей системе.
Представьте ситуацию: дизайнер обновляет макет в Figma и находит несоответствие с текущей онтологией. В этом случае AI может помочь минимизировать изменения в общей онтологии и макете, предлагая конкретные корректировки и указывая, где дизайн можно упростить. После утверждения макета, AI автоматически зарефакторит или добавит необходимые компоненты, а бэкенд адаптируется к новым сущностям. Такой конвейер мог бы сделать процесс разработки более целостным и независимым от конкретных технологий.
Долгосрочное планирование в UI-разработке уже не заключается в попытке предсказать будущее технологий. Речь идет о том, чтобы создать систему, которая готова меняться. Мы сможем выйти из замкнутого круга постоянных обновлений и переделок, если отделим логику приложения от конкретных деталей реализации и будем использовать формализованные онтологии.
Давайте перестанем писать код, ориентируясь исключительно на текущий тренд (React, Angular и т.д.), и начнем создавать гибкие, легко адаптируемые системы, которые выдержат испытание временем. Например, для начала, будем использовать инъекцию зависимостей, и изолированный вью. А как дополнительное упражнение, можно попробовать использовать MVVM-архитектуру, онтологии и AI для автоматизации рефакторинга и упрощения архитектуры.
Мне кажется, что в будущем UI-разработка станет больше похожа на философию в действии с элементами DevOps — а это, по сути, про понимание и интеграцию всех деталей вместе. Позволю себе вольность и скажу, что вероятно UI разработчик будет больше как “DesignOps”, больше похожим на дизайнера на стероидах. Но дизайнер в моем понимании будет больше похож на архитектора (дизайн и архитектура синонимы в английском), чьей задачей является problem solving через дизайн, а не простое создание красивых макетов.
Надеюсь, эта статья была полезной и помогла вам взглянуть на разработку интерфейсов с новой стороны и ментально подготовиться к переменам, которые уже не за горами. Желаю вам и вашим приложениям прожить долгую и счастливую жизнь!
Автор: kino6052
Источник [5]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/12380
URLs in this post:
[1] логику: http://www.braintools.ru/article/7640
[2] мышление: http://www.braintools.ru/thinking
[3] примере реализации игры в крестики-нолики из документации React, но измененной нами: https://gist.github.com/kino6052/70077e8a4873eaa2faa49ac7ddbc928b
[4] интеллект: http://www.braintools.ru/article/7605
[5] Источник: https://habr.com/ru/companies/beeline_tech/articles/884182/?utm_source=habrahabr&utm_medium=rss&utm_campaign=884182
Нажмите здесь для печати.