TGViewer
C# Geeks (.NET) C# Geeks (.NET) @csharpgeeks · 550 subscribers
Post #494 276
3️⃣ Violation of Single Responsibility (SRP)

❗️ شما ممکن است استدلال کنید که چون ما از interfaceها (مثل IEmailService) استفاده می‌کنیم، decoupled هستیم. این برای جزئیات پیاده‌سازی درست است، اما برای orchestration اشتباه است.
UserService در حال حاضر دو دلیل برای تغییر دارد:

• Core Domain Logic:
"اکنون علاوه بر email به username نیز نیاز داریم."

• Notification Policy:
"تیم Marketing می‌خواهد علاوه بر Email یک SMS هم ارسال شود."

UserService
باید فقط مسئول state change باشد (ایجاد کاربر).نباید مسئول orchestrating کردن side effectها باشد.
🔥 این نقض کامل اصل Single Responsibility است.

Level 1: Logical Decoupling with Domain Events

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

برای این کار از Domain Events استفاده می‌کنیم.
در اینجا نسخهٔ refactor شدهٔ UserService را می‌بینید:
public class UserService(
IUserRepository userRepository,
IDomainEventDispatcher dispatcher,
IUnitOfWork unitOfWork)
{
public async Task RegisterUser(string email, string password)
{
// 1. Create the User Entity
var user = new User(email, password);

// 2. Capture the side effect as an event object
var userRegisteredEvent = new UserRegisteredEvent(user.Id, user.Email);

// 3. Add the entity to the repository
await userRepository.AddAsync(user);

// 4. Dispatch the event (Assuming in-process dispatching here for simplicity)
// Note: Handlers for Email and Analytics are now completely separate classes.
await dispatcher.Dispatch(userRegisteredEvent);

await unitOfWork.SaveChangesAsync();
}
}

اکنون UserService پایدار است.
اگر فردا بخواهیم یک قابلیت جدید مثل "Loyalty Points" اضافه کنیم، این متد هیچ تغییری نمی‌کند.
فقط یک handler جدید برای UserRegisteredEvent اضافه می‌کنیم.

اما هنوز مشکل reliability حل نشده. اگر سیستم دقیقاً بعد از Dispatch ولی قبل از SaveChangesAsync کرش کند چه؟
ممکن است email ارسال شود اما user ذخیره نشود.
یا برعکس: user ذخیره شود اما event از دست برود.

Level 2: Reliability with the Outbox Pattern

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

باید تضمین کنیم که اگر User ذخیره شد، UserRegisteredEvent نیز ذخیره شود.

اینجاست که Outbox Pattern وارد می‌شود.
به جای ارسال مستقیم event به message bus، ما event را در یک جدول OutboxMessages ذخیره می‌کنیم.
و این کار در همان transaction ذخیرهٔ user انجام می‌شود.

اینجا پیاده‌سازی کامل logic را می‌بینید:
public async Task RegisterUser(string email, string password)
{
// 1. Create the Domain Event
var user = new User(email, password);
var domainEvent = new UserRegisteredEvent(user.Id, user.Email);

// 2. Open a Transaction
using var transaction = dbContext.Database.BeginTransaction();

try
{
// 3. Save the User to the Users Table
dbContext.Users.Add(user);

// 4. Serialize the Event and Save to Outbox Table
var outboxMessage = new OutboxMessage
{
Id = Guid.NewGuid(),
Type = nameof(UserRegisteredEvent),
Content = JsonSerializer.Serialize(domainEvent),
OccurredOn = DateTime.UtcNow,
ProcessedOn = null // Null means it hasn't been handled yet
};

dbContext.OutboxMessages.Add(outboxMessage);

// 5. Commit BOTH changes atomically
await dbContext.SaveChangesAsync();
await transaction.CommitAsync();
}
catch
{
await transaction.RollbackAsync();
throw;
}
}

اکنون یک background worker (در یک process جدا) جدول OutboxMessages را poll می‌کند.
پیام را برداشته و آن را به message bus (RabbitMQ، Azure Service Bus و...) publish می‌کند.

اگر email service از کار بیفتد؟worker دوباره تلاش می‌کند.با این روش به At-Least-Once Delivery رسیده‌ایم.
More from @csharpgeeks
  1. Sep 22, 2026یه مدتی قراره از دنیای NET. فاصله بگیرم، چون وقتشه برم سربازی. راستش نمیدونم این مدت رو چج…
  2. Sep 20, 2026🔥 حالا مشکل اصلی: Alert Storm فرض کن Database از دسترس خارج شده. ۱۰۰ Pod داری. هر Pod می‌…
  3. Sep 20, 2026🚨 طراحی سیستم Monitoring و Alerting در یک سیستم بزرگ فرض کن ساعت ۳ صبح است. سیستم شما با…
  4. Sep 19, 2026#Engineering_Leadership تصمیم نگرفتن هم یک تصمیم است یه چیز عجیب توی تیم‌های مهندسی: گاهی…
  5. Sep 19, 2026☑ چک‌لیست آماده‌سازی تیم، فرایندها و زیرساخت برای توسعه با AI توجه: هیچ چک‌لیستی جهان‌شمول…
  6. Sep 19, 2026📌پایان یک انتظار طولانی: اعتبارسنجی ناهمگام (Async Validation) در NET 11.
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →