مارکت‌پلیس · مارکت‌پلیس مواد غذایی — آلمان و اتحادیه‌ی اروپا

پلاز‌مارکت: مارکت‌پلیسی در آلمان که قانون در مدل دامنه‌اش نوشته شده

مارکت‌پلیس چندفروشندگی مواد غذایی برای بازار آلمان، در حال بهره‌برداری: پنج زبان (دو تا راست‌به‌چپ)، 15 زمینه‌ی محدود، تسویه‌ی چندفروشنده و فاکتور مطابق قانون آلمان — روی .NET 10، Next.js و PostgreSQL.

کارفرما
PlazMarket
حوزه
مارکت‌پلیس مواد غذایی — آلمان و اتحادیه‌ی اروپا
سال
1404
وضعیت
در حال بهره‌برداری
  • تحلیل و کشف نیاز
  • طراحی تجربه و رابط کاربری
  • معماری نرم‌افزار
  • توسعه فرانت‌اند
  • توسعه بک‌اند و API
  • طراحی پایگاه داده
  • یکپارچه‌سازی با سیستم‌های دیگر
  • استقرار و DevOps
  • سئوی فنی
  • تست و تضمین کیفیت
  • پشتیبانی و توسعه مستمر

نتیجه در عدد

15
زمینه‌ی محدود با داده‌ی مستقل

238 نقطه‌ی پایانی در 19 ماژول

845
تست خودکار

واحد و یکپارچگی، روی پایگاه داده‌ی واقعی

5
زبان

دو تای آن‌ها راست‌به‌چپ

چالش

کار سختِ یک مارکت‌پلیس آلمانی، سبد خرید نیست. یک برچسب قیمت آلمانی باید به پرسش‌هایی جواب بدهد که یک اسکیمای معمولی فروشگاهی اصلاً نمی‌پرسد:

قیمت هر کیلو چقدر است — بر اساس قیمتی که خریدار امروز واقعاً می‌پردازد، نه قیمت فهرست؟ چه بخشی از مبلغ، ودیعه‌ی قابل بازگشتِ ظرف است که اصلاً جزو قیمت کالا نیست؟ اگر هفته‌ی پیش تخفیف داشته، کمترین قیمت در سی روز قبل چقدر بوده؟ هر کدام از این‌ها اشتباه باشد، عددِ روی صفحه فقط نامرتب نیست — اظهار نادرست قیمت است.

بعد از فروش هم همین‌طور: فاکتور باید شماره‌ی بدون حفره و فیلدهای الزامی داشته باشد و اگر چیزی مرجوع شد، سند اصلاحی همان مشخصات را تکرار کند. پول بین فروشنده‌ها تقسیم می‌شود، تا پایان مهلت مرجوعی نگه داشته می‌شود و بعد پرداخت می‌شود — و هر قدم باید سندی بسازد که ممیز مالیاتی بتواند دنبالش کند و هیچ کدی نتواند بی‌صدا بازنویسی‌اش کند.

روی همه‌ی این‌ها، سایت باید در پنج زبان کار کند که دو تای آن‌ها — فارسی و عربی — راست‌به‌چپ‌اند.

راه‌حل

قانون به‌جای اینکه روی سامانه وصله شود، در مدل دامنه نوشته شد. قیمت واحد از قیمت قابل پرداخت محاسبه می‌شود، پس شروع کمپین رقم هر کیلو را با خودش می‌برد. ودیعه یک مقدار دامنه‌ای با رفتار مالیاتی خودش است و از پیش‌فاکتور سبد تا سطر فاکتور و بازپرداخت جدا می‌ماند. تاریخچه‌ی قیمت به‌ازای هر پیشنهاد ثبت می‌شود تا رقم «کمترین قیمت 30 روز» درست خوانده شود. مهلت مرجوعی از رده‌ی سیاستی می‌آید که در لحظه‌ی فروش به سفارش چسبیده، پس تغییر بعدی سیاست نمی‌تواند قولِ داده‌شده را عوض کند.

پول در سراسر سامانه Money است نه decimal، با یک قاعده‌ی گِرد کردن که یک بار روی هر سطر اعمال می‌شود؛ و جمع‌ها از پیش‌فاکتور به سفارش، فاکتور و تسویه منتقل می‌شوند، نه اینکه هر بار دوباره حساب شوند.

سه محصول ساخته شد روی یک بستر: فروشگاه با جست‌وجوی وجهی روی Elasticsearch و سفارش چندفروشنده‌ای که به بسته‌های مستقل تقسیم می‌شود؛ پنل فروشنده با احراز هویت کسب‌وکار، ورود گروهی CSV، کمپین، بسته‌بندی و مرجوعی، کمیسیون و نگهداشت ذخیره و صورت‌حساب تسویه؛ و پنل اپراتور با حاکمیت کاتالوگ، صف تأیید فاکتور، مالیات به تفکیک نرخ و اجرای پرداخت.

انطباق با GDPR و DSA هم به همین شکل ساختاری است: خط لوله‌ی درخواست داده‌ی کاربر روی همه‌ی زمینه‌ها پخش می‌شود با تأیید هویت ایمیلی و ساعت SLA؛ هویت حقوقی فروشنده روی صفحه‌ی فروشگاه عمومی است؛ و گزارش تخلف به کارشناس ارجاع می‌شود، نه اینکه به‌صورت خودکار کالا را بردارد.

قابلیت‌های کلیدی

  • سفارش چندفروشنده‌ای

    یک سبد، چند فروشنده؛ تقسیم به بسته‌های مستقل با ارسال و مرجوعی جدا.

  • تسویه و نگهداشت ذخیره

    کمیسیون، نگهداشت تا پایان مهلت مرجوع، بدهی و صورت‌حساب پرداخت — روی دفتری که ویرایش نمی‌پذیرد.

  • فاکتور بدون حفره

    شماره‌گذاری پیوسته به‌ازای هر صادرکننده زیر قفل سطر، با سند اصلاحی مطابق قانون.

  • جست‌وجوی وجهی

    برند، دسته، مبدأ، ارگانیک و قیمت روی Elasticsearch، با خانواده‌های تنوع در یک کارت.

  • احراز هویت کسب‌وکار فروشنده

    هویت حقوقی، شماره‌ی ثبت، شناسه‌ی مالیاتی، اطلاعات بانکی و بررسی مدارک پیش از فروش.

  • پنج زبان با رفتار محلی واقعی

    آلمانی، انگلیسی، ترکی، فارسی و عربی؛ تاریخ جلالی و هجری از داده‌ی محلی مرورگر، نه از استثنای دستی.

  • انطباق با GDPR و DSA

    خط لوله‌ی درخواست داده روی همه‌ی زمینه‌ها، هویت عمومی فروشنده و رسیدگی انسانی به گزارش تخلف.

  • 19 قالب ایمیل در پنج زبان

    HTML و متن ساده، با جهت نوشتار گیرنده، از دامنه‌ی احرازشده با SPF و DKIM و DMARC.

معماری و فناوری

سبک معماری
مونولیت ماژولار
الگوها
معماری پاک (Clean)طراحی دامنه‌محور (DDD)رویدادمحورالگوی OutboxCQRS

Clean Architecture روی پنج پروژه — دامنه، کاربرد، زیرساخت، API و یک هسته‌ی مشترک — با 15 زمینه‌ی محدود که هرکدام اسکیما و DbContext خودش را دارد. هیچ کوئری‌ای با join از مرز یک زمینه رد نمی‌شود؛ زمینه‌ها با رویدادهای دامنه حرف می‌زنند که با outbox تراکنشی تحویل داده می‌شوند، پس خطا در یکی، تراکنش دیگری را برنمی‌گرداند و هیچ رویدادی گم نمی‌شود.

دلیل این سخت‌گیری، پول است. هرچه درباره‌ی پول تصمیم می‌گیرد — مصرف کوپن، پرداخت به فروشنده، شماره‌گذاری پیوسته‌ی فاکتور — قفل سطر در یک تراکنش صریح می‌گیرد، و هر کدام تست همروندی دارد که درخواست‌های موازی واقعی را روی پایگاه داده‌ی واقعی اجرا می‌کند؛ این نقص‌ها فقط همان‌جا دیده می‌شوند، چون یک تقلبی درون‌حافظه‌ای ذاتاً سریالی است. دفتر تسویه در سطح پایگاه داده UPDATE و DELETE را رد می‌کند و اصلاح فقط با سند متقابل انجام می‌شود.

قاعده‌ی دیگری که در سراسر پروژه رعایت شده: یک تعریف برای هر قاعده. وقتی دو صفحه به یک جواب نیاز دارند — تعداد بسته، قیمت مؤثر، کلید کش — هر دو یک تابع را صدا می‌زنند، و همین ثابت مستقیم تست می‌شود.

ده سرویس میزبان کارهایی را می‌برند که شکل درخواست ندارند: توزیع outbox، ارسال ایمیل، جاروی انقضای پرداخت و رزرو موجودی، آزادسازی ذخیره، اعمال قواعد نگهداشت داده، و زمان‌بند قیمت که کمپین‌ها را شروع و تمام می‌کند.

بک‌اند و API
.NET 10C#MediatREntity Framework CoreREST APIClean Architecture

هشدارها به‌عنوان خطا، تحلیلگر API ممنوعه، 49 مایگریشن

پایگاه داده
PostgreSQL

PostgreSQL 17 — منبع حقیقت

کش
Redis

Redis 7 — کش و محدودیت نرخ توزیع‌شده

جست‌وجو
Elasticsearch

Elasticsearch 8.15 — جست‌وجو و وجه‌ها

فرانت‌اند
Next.jsReact.jsTypeScript

فروشگاه: App Router، کامپوننت سمت سرور، next-intl روی پنج زبان

فرانت‌اند
Vite

پنل فروشنده و اپراتور: SPA با TanStack Query و سیستم طراحی مشترک

زیرساخت و استقرار
DockernginxCloudflare

Docker Compose، پروکسی nginx، CDN و سرور ایمیل با SPF/DKIM/DMARC

یکپارچه‌سازی‌ها

  • درگاه پرداخت میزبانی‌شده با وب‌هوک، بازپرداخت و مدیریت چارج‌بک
  • شرکت‌های پستی برای تحویل و رهگیری بسته
  • سرور ایمیل خوداستقرار با SPF، DKIM و DMARC

پروژه‌ی مشابهی در ذهن دارید؟

یک تماس پانزده دقیقه‌ای کافی است تا بگوییم چه چیزی لازم است، چقدر طول می‌کشد و از کجا شروع کنیم.