ساخت یک توزیعکننده (Dispatcher) سفارشی Domain Events در NET. 🧱
Domain events
یک راه قدرتمند برای جداسازی (decouple) بخشهای مختلف سیستم شما هستند. به جای اتصال سفت و سخت منطق خود، میتوانید رویدادها را منتشر کرده و بخشهای دیگر کدتان را به آن رویدادها مشترک (subscribe) کنید. این الگو به ویژه در طراحی دامنه محور (Domain-Driven Design - DDD) ارزشمند است، جایی که منطق بیزینس باید متمرکز و منسجم باقی بماند.
در این مقاله، ما نحوه پیادهسازی یک توزیعکننده رویداد دامنه سبک و سفارشی را در NET. بررسی خواهیم کرد. منطق اصلی توزیع نباید به کتابخانههای شخص ثالث وابسته باشد.
📌ما پوشش خواهیم داد:
🔹 چرا ممکن است بخواهید از publish-subscribe در اپلیکیشن خود استفاده کنید.
🔹 نحوه تعریف انتزاعهای پایه رویداد دامنه.
🔹 نحوه پیادهسازی و ثبت handlerها.
🔹 نحوه ساخت یک توزیعکننده رویدادهای دامنه.
🔹 مزایا و معایب و زمان در نظر گرفتن گزینههای دیگر.
چرا Domain Events اهمیت دارند؟ 🤔
قبل از شیرجه زدن در پیادهسازی، بیایید مشکلی را که رویدادهای دامنه حل میکنند، درک کنیم. این کد با اتصال سفت و سخت را در نظر بگیرید:
public class UserService
{
public async Task RegisterUser(string email, string password)
{
var user = new User(email, password);
await _userRepository.SaveAsync(user);
// 🔗 مستقیماً به سرویس ایمیل متصل است
await _emailService.SendWelcomeEmail(user.Email);
// 🔗 مستقیماً به سرویس آنالیتیکس متصل است
await _analyticsService.TrackUserRegistration(user.Id);
// اگر بخواهیم ویژگیهای بیشتری اضافه کنیم چه؟
// این متد به رشد خود ادامه خواهد داد...
}
}
با رویدادهای دامنه، میتوانیم این را جدا کنیم: ✨
public class UserService
{
public async Task RegisterUser(string email, string password)
{
var user = new User(email, password);
await _userRepository.SaveAsync(user);
// رویداد را منتشر کن - بگذار بخشهای دیگر سیستم واکنش نشان دهند
await _domainEventsDispatcher.DispatchAsync(
[new UserRegisteredDomainEvent(user.Id, user.Email)]);
}
}
اکنون UserService فقط بر روی ثبت نام کاربر تمرکز دارد، در حالی که دغدغههای دیگر از طریق event handlerها مدیریت میشوند.
انتزاعهای پایه 🧬
بیایید با تعریف دو اینترفیس ساده که پایه و اساس سیستم رویداد ما را تشکیل میدهند، شروع کنیم:
// اینترفیس نشانگر برای تمام رویدادهای دامنه.
public interface IDomainEvent { }
// اینترفیس جنریک برای مدیریت رویدادهای دامنه.
public interface IDomainEventHandler<in T> where T : IDomainEvent
{
Task Handle(T domainEvent, CancellationToken cancellationToken = default);
}
پیادهسازی Handlerهای نمونه 📧📊
بیایید چند handler نمونه اضافه کنیم که نشان میدهند چگونه بخشهای مختلف سیستم شما میتوانند به یک رویداد یکسان واکنش نشان دهند:
// ارسال ایمیل خوشآمدگویی هنگام ثبت نام کاربر
internal sealed class SendWelcomeEmailHandler(...)
: IDomainEventHandler<UserRegisteredDomainEvent>
{
// ...
}
// ردیابی ثبت نام کاربر جدید در آنالیتیکس
internal sealed class TrackUserRegistrationHandler(...)
: IDomainEventHandler<UserRegisteredDomainEvent>
{
// ...
}
برای اینکه این کار کند، باید handlerهای خود را در کانتینر DI ثبت کنیم. میتوانید این ثبت را با اسکن اسمبلی با Scrutor خودکار کنید. نکته مهم این است که چندین handler میتوانند به یک رویداد یکسان واکنش نشان دهند.