Derinlemesine yazılım eğitimleri için kanalımı takip edebilirsiniz...

Loosely Coupled Sistemler Oluşturmak İçin Domain Event’ler Nasıl Kullanılır?

Merhaba,

Bu yazımızda, günümüzde birçok modern yazılım tasarımı ve mimarisi tarafından tercih edilmekte olan Domain Event‘ler eşliğinde loosely coupled(gevşek bağlı) sistemler oluşturma yaklaşımı üzerine biraz hasbihalde bulunalım istiyorum. Bu süreçte domain event’ler ile integration event’lerden bahsedecek ve ikisi arasındaki temel farkların neler olduğunu değerlendirecek ve biryandan da domain event’lerin nasıl uygulandığına ve özellikle EF Core gibi bir framework ile nasıl işlendiğine ve MediatR gibi kütüphaneyle de nasıl handle edildiğine pratiksel olarak değiniyor olacağız. O halde buyrun başlayalım…

Bir yazılım sisteminde birbirinden farklı bileşenler/servisler/yapılar birbirlerine ne kadar sıkı bağımlı ise o sistemde ihtiyaç dahilinde olan değişikliklere karşın olası dirençlerin bir o kadar yüksek ihtimal olduğunu söyleyebiliriz. Bu mantıkla bu bileşenleri mümkün mertebe loosely coupling(gevşek bağlı) olacak şekilde tasarlarsak eğer sistemdeki herhangi bir değişiklik durumunda diğer kısımlar büyük sorunlara yol açmayacak, bahsi konu olan değişikliğe karşı direnç oldukça zayıf olacaktır.

İşte, bizler birbirleriyle gevşek bağlı bileşenler oluşturmak istiyorsak eğer Domain-Driven Design(DDD) felsefesinin taktiksel bir deseni olan domain event’lerden istifade edebiliriz.

Domain event’ler sayesinde sistem bileşenleri arasındaki sıkı bağlantıları gevşetebilir ve böylece bir bileşende meydana gelen/gelecek olan değişim(ler) neticesinde diğer bileşenlerin doğrudan etkilenmesinin büyük ölçüde önüne geçebiliriz. Böylece var olan özellikleri modifiye etmenin dışında, sisteme yeni özellikleri ekleme sürecinde de esneklikler kazandırılabilmetkedir.

Bileşenler arasındaki gevşek bağlılığın bir diğer getirisi, bileşenlerin daha iyi bir şekilde modüler olmasını ve böylece bir bileşenin, diğer bileşenlerin nasıl tepki vereceklerine dair bilgi edinmek zorunda olmama gibi bir özgürlüğe sahip olmasını sağlamaktadır. Bu durum da sistemdeki bağımsızlık düzeyini arttıracak ve özellikle dağıtık modülerliğe uygun bir nitelik arz edecektir.

Tüm bunların dışında domain event’ler, olayların gönderildiği anda işlenebileceği gibi hedef bileşenin iş yoğunluğuna yahut durumuna uygun olacak şekilde de belirli bir zamanlamada ele alınabilmekte ve böylece zamanlama ve uygulama bağımsızlığı açısından uyum sağlayabilmektedirler.

Şimdi gelin tüm bu altını çizdiğimiz prensipleri uygulamada gerçekleştirebilmemizi sağlayan domain event’lerin tam teferruatlı bir tanımını gerçekleştirerek içeriğimizin seyrine devam edelim.

Domain Events Nedir?

Loosely Coupled Sistemler Oluşturmak İçin Domain Event'ler Nasıl KullanılırDomain Event, bir yazılım sisteminde gerçekleşen önemli olayları temsil eden ve diğer bileşenlerle iletişimi sağlayan bir kavramdır. Yukarıdaki satırlarda da bahsedildiği gibi Domain-Driven Design(DDD) yaklaşımının bir parçasıdır ve mimaride loosely coupling’i teşvik etmek için kullanılır.

Domain event’i, sistemdeki önemli bir işlemin gerçekleşmesi neticesinde, etki(fact) olarak nitelendirebileceğimiz anahtar noktalar olarak düşünebiliriz. Misal olarak; bir e-ticaret uygulamasında bir siparişin tamamlanması neticesinde ‘OrderCompleted’ gibi bir domain event oluşturulabilir ve böylece bu event sayesinde sistemin farklı katmanlarına siparişin tamamlandığına dair bilgi göndererek iletişim sağlanabilir. Tabi bu iletişimin kurulabilmesi ve diğer bileşenler tarafından gerçekleşen olaya karşılık uygun tepkinin verilebilmesi için yayınlanmış olan domain event’e diğer bileşenler tarafından abone olunması gerekmektedir.

Domain event’ler, ayrıca sistemde asenkron işlemleri destelemek için de kullanılabilmektedirler. Buna da bir örnek vermemiz gerekirse eğer; sipariş tamamlandıktan sonra arka planda müşteriye bir teşekkür maili göndermek isteyebiliriz. Bunun için asenkron bir tutum sergilememizi sağlayan domain event’lerden yine istifade edebiliriz.

Bir olay/event, geçmişte olmuş bir şeyi/olayı ifade eder. O yüzden domain event’lere sürekli geçmiş bir isim verilmelidir!

Integration Events Nedir?

Integration event ise distributed sistemlerdeki farklı bileşenlerin ve hizmetlerin iletişimini kolaylaştırmak için kullanılan bir kavramdır. Genellikle microservice mimarisinde, servisler arası etkileşimi ve iletişimi kurgulamak için kullanılmaktadırlar. Yani integration event için domain event’in dağıtık mimarideki muadilidir diyebiliriz.

Integration event’lerin temel özellikleri şunlardır;

  • Asenkron İletişim
    Integration event’ler, yapıları gereği asenkron bir davranış sergilerler. Bir servis tarafından yayınlanmış olan integration event, diğer servisler tarafından alınana kadar o servis beklemek zorunda kalmamaktadır. Haliyle bu durumda asenkron bir süreç olduğu gözlemlenmektedir. Böylece, servislerin daha hızlı ve verimli bir şekilde çalışmasına ve aralarında iletişim kurmasına olanak tanınmaktadır.
  • Bağımsızlık
    Integration event’ler, diğer bileşenlerin veya servislerin iç yapısını bilmek zorunda değildirler. Yayınlanan event’ler, hedef servislerin ihtiyacına uygun şekilde işlenebilirler.
  • Entegrasyon Noktası
    Integration event’ler, farklı bileşenlerin ve hizmetlerin birbirleriyle iletişim kurmasını sağlayan bir entegrasyon noktasıdır ve bu event’ler sayesinde sistemdeki bileşenlerin bağımsız olarak geliştirilmesi ve ölçeklendirilmesi kolaylaşmaktadır.
  • Entegrasyon Senaryoları
    Integration event’ler, farklı sistemler arasında entegrasyon senaryolarını kolaylaştırır ve bir sistemdeki değişikliği, diğer sistemleri doğrudan etkilemeden haberleşme yoluyla bildirebilir.

Domain Event ile Integration Event Arasındaki Farklar Nelerdir?

Domain event ile integration event anlamsal olarak birebir aynıyken, amaçsal olarak ise tamamen farklı vizyonlara sahip kavramlardır.

Evet, anlamsal olarak aynıdırlar… Çünkü, her ikisi de geçmişte meydana gelen bir olayı temsil etmektedirler ve biryandan da olay tabanlı iletişim yaklaşımının bir ürünüdürler.

Amaçsal açıdan ise farklılık arz ederler. Domain event’ler; bir yazılım sisteminin içindeki bileşenler arasındaki iletişimi kolaylaştırmak için kullanılırken, integration event’ler ise farklı sistemler veya servisler arasındaki iletişimi kolaylaştırmak için kullanılırlar. Yani domain event’ler aynı yazılım sistemi içerisindeki bileşenler arasında in-memory üzerinden senkron veya asenkron işlev görürken, integration event’ler ise genellikle farklı sistemler arasında message broker’lar vasıtasıyla asenkron varlık gösterirler.

Ayrıca domain event’ler, genelikle bir bileşen tarafından üretilir ve yayınlanır. Diğer bileşenler ise bu olaylara abone olarak, dinleme işlevini yürütürler. Integration event’ler ise daha geniş bir ortamda iletişimi sağladığından, genellikle bir message broker aracılığıyla yönetilir. Bir servis tarafından olay yayınlandığında, bu message broker’a gönderilir ve diğer bileşenler bu olayları oradan takip ederek bütünsel işlevsellik ve iletişim sağlanmış olur.

Domain Events Nasıl Oluşturulur ve Uygulanır?

Şimdi içeriğimizi esas konu olan domain event’lere odaklı bir şekilde seyretmeye devam edelim ve domain event’leri tasarlama detaylarından bahsederken biryandan da nasıl uygulanabileceğini pratiksel olarak inceleyelim.

.NET’te domain event’leri uygulamak için şuana kadar aklıma gelen aşağıdaki dört farklı yöntemden istifade edebiliriz;

  • Basit C# Delegate Kullanarak Domain Event Uygulama
    C#’ta ki delegate‘leri, domain event’leri basit bir şekilde uygulamak için kullanabiliriz.

        public class Order
        {
            //Domain Event Delegate
            public delegate void OrderCompletedEventHandler();
    
            //Domain Event
            public event OrderCompletedEventHandler OrderCompleted;
    
            //Event Tetikleme Metodu
            public virtual void OnOrderCompleted()
            {
                OrderCompleted();
            }
    
            //Sipariş Tamamlandığında Bu Metot Çağrılabilir
            public void CompleteOrder()
            {
                //Sipariş Tamamlandı...
                //...
    
                //Domain Event'i Tetikle
                OnOrderCompleted();
            }
        }
    

    Yukarıdaki kod bloğunu incelersek eğer; 4. satırda domain event’in metodunu temsil edecek olan bir delegate tanımlanmakta ve 7. satırda ise bu delegate’i kullanan bir domain event oluşturulmaktadır. Bu event ne zaman tetiklenir, işte o zaman bu event’i dinleyen diğer bileşenler yahut sınıflar uyarılacak ve kendilerinde bu event’i işlemek üzere tanımlanmış olan metotlar çalıştırılacaktır.

        [Route("api/[controller]")]
        [ApiController]
        public class OrdersController : ControllerBase
        {
            [HttpPost("create-order-delegate")]
            public IActionResult CreateOrderDelegate()
            {
                Domain_Events.Delegate.Order order = new();
                order.OrderCompleted += () =>
                {
                    Console.WriteLine("Sipariş tamamlandığında buradaki kodlar tetiklenecektir!");
                };
    
                order.CompleteOrder();
    
                return Ok();
            }
        }
    

    Burada sırf olayı simüle edebilmek için bir controller üzerinden örneklendirme yapılmıştır.

  • EventHandler Kullanarak Domain Event Uygulama
    .NET Framework’ü tarafından sağlanan ve olay tabanlı bir model olan EventHandler‘ı da kullanarak domain event’leri uygulayabiliriz. Şöyle ki;

    Öncelikle, domain event’inin taşıyacağı verileri temsil edecek olan bir olay sınıfı oluşturuyoruz.

        public class OrderEventArgs : EventArgs
        {
            public OrderEventArgs(Order order)
            {
                Order = order;
            }
    
            public Order Order { get; }
        }
    

    Ardından event’i temsil edecek olan bir delegate tanımlamasına ihtiyacımız olacak. Bunu da bir önceki adımda yaptığımız gibi ‘Order’ modeli içerisinde aşağıdaki gibi EventHandler delegate türünden şekillendirebiliriz.

        public class Order
        {
            //Domain Event
            public event EventHandler<OrderEventArgs> OrderCompleted;
    
            //Event Tetikleme Metodu
            public virtual void OnOrderCompleted(OrderEventArgs e)
            {
                OrderCompleted(this, e);
            }
    
            //Sipariş Tamamlandığında Bu Metot Çağrılabilir
            public void CompleteOrder()
            {
                //Sipariş Tamamlandı...
                //...
    
                //Domain Event'i Tetikleme
                OnOrderCompleted(new OrderEventArgs(this));
            }
        }
    

    Görüldüğü üzere 4. satırda EventHandler kullanılarak ‘OrderEventArgs’a özel bir event oluşturulmaktadır. Esasında buradan da anlaşılıyor ki, EventHandler‘da özünde genel bir delegate türüdür.

    Ve bu EventHandler ile yapılan domain event çalışmasını da aşağıdaki gibi kullanabilirsiniz.

        [Route("api/[controller]")]
        [ApiController]
        public class OrdersController : ControllerBase
        {
            [HttpPost("create-order-eventhandlers")]
            public IActionResult CreateOrderEventHandlers()
            {
                Domain_Events.EventHandlers.Order order = new();
                order.OrderCompleted += (sender, e) =>
                {
                    Console.WriteLine("Sipariş tamamlandığında buradaki kodlar tetiklenecektir!");
                };
    
                order.CompleteOrder();
    
                return Ok();
            }
        }
    
  • Mediator Tasarım Desenini Kullanarak Domain Event Uygulama
    Mediator tasarım desenini uygulayarakta domain event’leri etkin bir şekilde yönetebiliriz.

    Öncelikle domain event’i temsil edecek bir interface oluşturalım.

        //Domain Event Interface
        public interface IOrderCompletedEvent
        {
            void OrderCompleted(Order order);
        }
    

    Sonrasında ise bu domain event’i yönetecek ve bunu dinleyen diğer bileşenlere yönlendirecek olan Mediator sınıfını oluşturalım.

        //Mediator Sınıfı
        public class OrderMediator : IOrderCompletedEvent
        {
            readonly List<IOrderCompletedEvent> _eventListener = new();
            public void Register(IOrderCompletedEvent @event)
            {
                _eventListener.Add(@event);
            }
    
            public void OrderCompleted(Order order)
            {
                _eventListener.ForEach(l => l.OrderCompleted(order));
                //Sipariş Tamamlandı...
                //...
            }
        }
    

    Artık uygun bileşenleri oluşturduğumuza göre Mediator ile ilişkilendirilecek servisleri oluşturabiliriz.

        public class SmsService : IOrderCompletedEvent
        {
            public void OrderCompleted(Order order)
            {
                Console.WriteLine("Sms gönderildi.");
            }
        }
    
        public class EmailService : IOrderCompletedEvent
        {
            public void OrderCompleted(Order order)
            {
                Console.WriteLine("Email gönderildi.");
            }
        }
    

    Ve artık Mediator pattern ile tasarlanan bu domain event çalışmasını da aşağıdaki gibi kullanabiliriz.

        [Route("api/[controller]")]
        [ApiController]
        public class OrdersController : ControllerBase
        {
            [HttpPost("create-order-mediator-design-pattern")]
            public IActionResult CreateOrderMediatorDesignPattern()
            {
                Domain_Events.MediatorDesignPattern.Order order = new();
                OrderMediator mediator = new();
                EmailService emailService = new();
                SmsService smsService = new();
    
                mediator.Register(emailService);
                mediator.Register(smsService);
    
                mediator.OrderCompleted(order);
    
                return Ok();
            }
        }
    
  • MediatR Kütüphanesi Eşliğinde Domain Event Uygulama
    Yukarıdaki tüm yöntemlerin dışında, domain event’leri oluşturmak ve uygulamak için esnek ve okunaklı bir yaklaşım sağlayan, yüksek seviyeli ve olay tabanlı iletişim açısından oldukça kullanışlı olan MediatR kütüphanesinden istifade edilmesi elzemdir diyebiliriz. Evet, diğer yöntemlerde çözüm getirecektirler ama bizler bu içeriğimizde(ki bu konuyla eş değer kategoride olabilecek diğer içeriklerde de) domain event’lerin tasarımını ve uygulanmasını MediatR kütüphanesi ile gerçekleştiriyor olacağız.

    MediatR kütüphanesi ile domain event oluşturabilmek için öncelikle ilgili projeye MediatR kütüphanesini yükleyelim.

    Ardından domain event’i temsil edecek bir interface oluşturalım ve bunu INotification interface’inden türetelim.

        public interface IDomainEvent : INotification
        {
    
        }
    

    Bu interface, bir veya daha fazla handler sınıfına domain event yayınlayabilmek için MediatR’ın publish-subscribe desteğini kullanabileceğimiz bir abstraction görevi görecektir.

    Bu tanımlamadan sonra IDomainEvent interface’ini implement eden concrete bir domain event oluşturalım.

        public class OrderCreatedEvent : IDomainEvent
        {
            public Guid OrderId { get; set; }
            public List<object> OrderItems { get; set; }
        }
    

    Devamında ise bu event’i işleyecek olan bir ‘handler’ sınıfı oluşturalım ve INotificationHandler<T> interface’inden implement edelim.

        public class OrderCreatedEventHandler : INotificationHandler<OrderCreatedEvent>
        {
            public Task Handle(OrderCreatedEvent notification, CancellationToken cancellationToken)
            {
                //Olayı işleyecek kodlar...
                Console.WriteLine($"Yeni sipariş verildi... {notification.OrderId}");
                return Task.CompletedTask;
            }
        }
    

    Ve son olarak MediatR’ın IoC Provider’a eklenmesini ve oluşturduğumuz domain event’in uygulamada yapılandırılmasını sağlayalım.

    .
    .
    .
    builder.Services.AddMediatR(configuration => configuration.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly()));
    .
    .
    .
    

    Artık ilgili event’i aşağıdaki gibi tetikleyebiliriz.

        [Route("api/[controller]")]
        [ApiController]
        public class OrdersController : ControllerBase
        {
            readonly IMediator _mediator;
            public OrdersController(IMediator mediator)
            {
                _mediator = mediator;
            }
    
            [HttpPost("create-order-mediatr")]
            public IActionResult CreateOrderMediatR()
            {
                OrderCreatedEvent orderCreatedEvent = new()
                {
                    OrderId = Guid.NewGuid(),
                    OrderItems = new List<object> { "A", "B", "C" }
                };
                _mediator.Publish(orderCreatedEvent);
                return Ok();
            }
        }
    

    Peki neden MediatR kütüphanesi eşliğinde domain event’leri uygulamalıyız? diye sorarsanız eğer bunu birkaç maddeyle izah etmek isterim;

    • Yüksek Seviyeli ve Kolay Kullanım
      MediatR kütüphanesi, domain event’lerin ve olay tabanlı iletişimin daha yüksek seviyeli bir arayüzle uygulanmasını sağlamakta ve böylece kodun yönetilebilirliğiyle, okunaklılığını arttırmaktadır.
    • CQRS Entegrasyonu
      MediatR kütüphanesi, CQRS desenini uygulamak için hazır bir yapı sunmaktadır.
    • Request/Response Modeli
      MediatR kütüphanesi; bir olayın yayınlanmasının yanı sıra, olayın gerçekleştiği bileşenlerden bir yanıt almak için request/response modelini de destekler. Bu sayede olayların yanıtlanması ve işlem sonuçlarının daha düzenli bir şekilde ele alınması sağlanır.
    • Farklı Tipler İçin Tek Bir Yerde Yönetim
      MediatR kütüphanesi, farklı tipteki olaylar ve istekler için aynı yapıyı kullanmamıza olanak tanır. Böylece, uygulamanın farklı kısımlarında farklı tiplerdeki olayları yönetmek için birden çok yerde kod yazmak yerine, tek bir yerde tüm olaylar yönetilebilmektedir.
    • Test Edilebilirlik ve Gevşek Bağlılık(Loose Coupling)
      MediatR kütüphanesi, uygulamanın bileşenlerini gevşek bağlı hale getirir. Böylece test edilebilirliği artırır ve kodun bakımını kolaylaştırır.
    • Daha Zengin ve Olay Odaklı Özellikler
      MediatR kütüphanesi, olayları işlemek için oldukça zengin özellikler ve yetenekler sunar. Misal olarak; istekleri filtreleme, consumer önceliklendirme, olayları arabelleğe alma vs. gibi yetenekleriyle domain event yönetimini daha esnek hale getirebilmektedir.

    Anlayacağınız MediatR kütüphanesi, domain event’ler açısından daha yüksek seviyeli, modüler ve esnek bir çözüm sunmakla birlikte, projenin sürdürülebilirliğini sağlamaktadır.

Peki hoca! Domain event’ler nerede tetiklenmelidirler? şeklinde sorunuzu duyar gibiyim… Bu haklı soruyu hemen cevaplandırmaya çalışırsak eğer, domain event’ler genellikle domain modelinde gerçekleşen kimi önemli olaylar üzerinde tetiklenirler diyebiliriz. Bu olaylar, genellikle iş mantığının bir sonucu olarak ortaya çıkar ve uygulamanın farklı parçaları arasında iletişimi sağlamak için kullanılırlar. Ayrıca terminolojik olarak domain event’lerin tetiklenmesini Raising kavramıyla ifade etmeyi tercih ederiz. Yani bir sipariş oluşturulduğunda ‘OrderCreatedEvent’ event’ini tetiklemeyiz de, raise etmiş oluruz… 🙂 Ha tabi çok isterseniz ‘tetikleme’ terimini de kullanabilirsiniz…

Şimdi domain event’lerin raising edilmesi için bazı yaygın noktaları ele alalım;

  • Aggregate Root İçinde
    Eğer CQRS yahut Event Sourcing gibi tasarım desenlerini kullanıyorsanız, aggregate root’un içinde domain event’leri raising edebilirsiniz. Aggregate root, bir veya daha fazla ilişkili nesneyi gruplayan ve bu nesneler üzerinden işlemleri yöneten bir nesnedir. Misal olarak; ‘Product’ aggregate root’u içerisinde, bir ürün oluşturulduğu taktirde ‘ProductCreatedEvent’ini tetikleyebilirsiniz.
  • Servislerde
    İş mantığınızın bir sonucu olarak domain event’lerin raising edilebileceği servisler oluşturabilirsiniz. Bu servisler ile birden fazla domain nesnesi arasında koordinasyon sağlayabilir ve olayları tetikleyebilirsiniz.
  • Repository Metotları İçinde
    Veritabanı işlemleri sırasında domain event’ler raising edilebilir. Misal olarak; bir product eklendiğinde yahut güncellendiğinde durumu ifade edecek bir domain event ilgili repository metodu içerisinde tetiklenebilir.
  • Business Layer’da
    Yapılan operasyonlar neticesinde domain event’lerinizi raising etmeniz gerekebilir. Haliyle bunun için business layer’da uygun yerleri kullanabilirsiniz.
  • Entityler’de
    Domain event’ler, entity’ler içinde de raise edilebilirler. Ancak bu durumun uygulamanın tasarımına ve gereksinimlerine bağlı olarak dikkatli bir şekilde ele alınması gerekmektedir. Bunu direkt pratiksel olarak örneklendirmekte fayda görmekteyim. İlk olarak aşağıdaki gibi base bir ‘Entity’ class’ı oluşturarak başlayalım.

        public abstract class Entity
        {
            private readonly List<IDomainEvent> _domainEvents;
            public Entity()
                => _domainEvents = new();
    
            public IReadOnlyList<IDomainEvent> GetDomainEvents()
                => _domainEvents.ToList();
    
            public void ClearDomainEvents()
                => _domainEvents.Clear();
    
            protected void RaiseDomainEvent(IDomainEvent domainEvent)
                => _domainEvents.Add(domainEvent);
        }
    

    Bu sınıfta, görüldüğü üzere entity içerisinde raise edilecek olan domain event’ler RaiseDomainEvent fonksiyonu aracılığıyla tutulmaktadır.

    Şimdi de, bu sınıftan türetilen esas entity sınıflarımızı aşağıdaki gibi tanımlayarak, domain event’leri oluşturabiliriz.

        public class Order : Entity
        {
            public Guid Id { get; set; }
            public List<object> OrderItems { get; set; }
            public void Complete()
            {
                RaiseDomainEvent(new OrderCreatedEvent()
                {
                    OrderId = Id,
                    OrderItems = OrderItems
                });
            }
        }
    

    Görüldüğü üzere ‘Order’ nesnesi başarıyla üretildikten sonra ‘Complete’ fonksiyonu tetiklendiği zaman bir ‘OrderCreatedEvent’ olayı oluşturulacak ve RaiseDomainEvent fonksiyonu aracılığıyla bu olay raise edilmek üzere base’de ki(entity) koleksiyona eklenecektir.

    İşte bu şekilde bir tasarım eşliğinde, entity’lerimiz de, raise etmek için bir veya birden fazla domain event tanımlayabiliriz.

    Şimdi yaptığımız bunca çalışmayı kullanabileceğimiz uygun bir senaryo düşünürsek eğer, entity’de yapılan bu yapıyı kullanabileceğimiz en uygun yerlerden biri EF Core’un SaveChanges metodu olabilir gibi. Şöyle ki;

        public class ApplicationDbContext : DbContext
        {
            public DbSet<Order> Orders { get; set; }
            readonly IMediator _mediator;
            public ApplicationDbContext(DbContextOptions options, IMediator mediator) : base(options)
            {
                _mediator = mediator;
            }
    
            public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
            {
                var result = await base.SaveChangesAsync(cancellationToken);
    
                async Task PublishDomainEventsAsync()
                {
                    var domainEvents = ChangeTracker
                        .Entries<Entity>()
                        .Select(entry => entry.Entity)
                            .SelectMany(entity =>
                            {
                                var domainEvents = entity.GetDomainEvents();
                                entity.ClearDomainEvents();
                                return domainEvents;
                            })
                        .ToList();
    
                    foreach (var domainEvent in domainEvents)
                    {
                        await _mediator.Publish(domainEvent);
                    }
                }
    
                await PublishDomainEventsAsync();
    
                return result;
            }
        }
    

    Yukarıda tanımlanan ApplicationDbContext sınıf içeriğine göz atarsanız, SaveChangesAsync metodu override edilmekte ve içerisinde 12. satırda olduğu gibi değişiklikler veritabanına yansıtıldıktan sonra, local function olarak tanımlanmış olan PublishDomainEventsAsync metodu çağrılmaktadır. Bu metot içeriğinde, ChangeTracker üzerinden işlem yapılan nesnenin ‘GetDomainEvents’ fonksiyonuyla domain event’leri elde edilmekte ve ardından MediatR kütüphanesiyle bu event’ler publish edilmektedir.

Nihai olarak;
Domain event’ler eşliğinde, uygulama bünyesindeki bileşenler arasında loosely coupled(gevşek bağlı) davranışlar sergileyebilir ve domain logic’i asenkron süreçlerdeki olası yan etkilerden arındırarak çalışmalarımızı rahatlıkla gerçekleştirebiliriz. Bu içeriğimizde bu durumları teorik olarak masaya yatırmaya çalışıp, pratiksel olarakta mümkün mertebe tecrübe etmeye çalışmış bulunuyoruz.

İlgilenenlerin faydalanması dileğiyle…
Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar…

Not : Örnek projeyi aşağıdaki github adresinden edinebilirsiniz.
https://github.com/gncyyldz/Loosely_Coupled_Systems_Domain_Events_Examples

Bunlar da hoşunuza gidebilir...

1 Cevap

  1. Furkan dedi ki:

    Yazı için teşekkür ederim hocam. Bir sorum olacak. Diyelim ki 12. satırda SaveChanges() ile değişiklikler veritabanına yansıtıldı ve 12. satırdan sonra domain eventler raise edildi. Diyelim ki domain evenler handler edilir iken bir bir hata oluştu ve 12. satırda veritabanına kayıt edilen işlemlerin geri alınması gerekiyor. Bunu çözmek için kullanılan güzel bir yaklaşım var mı?

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir