Практические примеры решений
Цель: Помочь принимать решения о размещении кода через практические примеры.
Пример 1: Добавление лайка к статье
Вопрос: Где разместить логику добавления/удаления лайка?
MUST
- Пользовательские сценарии размещать в
features/.
// ✅ CORRECT - пользовательский сценарий в features/
// features/article-favorite/model/use-toggle-favorite.ts
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { invalidateArticleListRelatedQueries } from '@/entities/article';
import {
favoriteArticle,
unfavoriteArticle,
} from '@/shared/api/endpoints/article.api';
export function useToggleFavorite(slug: string) {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (isFavorited: boolean) =>
isFavorited ? unfavoriteArticle(slug) : favoriteArticle(slug),
onSuccess: () => {
invalidateArticleListRelatedQueries(queryClient);
},
});
}
- UI компоненты сценария размещать в той же feature.
// ✅ features/article-favorite/ui/favorite-button.tsx
import { useTranslations } from 'next-intl';
import { useToggleFavorite } from '../model/use-toggle-favorite';
import type { Article } from '@/entities/article';
interface FavoriteButtonProps {
record: Article;
}
export function FavoriteButton({ record }: FavoriteButtonProps) {
const { mutate } = useToggleFavorite(record.slug);
const t = useTranslations('common');
const handleToggleFavorite = () => {
mutate(record.favorited);
};
return (
<button onClick={handleToggleFavorite}>
{t('actions.favor', { isFavorite: record.favorited })}
<span>{record.favoritesCount}</span>
</button>
);
}
Обоснование размещения в features/
Почему features/article-favorite/?
- Это законченный пользовательский сценарий
- Управляет состоянием и обработкой ошибок
- Оркеструет взаимодействие с entities и API
- Может быть переиспользован на разных страницах
FORBIDDEN
- Размещать пользовательские сценарии в
entities/.
// ❌ FORBIDDEN - сценарий в entities
// entities/article/model/use-toggle-favorite.ts
export function useToggleFavorite(slug: string) {
// Это сценарий, а не бизнес-логика сущности!
}
- Размещать сценарии в
shared/.
// ❌ FORBIDDEN - сценарий в shared
// shared/hooks/use-toggle-favorite.ts
export function useToggleFavorite(slug: string) {
// shared не содержит бизнес-логику!
}
Пример 2: Валидация email
Вопрос: Где разместить валидацию email?
MUST
- Технические валидаторы без бизнес-логики размещать в
shared/lib/validators/.
// ✅ CORRECT - техническая утилита в shared
// shared/lib/validators/email.ts
export function isValidEmail(email: string): boolean {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
- Использовать технические валидаторы через импорт в features.
// ✅ CORRECT - использование в features
// features/auth/model/validation.ts
import { isValidEmail } from '@/shared/lib/validators/email';
import { z } from 'zod';
export const createLoginSchema = (t: TFunction) =>
z.object({
email: z.string().refine(isValidEmail, t('errors.invalidEmailFormat')),
password: z.string().min(8, t('errors.passwordTooShort')),
});