Architecture Decision Record
إدارة البيانات العامة
نمط composable مركزي يوفر وصولاً موحداً لبيانات الجلسة في أي مكان بالتطبيق — بدون نسخ البيانات أو مزامنة يدوية.
الفهرس
نظرة عامة مسار تدفق البيانات هيكل بيانات الجلسة مرجع الـ API أمثلة الاستخدام الآلية الداخلية دورة تحديث البيانات القرارات التصميمية الملفات ذات الصلة نظرة عامة
المبدأ الأساسي
useGlobal يقرأ مباشرة من useAuth().data عبر computed — بدون نسخ البيانات أو sync يدوي. لو البيانات موجودة أصلاً في مصدر reactive — لا تنسخها.
composables/core/useGlobal.js
مسار تدفق البيانات
كيف تنتقل البيانات من تسجيل الدخول إلى أي مكان في التطبيق
تسجيل الدخول
POST /api/auth/login → يرجع access_token
useLogin → signIn(credentials) → nuxt-auth يحفظ الـ token في cookie
Session Fetch
GET /api/auth/profile → GET /global (External API)
nuxt-auth → getSession() → النتيجة تتخزن في useAuth().data (reactive ref)
useGlobal() Composable
يقرأ مباشرة من useAuth().data عبر computed
لا نسخ — لا sync — لا ازدواجية. يصدّر: user, lists, employees, findInList, refreshGlobal, session
المستهلكون
Components, Composables, Render Functions
Navbar, GlobalSelect, form.js, schema.js, columns.js — كلها تقرأ من نفس المصدر
هيكل بيانات الجلسة
البيانات التي يرجعها الـ API بعد تسجيل الدخول
Session Data StructureJSON
{
loginName: "ahmed",
name: "أحمد محمد",
privateCompanyMode: true,
list: {
employees: [
{ id: 1, name: "أحمد" },
{ id: 2, name: "محمد" }
],
departments: [
{ id: 1, name: "IT" }
],
categories: [...],
// ... قوائم أخرى
}
}1
POST /api/auth/login → يرجع access_token
2
GET /api/auth/profile → server proxy يضرب GET /global بالـ token
3
النتيجة تتخزن في useAuth().data
مرجع الـ API
كل الخصائص والدوال التي يصدّرها useGlobal
استيراد واستخدامjs
const { user, lists, employees, findInList, refreshGlobal, session } = useGlobal()userComputedRefبيانات المستخدم الحالي — loginName و name.
const { user } = useGlobal()
// في template
{{ user.loginName }} // → "ahmed"
{{ user.name }} // → "أحمد محمد"listsComputedRefكل الـ lookup lists التي يرجعها الـ API — employees, departments, categories, إلخ.
const { lists } = useGlobal()
const departments = computed(
() => lists.value.departments || []
)employeesComputedRefاختصار مباشر لـ lists.value.employees — لأن قائمة الموظفين هي الأكثر استخداماً.
const { employees } = useGlobal()
// بدلاً من:
const employees = computed(
() => lists.value.employees || []
)findInList(list, id, idKey?)Functionيبحث عن عنصر بالـ ID في أي قائمة. يقبل idKey اختياري للبحث بحقل مخصص.
const { employees, findInList } = useGlobal()
const emp = findInList(employees, 5)
// → { id: 5, name: "أحمد" }
const dept = findInList(departments, 'IT', 'code')refreshGlobal()Functionيحدث كل البيانات العامة عبر إعادة استدعاء getSession() — يُستخدم بعد إضافة/تعديل/حذف عنصر.
const { refreshGlobal } = useGlobal()
await service.create(newEmployee)
await refreshNuxtData(service.CACHE_KEY)
refreshGlobal()sessionRefالبيانات الخام للـ session كما هي — للحالات التي تحتاج وصول مباشر لحقول غير موجودة في الـ computed.
const { session } = useGlobal()
const privateMode = computed(
() => session.value?.privateCompanyMode
)أمثلة الاستخدام
كيف يُستخدم useGlobal في أماكن مختلفة من التطبيق
في Component (Navbar)vue
<script setup>
const { user } = useGlobal()
</script>
<template>
<UUser :name="user.loginName" :description="user.name" />
</template>في Module Composable (form.js)js
export const useEmployeesForm = () => {
const { lists, findInList } = useGlobal()
const departments = computed(
() => lists.value.departments || []
)
return { departments }
}في Columns (h() render)js
export const useEmployeesColumns = () => {
const { findInList, employees } = useGlobal()
const createColumns = () => {
return computed(() => [{
accessorKey: 'departmentId',
header: 'القسم',
cell: ({ row }) => {
const dept = findInList(departments, row.original.departmentId)
return dept?.name || '—'
}
}])
}
return { createColumns }
}عبر GlobalSelect Componentvue
<!-- يكفي تحديد اسم القائمة -->
<GlobalSelect
list="employees"
label="الموظف"
v-model="state.employeeId"
/>الآلية الداخلية
كيف يعمل useGlobal من الداخل
Implementationjs
export const useGlobal = () => {
const { data: session, getSession } = useAuth()
const user = computed(() => ({
loginName: session.value?.loginName || '',
name: session.value?.name || ''
}))
const lists = computed(() => session.value?.list || {})
const employees = computed(() => lists.value.employees || [])
const refreshGlobal = () => getSession()
const findInList = (list, id, idKey = 'id') =>
list.value?.find(item => item[idKey] === id) || null
return { user, lists, employees, refreshGlobal, findInList, session }
} لماذا computed وليس نسخ؟
useAuth().data ──computed──→ useGlobal ──read──→ أي مكان
- لو session تتحدث (بعد getSession()) → كل مكان يتحدث فوراً
- لا watchers، لا actions، لا manual sync
- مستحيل تكون البيانات قديمة — لأنها تُقرأ من المصدر مباشرة
دورة تحديث البيانات
كيف تتحدث البيانات بعد إضافة عنصر جديد
إضافة عنصر جديد
service.create(data) → POST /api/employees
تحديث الجدول المحلي
await refreshNuxtData(service.CACHE_KEY)
تحديث الـ lookup lists
refreshGlobal() → getSession() → GET /global
التحديث التلقائي
useAuth().data يتحدث → كل computed في useGlobal يتحدث → كل مستهلك يتحدث
القرارات التصميمية
لماذا اخترنا هذا النمط وليس البدائل الأخرى
لماذا ليس Pinia أو useState؟
| المعيار | Pinia / useState | useGlobal (computed) |
|---|---|---|
| مصدر البيانات | نسخة منفصلة (sync مطلوب) | قراءة مباشرة من useAuth |
| تحديث البيانات | يدوي (action/watcher) | تلقائي (computed) |
| خطر بيانات قديمة | ممكن — لو نسيت الـ sync | مستحيل — لا sync أصلاً |
| Dependencies | @pinia/nuxt + setup | صفر |
| SSR | يحتاج إعداد | يعمل تلقائياً (يرث من useAuth) |
| يعمل في composables | نعم | نعم |
| يعمل في h() render | نعم | نعم |
لماذا ليس provide/inject؟
| الجانب | provide/inject | useGlobal |
|---|---|---|
| النطاق | يعتمد على component tree | يعمل في أي مكان |
| في composables مستقلة | لا يعمل (form.js, columns.js) | يعمل |
| في render functions | لا يعمل | يعمل |
| في components | يعمل | يعمل |
متى تستخدم كل حل؟
| الحالة | الحل المناسب |
|---|---|
| بيانات من مصدر reactive موجود (useAuth) | computed + composable |
| بيانات ينشئها الكلاينت (shopping cart, wizard) | Pinia |
| بيانات معقدة مع mutations كثيرة | Pinia |
| بيانات بسيطة مشتركة بين صفحتين | useState |