# بناء منصة SaaS متعددة المستأجرين بلغة Go: CircleXO على ToGO

> كيف بُنيت CircleXO، المنصة التي تجمع تطبيقات الأعمال بحساب واحد وفاتورة واحدة ورابط MCP واحد، على ToGO: أمان مستوى الصف في Postgres لكل مؤسسة، وعقد للتطبيقات، واتحاد MCP.

*2026-10-19T12:49:05Z*

# بناء منصة SaaS متعددة المستأجرين بلغة Go: CircleXO على ToGO

![دراسة حالة CircleXO على ToGO](https://to-go.dev/uploads/sites/c797a67b-97b7-4ed5-8459-baafb6981307/media/circlexo-togo-case-study-ar.png)

CircleXO منصة تجمع تطبيقات الأعمال: حساب واحد، ومؤسسة واحدة، وفاتورة واحدة، ورابط MCP واحد لعائلة كاملة من المنتجات (مهام، ومحرّك، وحوسبة، وذكرى وغيرها). هي **منصة SaaS متعددة المستأجرين مكتوبة بلغة Go**. كل طلب يتبع مؤسسة بعينها، ولا يجوز لقاعدة البيانات أن تُظهر صفوف مؤسسة لمؤسسة أخرى أبدًا.

تشرح هذه الدراسة كيف بُنيت المنصة العاملة الآن على [circlexo.com](https://circlexo.com/?utm_source=to-go.dev&utm_medium=case-study) فوق [ToGO](https://github.com/togo-framework/togo): ما الذي يقدمه ToGO، وما الذي كان على المنصة أن تبنيه بنفسها، وما الذي كان صعبًا. مستودع CircleXO خاص، لذلك نشرح البنية بالكلام دون مقتطفات من الكود.

## المشكلة

كان لكل منتج في العائلة تسجيله الخاص، وجدول مستخدميه، وفوترته. العميل الذي يستخدم ثلاثة منها كان يملك ثلاثة حسابات ويستلم ثلاث فواتير، ووكلاء الذكاء الاصطناعي عنده يحتاجون ثلاث نقاط MCP وثلاثة رموز دخول.

كان على المنصة أن توفّر:

- **هوية واحدة.** تسجيل دخول مرة واحدة (OpenID Connect) ثم الدخول إلى أي تطبيق مباشرة.
- **المؤسسات كمستأجرين.** الأعضاء والأدوار والاشتراكات تتبع مؤسسة، وكل استعلام محصور في مؤسسة واحدة.
- **عقد للتطبيقات.** كل تطبيق يصف نفسه في ملف manifest: الروابط، وعناوين إعادة التوجيه في OIDC، وخطافات دورة الحياة (التجهيز والإزالة وفحص الحالة)، وأحداث الـ webhook التي يريدها، وأدوات MCP التي يقدمها.
- **اتحاد MCP.** رابط MCP واحد للمؤسسة. تمرّر المنصة كل استدعاء أداة إلى التطبيق صاحب الأداة، بشرط أن تكون المؤسسة مشتركة في ذلك التطبيق.

## لماذا ToGO

واجهة CircleXO البرمجية مكتوبة بـ Go وتطبيق الويب بـ React، وهذا هو شكل ToGO بالضبط: واجهة Go (Huma لـ REST/OpenAPI، وgqlgen لـ GraphQL)، وsqlc لاستعلامات مُحدَّدة الأنواع، وترحيلات SQL بصيغة Atlas، وواجهة React في مستودع واحد. لم تحتج المنصة إلى اختيار هذه القطع وربطها بنفسها. أول commit في المستودع (24 أغسطس 2026) عنوانه حرفيًا "CircleXO rebuild: blueprint + ToGo phase-0 scaffold".

السبب الثاني هو نموذج الإضافات (plugins). كل قدرة تتشاركها المنصة مع بقية المنتجات موجودة أصلًا كإضافة في ToGO، ومهام ومحرّك وحوسبة وذكرى تعمل على الإضافات نفسها. نموذج تفكير واحد يغطي العائلة كلها.

## البنية

**إضافات ToGO المستخدمة.** مستوردة في `internal/plugins/plugins.gen.go`، وهو الملف الذي يولّده ToGO ويديره `togo install`:

| الإضافة | استخدامها في المنصة |
|---|---|
| `auth` (و`auth-dev` لتسجيل الدخول محليًا) | الجلسات ووسيط المصادقة |
| `db-postgres` | مشغّل Postgres |
| `cache` | عمليات بحث قصيرة العمر |
| `queue` | المهام في الخلفية |
| `realtime` | التحديثات الحية في تطبيق الويب |
| `storage` | الوسائط المرفوعة |
| `i18n` | العربية والإنجليزية |

كلها تعمل فوق نواة ToGO بالإصدار v0.21.0 كما هو مثبّت في `go.mod`.

**ما تبنيه المنصة فوقها.** حزم المنصة الخاصة في `internal/hub/`:

- **الهوية:** `accounts` و`orgs` و`profile` و`oidc` (المنصة هي نفسها مزوّد OpenID Connect)
- **التجارة:** `billing` و`store` و`registry` (ناشرو التطبيقات وإصداراتها)
- **الاتحاد:** `gateway`، ومعه خوادم MCP للإدارة (`adminmcp`) والبيانات (`datamcp`) ورحلات الإعداد (`journeymcp`)
- **العزل بين المستأجرين:** `hubdb`

**العزل بأمان مستوى الصف في Postgres.** لا تعتمد المنصة على أن يتذكر كل استعلام إضافة `WHERE org_id = …`. تفتح `hubdb` معاملة (transaction) محصورة في مؤسسة، وتضبط ثلاثة إعدادات محلية للمعاملة عبر `set_config(…, true)`: المؤسسة، والمستخدم، وعلامة النظام. سياسات أمان مستوى الصف (RLS) في الجداول تقارن كل صف بهذه الإعدادات. ولأن الإعدادات محلية للمعاملة، لا يمكن لاتصال من مجمّع الاتصالات أن ينقل سياق مستأجر إلى الطلب التالي.

**عقد التطبيقات.** كل تطبيق يحمل ملف `circlexo.app.yaml` (الإصدار 1)، ويُتحقَّق منه مقابل JSON Schema. أداة سطر الأوامر في المنصة تتحقق من الملف بالأمر `circlexo app validate`. في المستودع ملفات manifest لـ 14 تطبيقًا من تطبيقات العائلة. يعلن الملف عن نقطة MCP، وطريقة النقل (streamable HTTP)، وطريقة المصادقة (token exchange)، وأثر كل أداة (قراءة أو كتابة). وتستخدم البوابة هذا الأثر في سياساتها.

**اتحاد MCP.** تبني البوابة فهرسًا لكل مؤسسة: أي التطبيقات يمكنها الوصول إليها، وأي أدوات يقدمها كل تطبيق. تخزّن مخططات الأدوات مؤقتًا حسب إصدار التطبيق، وتسجّل كل استدعاء تمرّره في سجل تدقيق. يحصل الوكيل على رابط MCP واحد، ولا يرى إلا الأدوات التي يحق لمؤسسته استخدامها.

## الأرقام

مأخوذة من المستودع في 19 أكتوبر 2026، ولا شيء غيره:

- **8 إضافات ToGO** مستوردة (الجدول أعلاه) على ToGO v0.21.0. العدّ من `plugins.gen.go`.
- **57** ملف ترحيل للمنصة في `db/hub/migrations`، منها **31** ملفًا تتعامل مع أمان مستوى الصف.
- **14** ملف manifest لتطبيقات العائلة.
- **3** خوادم MCP في المنصة (الإدارة، والبيانات، والرحلات)، إضافة إلى بوابة الاتحاد.
- **236** commit منذ أول commit للهيكل في 24 أغسطس 2026.

لا توجد هنا أرقام زيارات أو عملاء، لأنه لم يُنشر منها شيء.

## ما كان صعبًا، وأين ساعد ToGO

**العزل بين المستأجرين مسؤولية التطبيق لا الإطار.** إضافة `auth` في ToGO تخبرك من هو المستخدم، لكنها لا تقرر أي صفوف يحق للاستعلام أن يراها. كتبت المنصة هذه الطبقة بنفسها (`hubdb`)، والجزء الصعب كان جعل نسيانها مستحيلًا: معاملات محصورة في مؤسسة مع أمان مستوى الصف، فإذا غاب الفلتر يُغلق الباب بدل أن يُفتح. دور ToGO كان ألا يقف في الطريق: الاستعلامات والمعاملات هي `database/sql` عادية في Go، فيلتف العزل حولها دون صراع مع ORM.

**مخططان وأمر واحد.** الأمر `togo migrate` يشغّل `cmd/migrate` الخاص بالتطبيق. في CircleXO يطبّق هذا الأمر مخطط التطبيق الأساسي، ثم ترحيلات المنصة المرقّمة إذا كانت قاعدة بيانات المنصة مضبوطة. توسيع خطوة الترحيل في الإطار احتاج بضعة أسطر في ملف واحد، لا نسخة معدّلة من الإطار.

**الإضافات جعلت العائلة متسقة.** المنتجات تستخدم أصلًا إضافات `auth` و`queue` و`realtime` و`i18n` نفسها، فصار "ضمّ تطبيق إلى المنصة" ملف manifest وعددًا قليلًا من نقاط النهاية (التجهيز، والإزالة، وفحص الحالة، والأحداث)، لا إعادة بناء.

**حدود صريحة.** CircleXO في مرحلة انتقال. يجري تصميم إصدار قادم كمستودع ToGO واحد، تُبنى فيه المنصة وكل تطبيق كملف تنفيذي مستقل. هذه الدراسة تصف المنصة التي تعمل اليوم.

## روابط

- CircleXO: [circlexo.com](https://circlexo.com/?utm_source=to-go.dev&utm_medium=case-study)
- ToGO على GitHub: [github.com/togo-framework/togo](https://github.com/togo-framework/togo)
- كل المنتجات المبنية على ToGO: [معرض ToGO](https://to-go.dev/ar/showcase)
- دراسات حالة أخرى: [SeatFor](https://to-go.dev/ar/blog/seatfor-togo-case-study)، [ذكرى](https://to-go.dev/ar/blog/zekra-togo-case-study)، [مهام](https://to-go.dev/ar/blog/mahaam-togo-case-study)، [محرّك](https://to-go.dev/ar/blog/moharrik-togo-case-study)، [حوسبة](https://to-go.dev/ar/blog/hosbah-togo-case-study)

## أسئلة شائعة

**هل CircleXO مفتوح المصدر؟**
لا. مستودع CircleXO خاص، ولهذا لا تحتوي الدراسة على مقتطفات من الكود. أما ToGO نفسه فمفتوح المصدر.

**هل يتولى ToGO العزل بين المستأجرين نيابة عنك؟**
لا. يقدم ToGO المصادقة، ومشغّل قاعدة البيانات، والطوابير، والتحديثات الحية وغيرها كإضافات. أما العزل بين المستأجرين فهو كود التطبيق، وCircleXO ينفّذه بمعاملات محصورة في مؤسسة وبأمان مستوى الصف في Postgres.

**لماذا أمان مستوى الصف بدل شرط WHERE في كل استعلام؟**
شرط WHERE المنسي يسرّب البيانات. أما سياسة أمان مستوى الصف فتُغلق الباب: إذا لم تُضبط المؤسسة على المعاملة، لا يرى الاستعلام أي صفوف لمستأجر.

**ما هو اتحاد MCP؟**
نقطة MCP واحدة لكل مؤسسة. تمرّر المنصة كل استدعاء أداة إلى التطبيق صاحبها، بعد التحقق من اشتراك المؤسسة، وتسجّل الاستدعاء.

**أي إصدار من ToGO يستخدم CircleXO؟**
النواة مثبّتة على الإصدار v0.21.0 في `go.mod`.

---

Source: https://to-go.dev/ar/blog/circlexo-togo-case-study
