Next.js وSage. قد لا يكون هناك ما تناديه.
عدة منتجات من Sage تعمل على جهاز في مكتب العميل بلا عنوان عام. وهذا ليس عائقًا أمام التكامل، بل هو التكامل نفسه، وهو ما يقرر ما تستطيع الصفحة عرضه.
يصل طلب يقول إن بوابة العملاء ينبغي أن تعرض الفواتير المستحقة لكل حساب، والفواتير في Sage. يُقرأ كأنه مهمة جلب بيانات. وقبل أن يكون كذلك، هناك سؤال يُجاب يحسم البنية كلها، وجوابه كثيرًا ما يكون أنه لا عنوان تجلب منه.
فـ Sage عائلة منتجات لا منتج. بعضها خدمات مستضافة بواجهة عامة وتتصرف كأي تكامل سحابي آخر. وبعضها تطبيقات مثبّتة على خادم في مكتب العميل، وبياناتها في ملف على ذلك الخادم. وتطبيق Next.js المنشور لا يصل إلى ذلك الملف، لا من مكوّن خادم ولا من أي مكان، ولا قدر من المكتبة المناسبة يغيّر ذلك.
اعرف أين يعمل، قبل كل شيء
الساعة الأولى من المشروع مكالمة هاتفية لا مخطط.
اسأل عن أي منتج من Sage، وأي إصدار، وأين ثُبّت. واسأل من يدير ذلك الجهاز. واستعد لأن يكون الجواب غير مؤكد، واستعد لأن تعني عبارة "هو في السحابة" أن فريق المالية يصل إلى سطح مكتب Windows عبر المتصفح، وهو ما يبدو سحابة لكل من يستعمله وليس واجهة عامة.
هناك نتيجتان لا ثالث لهما، وليستا صيغتين لشيء واحد. إما أن توجد واجهة على الإنترنت، فيكون لديك تكامل عادي ويكون العمل الحقيقي هو أين ينبغي أن تعمل كل قطعة منه. وإما ألا توجد، فيكون القسم التالي هو المشروع كله.
الشكل الوحيد الذي يُعتمَد
حين يكون النظام داخل شبكة غيرك، يُفتح الاتصال من الداخل إلى الخارج. شيء يعمل بجوار بيانات Sage - خدمة على خادمهم، أو تصدير مجدول، أو موصّل صغير - يرسل البيانات خارجًا عبر HTTPS إلى نقطة تتحكم بها أنت. ولا يُفتح شيء وارد، ولهذا هي النسخة التي توقّع عليها إدارة تقنية المعلومات.
وتطبيق Next.js لديك في الطرف الآخر من ذلك، وهو ليس أبدًا من ينادي Sage. إنه يستقبل، أو في الغالب يقرأ ببساطة قاعدة بيانات تكتب فيها عملية الاستقبال.
Sage على خادمهم
-> موصّل داخل شبكتهم (صادر فقط)
-> نقطة الاستقبال (تتحقق وتكتب)
-> قاعدة بياناتك (نموذج القراءة)
-> مكوّنات الخادم (العرض)
ورسم هذا في الأسبوع الأول أهم مما يبدو، لأنه أيضًا العقد. فكل ما على يسار قاعدة بياناتك جهاز لا تتحكم به، يعمل بجدول لا تضعه أنت، على خادم يطفئه أحدهم في العطلة. وكل ما على يمينها لك، ويمكن أن يصير سريعًا وموثوقًا بقدر ما تشاء.
ومتى صار الرسم على الورق، كفّ بقية التصميم عن أن يكون عن Sage. إنه نموذج قراءة، وهو المسألة المعتادة في اختيار قاعدة بيانات وفهرستها للاستعلامات التي تجريها صفحاتك.
عمر البيانات حقل، لا إعداد تخزين مؤقت
الخطأ التالي هو معاملة الحداثة كمسألة تخزين مؤقت. وهي ليست كذلك، والتمييز يستحق الدقة لأن المفردات متداخلة.
فـ use cache مع cacheLife تتحكم بكم يحتفظ Next.js بنتيجة محسوبة قبل إعادة
حسابها. وهذا قول عن عرضك أنت. ولا يقول شيئًا البتة عن قِدم الأرقام تحته، لأن
إعادة الحساب تقرأ صف قاعدة البيانات نفسه الذي كتبه الموصّل آخر مرة في السادسة
والنصف صباحًا. اضبط أقصر عمر تخزين تريده، ولن يصير إجمالي الفاتورة أحدث.
فعمر البيانات إذًا يجب أن يُنمذَج بوصفه بيانات. كل كيان مزامن يحمل ختم التشغيلة التي أنتجته، وذلك العمود ليس تشخيصًا - إنه حقل تعرضه الواجهة:
export async function AccountBalance({ accountId }: { accountId: string }) {
const account = await db.account.findUnique({ where: { id: accountId } })
const age = Date.now() - account.syncedAt.getTime()
return (
<section>
<Amount value={account.balance} />
{age > 2 * 60 * 60 * 1000 ? (
<Warning>
آخر تحديث {formatRelative(account.syncedAt)}. لم يُبلّغ النظام
المحاسبي منذ ذلك الحين.
</Warning>
) : (
<Note>حتى {formatTime(account.syncedAt)}</Note>
)}
</section>
)
}والساعتان ليستا رقمًا سحريًا، وعليك اشتقاق رقمك من عدد مرات تشغيل الموصّل المفترضة. المقصد أن للمكوّن ثلاث حالات لا حالتين، وأن الثالثة - موجودة لكنها قديمة - هي الحالة التي سيقضي فيها النظام أيامه السيئة فعلًا. والصفحة التي تعرض رصيدًا عمره أربع عشرة ساعة كما تعرض رصيدًا طازجًا ليست عطلًا في التخزين المؤقت. إنها تصميم لم يفكر في الحالة أصلًا.
بعض الأرقام لا تُعرض قديمة إطلاقًا
بعد أن تقرر أن أغلب القيم يمكن عرضها مع عمرها، قرّر على حدة أيها لا يمكن.
رصيد مستحق من هذا الصباح مفيد وموسوم بوضوح. أما حدّ الائتمان الذي يقرر إن كان الطلب يُقبل فهو نوع آخر من الأرقام، وعرض قديمٍ منه يعني قبول طلب كان فريق المالية سيرفضه. والسعر التعاقدي مثله. والمخزون المتاح كذلك، إن كان أحدهم على وشك الوعد به لعميل.
في هذه، تقول الواجهة الصادقة إن الفحص متعذّر الآن وتعرض الخطوة التالية - تعليق الطلب للمراجعة، أو إخبار العميل بأن أحدهم سيؤكد له. إنها تجربة أسوأ من رقم، وأفضل من رقم خاطئ، وسيخبرك فريق المالية أي الحقول تنتمي إلى هذه الفئة في نحو عشر دقائق إن سألته مباشرة.
الكتابة في الاتجاه الآخر وعد مختلف
القراءة هي الاتجاه السهل. أما نموذج في تطبيقك يُنشئ شيئًا في الطرف الآخر فهو حيث تُختبر البنية، لأنك لا تستطيع التأكيد.
تكتب الـ Server Action الطلب في قاعدة بياناتك أنت وتعود. هذا كل ما تستطيع قوله بصدق. أما وصول السجل إلى Sage فمعلّق بتشغيلة موصّل قد تبعد دقائق، ولا سبيل للدالة أن تنتظرها دون إبقاء طلب مفتوحًا مدة لا تسمح بها أي منصة.
ما يعني أن "قيد الانتظار" حالة حقيقية لها صفّها وشاشتها ومسار فشلها، لا مؤشر دوران. يرسل المستخدم، فيرى أنه وُضع في الطابور، ثم يراه يتحول إلى مؤكد حين تُبلّغ مزامنة لاحقة. وإن فشل التحقق في جهة Sage - حساب عميل غير موجود، أو حساب دفتري أُعيدت تسميته - فلا بد أن يصل ذلك الفشل إلى مكان ينظر إليه إنسان، لأنه لم يعد هناك طلب يُعاد إليه خطأ.
وبناء هذا كواجهة متفائلة ثم الأمل بالأفضل هو أكثر الطرق شيوعًا لخسارة هذه المشاريع أسبوعًا في الشهر الأول بعد الإطلاق. الكتابة إما وقعت وإما لا، والتطبيق هو الشيء الوحيد القادر على حمل هذا الفرق.
ما تجنيه من ذلك
لا شيء مما سبق التفاف حول قيد. نموذج القراءة المبني هكذا أسرع من أي نداء آني كان ليكون، ويبقى عاملًا حين لا يعمل خادم المحاسبة، ويرتبط ببقية بياناتك، ويمكن فهرسته للاستعلامات التي تجريها صفحاتك فعلًا بدل تلك التي صُمّمت لها واجهة محاسبية.
وما يكلّفه هو الصدق: للأرقام عمر، والواجهة تقوله، والكتابات طلبات لا وقائع. والفرق التي تبني ذلك من البداية تطلق بوابة يثق بها الناس. والفرق التي تضيفه بعد أول بلاغ دعم تعيد كتابة المكوّنات الأهم بالذات.
مشاريعنا المؤسسية على هذا الشكل في معظمها، والحد بين التطبيق ونظام السجل المعتمد هو الجزء الذي نحدّد نطاقه أولًا.
