פיתוח שירותי Medusa.js מותאמים אישית
דמיינו שחנות Medusa שלכם מעבדת 1000 הזמנות ביום. המשימה המורכבת הראשונה: תוכנית נאמנות עם צבירת נקודות על רכישות ומימוש להנחות. אין מודול מוכן ללוגיקה כזו. יישום בנתיבי API מוביל לקוד ספגטי ולשאילתות N+1 שמאטות את התגובה ל-2 שניות. שירותים מותאמים אישית ב-Medusa הם מחלקות TypeScript שחיות במיכל IoC ויכולות לעשות הכל: מ-CRUD ועד אינטגרציה עם שירותים חיצוניים. צברנו ניסיון מספק בעיצוב שירותים ל-Medusa ואנחנו מוכנים לחלוק פתרונות מוכחים. להלן ניתוח באמצעות מודול הנאמנות כדוגמה.
בעיות ששירות מותאם אישית פותר
הצבת לוגיקה עסקית ישירות בבקרים מובילה לשאילתות N+1, חוסר יכולת שימוש חוזר וקשיים בבדיקות יחידה. שירות מותאם אישית מכיל הכל: פעולות מסד נתונים, קריאות API, חישובים. אתם מקבלים מודול שניתן לקרוא מ-workflows, מנויים או מלוח הניהול. זה מקצר את זמן התחזוקה פי 2–3 בהשוואה לגישת ה"מונוליתית". אנו מבטיחים שהשירות יעוצב תוך התחשבות בתרחישי כשל וניסיונות חוזרים אופייניים. לפי תיעוד Medusa, שירותים מותאמים אישית הם הדרך המומלצת לארגון לוגיקה מורכבת.
כיצד שירות מותאם אישית מבטל את בעיית N+1
שאילתות N+1 הן בעיה נפוצה בעבודה עם ישויות קשורות. במקום לבצע שאילתה אחת עם JOIN, הבקר מבצע לולאה של N שאילתות. שירות מותאם אישית פותר זאת באמצעות מאגרי נתונים ואגרגציה. לדוגמה, שירות נאמנות יכול לקבל את סכום הנקודות עבור כל הלקוחות בשאילתת SQL אחת, לא אחת אחת. זה מקצר את זמן התגובה ב-60% ומפחית את עומס מסד הנתונים.
איך אנחנו עושים את זה (הוכחה טכנית)
להלן דוגמה מלאה לשירות נאמנות, כולל רישום מודול ושימוש ב-Workflow.
// src/modules/loyalty/service.ts import { MedusaContainer, Logger } from '@medusajs/framework/types'; type LoyaltyPoint = { customerId: string; points: number; reason: string; orderId?: string; }; export default class LoyaltyService { protected logger: Logger; private db: any; // MikroORM or raw query constructor({ logger }: { logger: Logger }) { this.logger = logger; } async getCustomerPoints(customerId: string): Promise<number> { const result = await this.db.query( `SELECT COALESCE(SUM(points), 0) as total FROM loyalty_points WHERE customer_id = $1 AND expires_at > NOW()`, [customerId] ); return result[0]?.total ?? 0; } async addPoints(data: LoyaltyPoint): Promise<void> { this.logger.info(`Adding ${data.points} points to customer ${data.customerId}`); await this.db.query( `INSERT INTO loyalty_points (customer_id, points, reason, order_id, created_at, expires_at) VALUES ($1, $2, $3, $4, NOW(), NOW() + INTERVAL '1 year')`, [data.customerId, data.points, data.reason, data.orderId ?? null] ); } } // src/modules/loyalty/index.ts import { Module } from '@medusajs/framework/utils'; import LoyaltyService from './service'; export const LOYALTY_MODULE = 'loyaltyModuleService'; export default Module(LOYALTY_MODULE, { service: LoyaltyService }); // medusa-config.ts defineConfig({ modules: [{ resolve: './src/modules/loyalty' }] }); עכשיו אנחנו משתמשים בשירות ב-workflow (שלב להוספת נקודות לאחר הזמנה):
import { createStep, StepResponse } from '@medusajs/framework/workflows-sdk'; const addLoyaltyPointsStep = createStep( 'add-loyalty-points', async (input: { orderId: string; customerId: string; orderTotal: number }, ctx) => { const service: LoyaltyService = ctx.container.resolve(LOYALTY_MODULE); const pointsToAdd = Math.floor(input.orderTotal / 100); await service.addPoints({ customerId: input.customerId, points: pointsToAdd, reason: 'order_completed', orderId: input.orderId, }); return new StepResponse({ pointsAdded: pointsToAdd }, { customerId: input.customerId, pointsToAdd }); }, async ({ customerId, pointsToAdd }, ctx) => { const service: LoyaltyService = ctx.container.resolve(LOYALTY_MODULE); await service.addPoints({ customerId, points: -pointsToAdd, reason: 'rollback' }); } ); השוואת סוגים
| סוג | מטרה | מתי להשתמש |
|---|---|---|
| שירות מודול | CRUD לישות מודול | ישות עם פעולות סטנדרטיות (למשל, מוצרים, עגלות) |
| שירות מותאם אישית | לוגיקה עסקית שרירותית | נדרשת לוגיקה ספציפית (נאמנות, סנכרון ERP) |
| שלב Workflow | שלב ב-Workflow | פעולה לשימוש חוזר במספר תרחישים |
רשימת בדיקה לפיתוח שירות מותאם אישית
- הגדרת תלויות (logger, מסד נתונים, API)
- יישום ממשק השירות
- רישום המודול ב-
// src/modules/loyalty/service.ts import { MedusaContainer, Logger } from '@medusajs/framework/types'; type LoyaltyPoint = { customerId: string; points: number; reason: string; orderId?: string; }; export default class LoyaltyService { protected logger: Logger; private db: any; // MikroORM or raw query constructor({ logger }: { logger: Logger }) { this.logger = logger; } async getCustomerPoints(customerId: string): Promise<number> { const result = await this.db.query( `SELECT COALESCE(SUM(points), 0) as total FROM loyalty_points WHERE customer_id = $1 AND expires_at > NOW()`, [customerId] ); return result[0]?.total ?? 0; } async addPoints(data: LoyaltyPoint): Promise<void> { this.logger.info(`Adding ${data.points} points to customer ${data.customerId}`); await this.db.query( `INSERT INTO loyalty_points (customer_id, points, reason, order_id, created_at, expires_at) VALUES ($1, $2, $3, $4, NOW(), NOW() + INTERVAL '1 year')`, [data.customerId, data.points, data.reason, data.orderId ?? null] ); } } // src/modules/loyalty/index.ts import { Module } from '@medusajs/framework/utils'; import LoyaltyService from './service'; export const LOYALTY_MODULE = 'loyaltyModuleService'; export default Module(LOYALTY_MODULE, { service: LoyaltyService }); // medusa-config.ts defineConfig({ modules: [{ resolve: './src/modules/loyalty' }] });וב-import { createStep, StepResponse } from '@medusajs/framework/workflows-sdk'; const addLoyaltyPointsStep = createStep( 'add-loyalty-points', async (input: { orderId: string; customerId: string; orderTotal: number }, ctx) => { const service: LoyaltyService = ctx.container.resolve(LOYALTY_MODULE); const pointsToAdd = Math.floor(input.orderTotal / 100); await service.addPoints({ customerId: input.customerId, points: pointsToAdd, reason: 'order_completed', orderId: input.orderId, }); return new StepResponse({ pointsAdded: pointsToAdd }, { customerId: input.customerId, pointsToAdd }); }, async ({ customerId, pointsToAdd }, ctx) => { const service: LoyaltyService = ctx.container.resolve(LOYALTY_MODULE); await service.addPoints({ customerId, points: -pointsToAdd, reason: 'rollback' }); } ); - כתיבת בדיקות יחידה ללוגיקה קריטית
- הוספת JSDoc עם תיאורי מתודות
- בדיקת אינטגרציה עם Workflow ונתיבי API
תהליך והערכת עלות
תהליך: ניתוח דרישות → עיצוב שירות ותלויות → יישום → בדיקות → פריסה. אורך בין יום אחד לפתרונות פשוטים ועד 3 שבועות למורכבים. העלות מחושבת באופן אישי לפי מורכבות. תקציב הפרויקט נדון בתחילת הדרך. הזמינו פיתוח וקבלו לוגיקה יציבה ללא עלויות נוספות.
הערכות זמן
| סוג שירות | זמן משוער |
|---|---|
| פשוט (1–2 פעולות, מקור נתונים אחד) | 1–2 ימים |
| עם אינטגרציית API חיצונית ולוגיקת ניסיונות חוזרים | 3–5 ימים |
| מורכב (לוגיקת נאמנות, תמחור B2B, מלאי מותאם אישית) | 1–3 שבועות |
מה כלול בפיתוח שירות מותאם אישית
אנחנו מספקים חבילה מלאה:
- ארכיטקטורה ועיצוב שירות
- יישום TypeScript לפי שיטות העבודה המומלצות של Medusa
- בדיקות (יחידה + אינטגרציה)
- תיעוד (README, JSDoc, דוגמאות קריאה)
- סיוע בפריסה והגדרת CI
- הדרכת הצוות שלכם לעבודה עם השירות
צרו קשר — נעריך את הפרויקט שלכם ונציע פתרון אופטימלי. זו השקעה ביציבות ובצמיחה של המסחר האלקטרוני שלכם.
כיצד שירות מותאם אישית פותר את בעיית הטרנזקציונליות
בנוסף ל-N+1, האטומיות של פעולות חשובה. ל-Medusa יש index.ts מובנה שמאפשר לשלב מספר שלבים בטרנזקציה אחת. שירות מותאם אישית משתמש בו כדי להבטיח שלמות נתונים: לדוגמה, צבירת נקודות והחלת הנחה מבוצעות בטרנזקציה אחת. זה מבטל דה-סנכרון ומפשט את הניפוי.







