Every library has one purpose — feature, data-access, ui, or util
Mix UI with API in one lib — Storybook needs the whole stack mocked.
nx-isolation-deps-library-purpose
Why it matters
| Stake | If ignored |
|---|---|
| Hard to test |
|
| Agent gets lost |
|
| Spreads failure |
|
How to fix
In Nx this is the type tag, not just convention. A library that mixes UI with API calls with business logic is a library you can't understand, test, or delete.
Examples
ts
// libs/sites-management/everything/src/lib/site-card.tsx
export function SiteCard({ siteId }: { siteId: string }) {
const [site, setSite] = useState<Site>();
useEffect(() => {
fetch(`/api/sites/${siteId}`).then(r => r.json()).then(setSite);
}, [siteId]);
const handleDelete = async () => {
await fetch(`/api/sites/${siteId}`, { method: 'DELETE' });
analytics.track('site_deleted');
};
return <div className="card">{/* ... */}</div>;
}ts
// libs/sites-management/ui/src/lib/site-card.tsx — type:ui, pure
export function SiteCard({ site, onDelete }: {
site: Site;
onDelete: () => void;
}) {
return <div className="card">{/* ... */}</div>;
}
// libs/sites-management/data-access/src/lib/sites-api.ts — type:data-access
export function useSite(id: string) { /* ... */ }
export function useDeleteSite() { /* ... */ }
// libs/sites-management/feature-site-list/src/lib/site-card-container.tsx — type:feature
export function SiteCardContainer({ siteId }: { siteId: string }) {
const { data: site } = useSite(siteId);
const deleteSite = useDeleteSite();
if (!site) return null;
return <SiteCard site={site} onDelete={() => deleteSite.mutate(siteId)} />;
}