Какие есть подходы к разделению бизнес-логики и UI?

Разделение бизнес-логики и UI (презентационного слоя) — ключевой принцип построения устойчивой архитектуры в React Native-приложениях. Это помогает улучшить читаемость, переиспользуемость, масштабируемость и тестируемость кода. Без такого разделения компонент становится монолитным, перегруженным и сложным для поддержки.

Существует несколько подходов и паттернов, позволяющих грамотно разделить UI и бизнес-логику:

1. Container/Presentational Pattern (Smart/Dumb Components)

Это один из самых распространённых способов разделения логики и отображения:

  • Presentational (глупый) компонент — отвечает только за отображение (UI). Получает данные и колбэки через props. Не знает, откуда приходят данные.

  • Container (умный) компонент — содержит бизнес-логику, работает с состоянием, side-effects (например, загрузка данных).

Пример:

// Presentational
const UserProfile = ({ user, onLogout }) => (
<View>
<Text>{user.name}</Text>
<Button onPress={onLogout} title="Logout" />
</View>
);
// Container
const UserProfileContainer = () => {
const user = useSelector(selectUser);
const dispatch = useDispatch();
const handleLogout = () => dispatch(logout());
return <UserProfile user={user} onLogout={handleLogout} />;
};

Плюсы:

  • Простая композиция

  • Повторное использование UI

  • Удобство тестирования UI и логики отдельно

2. Hooks-based Separation (Logic Hooks)

Разделение логики через пользовательские хуки (useXYZ), в которых инкапсулируется состояние и побочные эффекты, а UI остаётся чистым.

Пример:

// useUser.js
export function useUser() {
const user = useSelector(selectUser);
const dispatch = useDispatch();
const logout = () => dispatch(logout());
return { user, logout };
}
// UI-компонент
const UserProfile = () => {
const { user, logout } = useUser();
return (
<View>
<Text>{user.name}</Text>
<Button title="Logout" onPress={logout} />
</View>
);
};

Плюсы:

  • Чёткое отделение логики

  • Повторное использование хуков в разных местах

  • Простой способ разбить большие компоненты

3. MVVM (Model-View-ViewModel)

Этот паттерн особенно подходит для сложных приложений с обширной бизнес-логикой.

  • Model — источники данных: API, хранилища, базы

  • ViewModel — класс или хук, содержащий бизнес-логику

  • View — отображает интерфейс и подписывается на ViewModel

Пример:

// userViewModel.js
export const useUserViewModel = () => {
const { data, isLoading } = useGetUserQuery();
const navigation = useNavigation();
const goToSettings = () => navigation.navigate('Settings');
return {
username: data?.name ?? '',
isLoading,
goToSettings,
};
};
// UserView.tsx
const UserView = () => {
const { username, isLoading, goToSettings } = useUserViewModel();
return (
<View>
<Text>{isLoading ? 'Loading...' : username}</Text>
<Button onPress={goToSettings} title="Settings" />
</View>
);
};

Плюсы:

  • Отделение UI от бизнес-потоков

  • Возможность мока ViewModel в тестах

  • Близко к архитектуре нативных мобильных фреймворков

4. Redux или Zustand как слой логики

State management-библиотеки могут выступать как слой бизнес-логики, предоставляя компонентам только нужные данные и действия.

Redux:

  • Селекторы — для выборки данных

  • Thunks/Sagas — для асинхронной бизнес-логики

  • Dispatchers — для изменения состояния

const CounterView = () => {
const count = useSelector(state => state.counter.value);
const dispatch = useDispatch();
return (
<View>
<Text>{count}</Text>
<Button title="+" onPress={() => dispatch(increment())} />
</View>
);
};

Zustand:

const useStore = create((set) => ({
count: 0,
increment: () => set((s) => ({ count: s.count + 1 })),
}));
const Counter = () => {
const { count, increment } = useStore();
return (
<View>
<Text>{count}</Text>
<Button onPress={increment} title="Add" />
</View>
);
};

Плюсы:

  • Единый источник бизнес-логики

  • Компоненты максимально «тупые»

  • Легко расширять

5. Реализация слоя Use Case / Service

Выделение бизнес-операций в сервисные функции/классы или use-case слои. Это особенно полезно, если приложение взаимодействует с несколькими источниками данных или требует комплексных правил.

// authService.ts
export const authService = {
login: async (email, password) => {
const token = await api.login(email, password);
await AsyncStorage.setItem('token', token);
return token;
},
};
// В компоненте
useEffect(() => {
authService.login(email, password).then(token => {
dispatch(setToken(token));
});
}, \[\]);

Плюсы:

  • Централизация бизнес-правил

  • Упрощает тестирование и замену слоя API

  • Не связан с UI

6. Кастомные HOC (Higher-Order Components)

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

const withUser = (Component) => (props) => {
const user = useSelector(selectUser);
return <Component {...props} user={user} />;
};
const Profile = ({ user }) => <Text>{user.name}</Text>;
export default withUser(Profile);

Плюсы:

  • Переиспользуемость логики

  • Оборачивание UI без изменения его структуры

Минусы:

  • Плохо читается при вложенных HOC

  • Появляется так называемый "wrapper hell"

7. Атомарная архитектура компонентов

Смысл — разбить интерфейс на независимые, изолированные компоненты:

  • Atoms — кнопки, иконки, поля ввода

  • Molecules — группы атомов: карточка, форма

  • Organisms — большие UI-блоки с логикой

  • Pages — полноценные экраны, содержащие организмов

Такой подход часто используется с Atomic Design и Design Systems.

8. Разделение через слои в структуре проекта

Пример структуры:

/src
/components (UI)
Button.tsx
Card.tsx
/hooks (логика)
useAuth.ts
useCart.ts
/services
api.ts
storage.ts
/features
/user
UserView.tsx
useUser.ts
userSlice.ts

Плюсы:

  • Чистая структура

  • Чёткое разделение ответственности

  • Масштабируемость при росте проекта

Разделение бизнес-логики от UI — основа надёжного, поддерживаемого приложения. Каждый из описанных подходов может применяться в зависимости от размера проекта, предпочтений команды и требований архитектуры. Часто эффективной оказывается комбинация нескольких стратегий: например, хранилище состояния (Redux) + ViewModel хуки + UI компоненты.