الفرق في الفلسفة
“أنا task runner سريع جدًا. اعمل الـ monorepo بتاعك بالطريقة اللي تعجبك، وأنا أشغّله وأكيّشه.”
أداة صغيرة بتعمل حاجة واحدة كويس.
“أنا build system + منصة. هفهم الكود بتاعك، أولّدلك projects، أحمي الحدود بينهم، وأوزّع المهام على machines.”
أداة كبيرة بتمسك الريبو كله.
Project Graph
Nx بيحلل الـ imports في كودك نفسه (مش بس package.json) ويبني خريطة لمين بيعتمد على مين. وبيعرضهالك تفاعلي في المتصفح:
npx nx@latest init # ضيف Nx لريبو موجود (حتى لو فيه Turbo/pnpm)npx nx graph # افتح الخريطةnpx nx affected -t lint test build # زي --affected في turbonpx nx show project web # كل الـ targets المستنتجة للـ project
من ميزاته المهمة: Inferred tasks — لو لقى vite.config.ts بيستنتج إن فيه build و serve و test لوحده من غير ما تكتبهم.
Generators
بدل ما تنسخ فولدر وتعدّل 7 ملفات كل ما تعمل package جديدة، Nx عنده generators بتعمل الـ scaffold كامل ومتوصّل:
npx nx add @nx/reactnpx nx g @nx/react:library packages/jobs-feature --bundler=nonenpx nx g @nx/react:component packages/ui/src/Badgenpx nx g @nx/nest:application apps/api
Module Boundaries: أقوى ميزة
في ريبو كبير، أكيد هييجي يوم حد يعمل import من apps/api جوه packages/ui، أو feature تـ import من جوه feature تانية. Nx بيمنع ده بـ tags + ESLint rule:
{ "name": "ui", "tags": ["scope:shared", "type:ui"]}import nx from "@nx/eslint-plugin";export default [ ...nx.configs["flat/base"], { files: ["**/*.ts", "**/*.tsx"], rules: { "@nx/enforce-module-boundaries": ["error", { depConstraints: [ { sourceTag: "type:ui", onlyDependOnLibsWithTags: ["type:ui", "type:util"] }, { sourceTag: "type:feature", onlyDependOnLibsWithTags: ["type:ui", "type:util", "type:data"] }, { sourceTag: "scope:web", onlyDependOnLibsWithTags: ["scope:web", "scope:shared"] }, { sourceTag: "scope:api", onlyDependOnLibsWithTags: ["scope:api", "scope:shared"] }, ], }], }, },];دلوقتي لو ui حاول يـ import من feature، الـ lint هيضرب في الـ PR. ده بيترجم الـ clean architecture من “اتفاق شفهي” لـ “قاعدة بتتنفّذ”.
المقارنة الصريحة
| الجانب | Turborepo | Nx |
|---|---|---|
| منحنى التعلم | ساعات | أيام لأسابيع |
| Config | turbo.json واحد | nx.json + project.json أو inferred |
| Caching محلي/remote | ✅ / Vercel أو self-host | ✅ / Nx Cloud أو self-host |
| Affected | ✅ --affected | ✅ nx affected (أنضج) |
| Project graph | من package.json | من الـ imports + UI تفاعلي |
| Generators | بسيطة (turbo gen) | قوية جدًا + plugins |
| Module boundaries | محدود/لسه بيتطور | ✅ ناضج |
| توزيع المهام على machines | ❌ | ✅ (Nx Agents) |
| الأنسب لـ | فريق صغير/متوسط، 2–15 package | فرق كبيرة، عشرات الـ projects، قواعد معمارية صارمة |
🧪 Lab
- خد الريبو بتاع الأسبوع التالت واعمل branch جديد.
npx nx@latest initوشغّلnx graph— قارنها باللي في دماغك.- ضيف tags لكل project واعمل rule تمنع
uiمنtypes… ثم اكسرها بقصد. - اعمل library جديدة بـ generator وشوف الملفات اللي اتعملت.
- اكتب لنفسك صفحة: هتختار إيه لمشروعك الجاي وليه.
١) إيه الفرق الجوهري في إزاي الاتنين بيبنوا الـ graph؟
٢) عاوز تمنع أي feature تـ import من feature تانية. الأداة؟
@nx/enforce-module-boundaries.