BetaViberTest is in active development — expect breaking changes.
Overview
#005mediumArchitecture & Structure
Separation of Concerns
Detects code that mixes UI, data fetching, and business logic layers.
Rule ID:
separation-of-concernsExamples#
BadUI component with data fetching
// UserProfile.tsx — UI + data fetching + business logic
export function UserProfile({ userId }: { userId: string }) {
const [user, setUser] = useState(null);
useEffect(() => {
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => setUser(data));
}, [userId]);
const fullName = user
? `${user.firstName} ${user.lastName}`
: '';
const isAdmin = user?.roles.includes('admin');
return (
<div>
<h1>{fullName}</h1>
{isAdmin && <AdminBadge />}
</div>
);
}GoodSeparated into layers
// hooks/use-user.ts — data fetching
export function useUser(userId: string) {
const [user, setUser] = useState<User | null>(null);
useEffect(() => {
userService.getById(userId).then(setUser);
}, [userId]);
return user;
}
// lib/user-utils.ts — business logic
export function getFullName(user: User) {
return `${user.firstName} ${user.lastName}`;
}
export function isAdmin(user: User) {
return user.roles.includes('admin');
}
// UserProfile.tsx — UI only
export function UserProfile({ userId }: { userId: string }) {
const user = useUser(userId);
if (!user) return <Skeleton />;
return (
<div>
<h1>{getFullName(user)}</h1>
{isAdmin(user) && <AdminBadge />}
</div>
);
}What It Detects#
mediumUI component (.tsx/.jsx) with direct data fetching (fetch, axios, prisma, supabase)
UI component contains data fetching logic
Fix: Move data fetching to a custom hook (useXxx) or service layer. Components should only handle rendering.
mediumRoute handler with >5 function definitions
Route handler contains {N} function definitions — too much business logic
Fix: Extract business logic to a service/use-case layer.
mediumRaw SQL outside data-layer directories
Raw SQL ({keyword}) found outside data access layer
Fix: Move database queries to a repository or data access layer.
Exclusions#
The following are automatically excluded from this rule:
- Auth-related files (login, signup, etc.) where SDK calls are expected
Configuration#
This rule is enabled by default. To disable it:
.vibertestrc.jsonjson
{
"rules": {
"separation-of-concerns": {
"enabled": false
}
}
}