ویرگول
ورودثبت نام
سمانه شریفی
سمانه شریفی
سمانه شریفی
سمانه شریفی
خواندن ۳ دقیقه·۱ ماه پیش

EF Core vs DDD: کِی Navigation Property بگذاریم و کِی فقط ID؟

در توسعه نرم‌افزارهای تجاری، به‌خصوص پروژه‌هایی که از معماری Domain-Driven Design (DDD) و Entity Framework Core استفاده می‌کنند، یکی از چالش‌های رایج این است:

«آیا باید Navigation Property داشته باشیم یا فقط ID ذخیره کنیم؟»

این سؤال ساده در ظاهر، اما در واقع تأثیر عمیقی روی طراحی دامنه، عملکرد، استقلال ماژول‌ها و مرزبندی Aggregateها دارد.

در این مقاله با یک مثال واقعی (سیستم تخفیف در ماژول Booking) بررسی می‌کنیم که چرا و چه زمانی باید Navigation Property را نگه داریم یا حذف کنیم.


🧩 بخش ۱: درک Aggregate و قوانین ارتباط در DDD

در معماری DDD، موجودیت‌ها در Aggregate‌ها سازمان‌دهی می‌شوند.
هر Aggregate شامل:

  • یک Aggregate Root

  • موجودیت‌ها یا Value Objectهای داخلی

✳ قانون طلایی DDD

Aggregateها نباید به‌صورت مستقیم به Aggregateهای دیگر ارجاع شیء–به–شیء داشته باشند.

یعنی موجودیت‌های یک Aggregate نباید Navigation Property به موجودیت‌های Aggregate دیگر داشته باشند.

این قانون باعث:

  • استقلال دامنه‌ها

  • کاهش coupling

  • جلوگیری از بارگذاری‌های غیرضروری

  • شفافیت قوانین تجاری

می‌شود.


🧩 بخش ۲: نقش Navigation Property در EF Core

EF Core اعتقاد دارد روابط را بهتر بفهمد، بنابراین معمولاً وجود Navigation Property مثل:

public Service Service { get; set; }

به آن کمک می‌کند:

  • Lazy/Eager Loading

  • Include()

  • Mapping آسان‌تر به DTO

  • Tracking بهتر

اما در DDD، این دقیقاً چیزی است که نباید برای Aggregateهای مختلف استفاده شود.


🧩 بخش ۳: نمونه موردی — DiscountRule و Service

فرض کنیم در یک سیستم رزرو خدمات (Booking System) ساختاری شبیه زیر داریم:

  • DiscountCard (Aggregate Root)

    • DiscountRule (Entity)

  • Service (Aggregate Root مستقل)

DiscountRule:

public class DiscountRule : BaseEntity { public Guid DiscountCardId { get; set; } public Guid? ServiceId { get; set; } // فقط شناسه public DateTime? FromDate { get; set; } public DateTime? ToDate { get; set; } public decimal? Price { get; set; } public decimal? Percent { get; set; } public DiscountCard DiscountCard { get; set; } // داخل Aggregate }

نکته مهم

DiscountRule و DiscountCard متعلّق به یک Aggregate هستند، اما Service نه.

بنابراین Navigation زیر حذف می‌شود:

// public Service Service { get; set; } ❌

🎨 بخش ۴: شماتیک بصری Aggregateها

✔ ارتباط مجاز (درون یک Aggregate)

DiscountCard (Root) └── DiscountRule

تميیز:

DiscountRule → DiscountCard ✔ Navigation Allowed

❌ ارتباط غیرمجاز (دو Aggregate مستقل)

DiscountRule ----X----> Service

چرا؟

چون:

  • Aggregateها باید مستقل باشند

  • اعمال قوانین دامنه نباید مرز Aggregateها را بشکند

  • EF Core نباید این دو را در یک درخت آبجکت لود کند


🧩 بخش ۵: اگر Navigation حذف شود چه می‌شود؟

🔹 رفتار درست در DDD

DiscountRule فقط شناسه Service را نگه می‌دارد:

public Guid? ServiceId { get; set; }

اگر Service لازم شد، در لایه Application بازیابی می‌شود:

var rule = await _ruleRepo.GetByIdAsync(id); var service = await _serviceRepo.GetByIdAsync(rule.ServiceId); return new DiscountRuleDto(rule, service);

Aggregate DiscountCard هیچ وابستگی به Service پیدا نمی‌کند.


🧩 بخش ۶: آیا همیشه باید Navigation را حذف کنیم؟

خیر!

نوع رابطهNavigationتوضیحEntity → Root (در یک Aggregate)✔ مجازمثل DiscountRule → DiscountCardAggregate → Aggregate❌ ممنوعمثل DiscountRule → ServiceValue Object → Entity✔ مجازمشکلی نداردچند به چند میان Aggregateها❌ ممنوعباید به صورت ID طراحی شود


🚀 بخش ۷: مزایای این کار در پروژه واقعی

✔ استقلال ماژول‌ها

Aggregate رزرو (Booking) به Aggregate Service وابسته نمی‌شود.

✔ جلوگیری از بارگذاری غیرضروری

Includeهای سنگین حذف می‌شوند.

✔ سادگی قوانین دامنه

هر Aggregate قوانین خودش را کنترل می‌کند.

✔ امکان تغییر در آینده

سرویس‌ها یا قوانین تخفیف می‌توانند بدون شکستن دیگری تغییر کنند.

✔ پیاده‌سازی بهتر CQRS و Microservices

Aggregateها مستقل‌تر می‌شوند.


🎯 نتیجه‌گیری

تمایز بین Navigation Property و Reference by ID یک تصمیم ساده نیست، بلکه قلب معماری DDD است.

به‌صورت خلاصه:

  • اگر دو موجودیت داخل یک Aggregate هستند → Navigation بگذارید.

  • اگر دو موجودیت از دو Aggregate متفاوت هستند → Navigation را حذف کنید و فقط ID نگه دارید.

با رعایت این اصل، سیستم شما:

  • مقیاس‌پذیرتر

  • تمیزتر

  • مستقل‌تر

  • با قابلیت توسعه بیشتر

خواهد بود.

۰
۰
سمانه شریفی
سمانه شریفی
شاید از این پست‌ها خوشتان بیاید